diff options
| author | Pedro F. Giffuni <pfg@FreeBSD.org> | 2015-01-28 22:55:06 +0000 |
|---|---|---|
| committer | Pedro F. Giffuni <pfg@FreeBSD.org> | 2015-01-28 22:55:06 +0000 |
| commit | e925d4a747df4a349c2baac436a71f606945baa5 (patch) | |
| tree | e3a2ccc21623f0453c6f43f9016535205ba10d21 /amd | |
| parent | 509fcedcfec077a6070002f4059db0ae675be869 (diff) | |
Notes
Diffstat (limited to 'amd')
64 files changed, 25993 insertions, 0 deletions
diff --git a/amd/am_ops.c b/amd/am_ops.c new file mode 100644 index 000000000000..5a5c33650eca --- /dev/null +++ b/amd/am_ops.c @@ -0,0 +1,475 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * 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. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/am_ops.c + * + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + + +/* + * The order of these entries matters, since lookups in this table are done + * on a first-match basis. The entries below are a mixture of native + * filesystems supported by the OS (HAVE_FS_FOO), and some meta-filesystems + * supported by amd (HAVE_AMU_FS_FOO). The order is set here in expected + * match-hit such that more popular filesystems are listed first (nfs is the + * most popular, followed by a symlink F/S) + */ +static am_ops *vops[] = +{ +#ifdef HAVE_FS_NFS + &nfs_ops, /* network F/S (version 2) */ +#endif /* HAVE_FS_NFS */ +#ifdef HAVE_AMU_FS_LINK + &amfs_link_ops, /* symlink F/S */ +#endif /* HAVE_AMU_FS_LINK */ + + /* + * Other amd-supported meta-filesystems. + */ +#ifdef HAVE_AMU_FS_NFSX + &amfs_nfsx_ops, /* multiple-nfs F/S */ +#endif /* HAVE_AMU_FS_NFSX */ +#ifdef HAVE_AMU_FS_NFSL + &amfs_nfsl_ops, /* NFS with local link existence check */ +#endif /* HAVE_AMU_FS_NFSL */ +#ifdef HAVE_AMU_FS_HOST + &amfs_host_ops, /* multiple exported nfs F/S */ +#endif /* HAVE_AMU_FS_HOST */ +#ifdef HAVE_AMU_FS_LINKX + &amfs_linkx_ops, /* symlink F/S with link target verify */ +#endif /* HAVE_AMU_FS_LINKX */ +#ifdef HAVE_AMU_FS_PROGRAM + &amfs_program_ops, /* program F/S */ +#endif /* HAVE_AMU_FS_PROGRAM */ +#ifdef HAVE_AMU_FS_UNION + &amfs_union_ops, /* union F/S */ +#endif /* HAVE_AMU_FS_UNION */ + + /* + * A few more native filesystems. + */ +#ifdef HAVE_FS_UFS + &ufs_ops, /* Unix F/S */ +#endif /* HAVE_FS_UFS */ +#ifdef HAVE_FS_XFS + &xfs_ops, /* Unix (irix) F/S */ +#endif /* HAVE_FS_XFS */ +#ifdef HAVE_FS_EFS + &efs_ops, /* Unix (irix) F/S */ +#endif /* HAVE_FS_EFS */ +#ifdef HAVE_FS_LOFS + &lofs_ops, /* loopback F/S */ +#endif /* HAVE_FS_LOFS */ +#ifdef HAVE_FS_CDFS + &cdfs_ops, /* CDROM/HSFS/ISO9960 F/S */ +#endif /* HAVE_FS_CDFS */ +#ifdef HAVE_FS_PCFS + &pcfs_ops, /* Floppy/MSDOS F/S */ +#endif /* HAVE_FS_PCFS */ +#ifdef HAVE_FS_CACHEFS + &cachefs_ops, /* caching F/S */ +#endif /* HAVE_FS_CACHEFS */ +#ifdef HAVE_FS_NULLFS +/* FILL IN */ /* null (loopback) F/S */ +#endif /* HAVE_FS_NULLFS */ +#ifdef HAVE_FS_UNIONFS +/* FILL IN */ /* union (bsd44) F/S */ +#endif /* HAVE_FS_UNIONFS */ +#ifdef HAVE_FS_UMAPFS +/* FILL IN */ /* uid/gid mapping F/S */ +#endif /* HAVE_FS_UMAPFS */ + + /* + * These 4 should be last, in the order: + * (1) amfs_auto + * (2) amfs_direct + * (3) amfs_toplvl + * (4) amfs_error + */ +#ifdef HAVE_AMU_FS_AUTO + &amfs_auto_ops, /* Automounter F/S */ +#endif /* HAVE_AMU_FS_AUTO */ +#ifdef HAVE_AMU_FS_DIRECT + &amfs_direct_ops, /* direct-mount F/S */ +#endif /* HAVE_AMU_FS_DIRECT */ +#ifdef HAVE_AMU_FS_TOPLVL + &amfs_toplvl_ops, /* top-level mount F/S */ +#endif /* HAVE_AMU_FS_TOPLVL */ +#ifdef HAVE_AMU_FS_ERROR + &amfs_error_ops, /* error F/S */ +#endif /* HAVE_AMU_FS_ERROR */ + 0 +}; + + +void +ops_showamfstypes(char *buf, size_t l) +{ + struct am_ops **ap; + int linesize = 0; + + buf[0] = '\0'; + for (ap = vops; *ap; ap++) { + xstrlcat(buf, (*ap)->fs_type, l); + if (ap[1]) + xstrlcat(buf, ", ", l); + linesize += strlen((*ap)->fs_type) + 2; + if (linesize > 62) { + linesize = 0; + xstrlcat(buf, "\n ", l); + } + } +} + + +static void +ops_show1(char *buf, size_t l, int *linesizep, const char *name) +{ + xstrlcat(buf, name, l); + xstrlcat(buf, ", ", l); + *linesizep += strlen(name) + 2; + if (*linesizep > 60) { + xstrlcat(buf, "\t\n", l); + *linesizep = 0; + } +} + + +void +ops_showfstypes(char *buf, size_t l) +{ + int linesize = 0; + + buf[0] = '\0'; + +#ifdef MNTTAB_TYPE_AUTOFS + ops_show1(buf, l, &linesize, MNTTAB_TYPE_AUTOFS); +#endif /* MNTTAB_TYPE_AUTOFS */ + +#ifdef MNTTAB_TYPE_CACHEFS + ops_show1(buf, l, &linesize, MNTTAB_TYPE_CACHEFS); +#endif /* MNTTAB_TYPE_CACHEFS */ + +#ifdef MNTTAB_TYPE_CDFS + ops_show1(buf, l, &linesize, MNTTAB_TYPE_CDFS); +#endif /* MNTTAB_TYPE_CDFS */ + +#ifdef MNTTAB_TYPE_CFS + ops_show1(buf, l, &linesize, MNTTAB_TYPE_CFS); +#endif /* MNTTAB_TYPE_CFS */ + +#ifdef MNTTAB_TYPE_LOFS + ops_show1(buf, l, &linesize, MNTTAB_TYPE_LOFS); +#endif /* MNTTAB_TYPE_LOFS */ + +#ifdef MNTTAB_TYPE_EFS + ops_show1(buf, l, &linesize, MNTTAB_TYPE_EFS); +#endif /* MNTTAB_TYPE_EFS */ + +#ifdef MNTTAB_TYPE_MFS + ops_show1(buf, l, &linesize, MNTTAB_TYPE_MFS); +#endif /* MNTTAB_TYPE_MFS */ + +#ifdef MNTTAB_TYPE_NFS + ops_show1(buf, l, &linesize, MNTTAB_TYPE_NFS); +#endif /* MNTTAB_TYPE_NFS */ + +#ifdef MNTTAB_TYPE_NFS3 + ops_show1(buf, l, &linesize, "nfs3"); /* always hard-code as nfs3 */ +#endif /* MNTTAB_TYPE_NFS3 */ + +#ifdef MNTTAB_TYPE_NULLFS + ops_show1(buf, l, &linesize, MNTTAB_TYPE_NULLFS); +#endif /* MNTTAB_TYPE_NULLFS */ + +#ifdef MNTTAB_TYPE_PCFS + ops_show1(buf, l, &linesize, MNTTAB_TYPE_PCFS); +#endif /* MNTTAB_TYPE_PCFS */ + +#ifdef MNTTAB_TYPE_TFS + ops_show1(buf, l, &linesize, MNTTAB_TYPE_TFS); +#endif /* MNTTAB_TYPE_TFS */ + +#ifdef MNTTAB_TYPE_TMPFS + ops_show1(buf, l, &linesize, MNTTAB_TYPE_TMPFS); +#endif /* MNTTAB_TYPE_TMPFS */ + +#ifdef MNTTAB_TYPE_UFS + ops_show1(buf, l, &linesize, MNTTAB_TYPE_UFS); +#endif /* MNTTAB_TYPE_UFS */ + +#ifdef MNTTAB_TYPE_UMAPFS + ops_show1(buf, l, &linesize, MNTTAB_TYPE_UMAPFS); +#endif /* MNTTAB_TYPE_UMAPFS */ + +#ifdef MNTTAB_TYPE_UNIONFS + ops_show1(buf, l, &linesize, MNTTAB_TYPE_UNIONFS); +#endif /* MNTTAB_TYPE_UNIONFS */ + +#ifdef MNTTAB_TYPE_XFS + ops_show1(buf, l, &linesize, MNTTAB_TYPE_XFS); +#endif /* MNTTAB_TYPE_XFS */ + + /* terminate with a period, newline, and NULL */ + if (buf[strlen(buf)-1] == '\n') + buf[strlen(buf) - 4] = '\0'; + else + buf[strlen(buf) - 2] = '\0'; + xstrlcat(buf, ".\n", l); +} + + +/* + * return string option which is the reverse of opt. + * nosuid -> suid + * quota -> noquota + * ro -> rw + * etc. + * may return pointer to static buffer or subpointer within opt. + */ +static char * +reverse_option(const char *opt) +{ + static char buf[80]; + + /* sanity check */ + if (!opt) + return NULL; + + /* check special cases */ + /* XXX: if this gets too long, rewrite the code more flexibly */ + if (STREQ(opt, "ro")) return "rw"; + if (STREQ(opt, "rw")) return "ro"; + if (STREQ(opt, "bg")) return "fg"; + if (STREQ(opt, "fg")) return "bg"; + if (STREQ(opt, "soft")) return "hard"; + if (STREQ(opt, "hard")) return "soft"; + + /* check if string starts with 'no' and chop it */ + if (NSTREQ(opt, "no", 2)) { + xstrlcpy(buf, &opt[2], sizeof(buf)); + } else { + /* finally return a string prepended with 'no' */ + xstrlcpy(buf, "no", sizeof(buf)); + xstrlcat(buf, opt, sizeof(buf)); + } + return buf; +} + + +/* + * start with an empty string. for each opts1 option that is not + * in opts2, add it to the string (make sure the reverse of it + * isn't in either). finally add opts2. return new string. + * Both opts1 and opts2 must not be null! + * Caller must eventually free the string being returned. + */ +static char * +merge_opts(const char *opts1, const char *opts2) +{ + mntent_t mnt2; /* place holder for opts2 */ + char *newstr; /* new string to return (malloc'ed) */ + char *tmpstr; /* temp */ + char *eq; /* pointer to whatever follows '=' within temp */ + char oneopt[80]; /* one option w/o value if any */ + char *revoneopt; /* reverse of oneopt */ + size_t len = strlen(opts1) + strlen(opts2) + 2; /* space for "," and NULL */ + char *s1 = strdup(opts1); /* copy of opts1 to munge */ + + /* initialization */ + mnt2.mnt_opts = (char *) opts2; + newstr = xmalloc(len); + newstr[0] = '\0'; + + for (tmpstr = strtok(s1, ","); + tmpstr; + tmpstr = strtok(NULL, ",")) { + /* copy option to temp buffer */ + xstrlcpy(oneopt, tmpstr, 80); + /* if option has a value such as rsize=1024, chop the value part */ + if ((eq = haseq(oneopt))) + *eq = '\0'; + /* find reverse option of oneopt */ + revoneopt = reverse_option(oneopt); + /* if option orits reverse exist in opts2, ignore it */ + if (amu_hasmntopt(&mnt2, oneopt) || amu_hasmntopt(&mnt2, revoneopt)) + continue; + /* add option to returned string */ + if (newstr[0]) { + xstrlcat(newstr, ",", len); + xstrlcat(newstr, tmpstr, len); + } else { + xstrlcpy(newstr, tmpstr, len); + } + } + + /* finally, append opts2 itself */ + if (newstr[0]) { + xstrlcat(newstr, ",", len); + xstrlcat(newstr, opts2, len); + } else { + xstrlcpy(newstr, opts2, len); + } + + XFREE(s1); + return newstr; +} + + +am_ops * +ops_search(char *type) +{ + am_ops **vp; + am_ops *rop = 0; + for (vp = vops; (rop = *vp); vp++) + if (STREQ(rop->fs_type, type)) + break; + return rop; +} + + +am_ops * +ops_match(am_opts *fo, char *key, char *g_key, char *path, char *keym, char *map) +{ + am_ops *rop = 0; + char *link_dir; + + /* + * First crack the global opts and the local opts + */ + if (!eval_fs_opts(fo, key, g_key, path, keym, map)) { + rop = &amfs_error_ops; + } else if (fo->opt_type == 0) { + plog(XLOG_USER, "No fs type specified (key = \"%s\", map = \"%s\")", keym, map); + rop = &amfs_error_ops; + } else { + /* + * Next find the correct filesystem type + */ + rop = ops_search(fo->opt_type); + if (!rop) { + plog(XLOG_USER, "fs type \"%s\" not recognized", fo->opt_type); + rop = &amfs_error_ops; + } + } + + /* + * Make sure we have a default mount option. + * Otherwise skip past any leading '-'. + */ + if (fo->opt_opts == 0) + fo->opt_opts = strdup("rw,defaults"); + else if (*fo->opt_opts == '-') { + /* + * We cannot simply do fo->opt_opts++ here since the opts + * module will try to free the pointer fo->opt_opts later. + * So just reallocate the thing -- stolcke 11/11/94 + */ + char *old = fo->opt_opts; + fo->opt_opts = strdup(old + 1); + XFREE(old); + } + + /* + * If addopts option was used, then append it to the + * current options and remote mount options. + */ + if (fo->opt_addopts) { + if (STREQ(fo->opt_opts, fo->opt_remopts)) { + /* optimize things for the common case where opts==remopts */ + char *mergedstr; + mergedstr = merge_opts(fo->opt_opts, fo->opt_addopts); + plog(XLOG_INFO, "merge rem/opts \"%s\" add \"%s\" => \"%s\"", + fo->opt_opts, fo->opt_addopts, mergedstr); + XFREE(fo->opt_opts); + XFREE(fo->opt_remopts); + fo->opt_opts = mergedstr; + fo->opt_remopts = strdup(mergedstr); + } else { + char *mergedstr, *remmergedstr; + mergedstr = merge_opts(fo->opt_opts, fo->opt_addopts); + plog(XLOG_INFO, "merge opts \"%s\" add \"%s\" => \"%s\"", + fo->opt_opts, fo->opt_addopts, mergedstr); + XFREE(fo->opt_opts); + fo->opt_opts = mergedstr; + remmergedstr = merge_opts(fo->opt_remopts, fo->opt_addopts); + plog(XLOG_INFO, "merge remopts \"%s\" add \"%s\" => \"%s\"", + fo->opt_remopts, fo->opt_addopts, remmergedstr); + XFREE(fo->opt_remopts); + fo->opt_remopts = remmergedstr; + } + } + + /* + * Initialize opt_mount_type to "nfs", if it's not initialized already + */ + if (!fo->opt_mount_type) + fo->opt_mount_type = "nfs"; + + /* Normalize the sublink and make it absolute */ + link_dir = fo->opt_sublink; + if (link_dir && link_dir[0] && link_dir[0] != '/') { + link_dir = str3cat((char *) 0, fo->opt_fs, "/", link_dir); + normalize_slash(link_dir); + XFREE(fo->opt_sublink); + fo->opt_sublink = link_dir; + } + + /* + * Check the filesystem is happy + */ + if (fo->fs_mtab) + XFREE(fo->fs_mtab); + + fo->fs_mtab = rop->fs_match(fo); + if (fo->fs_mtab) + return rop; + + /* + * Return error file system + */ + fo->fs_mtab = amfs_error_ops.fs_match(fo); + return &amfs_error_ops; +} diff --git a/amd/amd.8 b/amd/amd.8 new file mode 100644 index 000000000000..565a5505b8c2 --- /dev/null +++ b/amd/amd.8 @@ -0,0 +1,371 @@ +.\" +.\" Copyright (c) 1997-2006 Erez Zadok +.\" 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. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Jan-Simon Pendry at Imperial College, London. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgment: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" %W% (Berkeley) %G% +.\" +.\" $Id: amd.8,v 1.14.2.1 2006/01/02 18:48:23 ezk Exp $ +.\" +.TH AMD 8 "3 November 1989" +.SH NAME +amd \- automatically mount file systems +.SH SYNOPSIS +.B amd +.B \-H +.br +.B amd +[ +.BI \-F " conf_file" +] +.br +.B amd +[ +.B \-nprvHS +] [ +.BI \-a " mount_point" +] [ +.BI \-c " duration" +] [ +.BI \-d " domain" +] [ +.BI \-k " kernel-arch" +] [ +.BI \-l " logfile" +] [ +.BI \-o " op_sys_ver" +] [ +.BI \-t " interval.interval" +] [ +.BI \-w " interval" +] [ +.BI \-x " log-option" +] [ +.BI \-y " YP-domain" +] [ +.BI \-A " arch" +] [ +.BI \-C " cluster-name" +] [ +.BI \-D " option" +] [ +.BI \-F " conf_file" +] [ +.BI \-O " op_sys_name" +] [ +.BI \-T " tag" +] +[ +.I directory +.I mapname +.RI [ " \-map-options " ] +] .\|.\|. +.SH DESCRIPTION +.B Amd +is a daemon that automatically mounts filesystems +whenever a file or directory +within that filesystem is accessed. +Filesystems are automatically unmounted when they +appear to have become quiescent. +.LP +.B Amd +operates by attaching itself as an +.SM NFS +server to each of the specified +.IB directories . +Lookups within the specified directories +are handled by +.BR amd , +which uses the map defined by +.I mapname +to determine how to resolve the lookup. +Generally, this will be a host name, some filesystem information +and some mount options for the given filesystem. +.LP +In the first form depicted above, +.B amd +will print a short help string. In the second form, if no options are +specified, or the +.B -F +is used, +.B amd +will read configuration parameters from the file +.I conf_file +which defaults to +.BR /etc/amd.conf . +The last form is described below. +.SH OPTIONS + +.\"*******************************************************" + +.TP +.BI \-a " temporary-directory" +Specify an alternative location for the real mount points. +The default is +.BR /a . + +.TP +.BI \-c " duration" +Specify a +.IR duration , +in seconds, that a looked up name remains +cached when not in use. The default is 5 minutes. + +.TP +.BI \-d " domain" +Specify the local domain name. If this option is not +given the domain name is determined from the hostname. + +.TP +.BI \-k " kernel-arch" +Specifies the kernel architecture. This is used solely +to set the ${karch} selector. + +.TP +.BI \-l " logfile" +Specify a logfile in which to record mount and unmount events. +If +.I logfile +is the string +.B syslog +then the log messages will be sent to the system log daemon by +.IR syslog (3). +The default syslog facility used is LOG_DAEMON. If you +wish to change it, append its name to the log file name, delimited by a +single colon. For example, if +.I logfile +is the string +.B syslog:local7 +then +.B Amd +will log messages via +.IR syslog (3) +using the LOG_LOCAL7 facility (if it exists on the system). + +.TP +.B \-n +Normalize hostnames. +The name refereed to by ${rhost} is normalized relative to the +host database before being used. The effect is to translate +aliases into ``official'' names. + +.TP +.BI \-o " op_sys_ver" +Override the compiled-in version number of the operating system. Useful +when the built in version is not desired for backward compatibility reasons. +For example, if the build in version is ``2.5.1'', you can override it to +``5.5.1'', and use older maps that were written with the latter in mind. + +.TP +.B \-p +Print PID. +Outputs the process-id of +.B amd +to standard output where it can be saved into a file. + +.TP +.B \-r +Restart existing mounts. +.B Amd +will scan the mount file table to determine which filesystems +are currently mounted. Whenever one of these would have +been auto-mounted, +.B amd +.I inherits +it. + +.TP +.BI \-t " timeout.retransmit" +Specify the NFS timeout +.IR interval , +in tenths of a second, between NFS/RPC retries (for UDP only). The default +is 0.8 seconds. The second value alters the retransmit counter, which +defaults to 11 retransmissions. Both of these values are used by the kernel +to communicate with amd. Useful defaults are supplied if either or both +values are missing. + +Amd relies on the kernel RPC retransmit mechanism to trigger mount retries. +The values of these parameters change the overall retry interval. Too long +an interval gives poor interactive response; too short an interval causes +excessive retries. + +.TP +.B \-v +Version. Displays version and configuration information on standard error. + +.TP +.BI \-w " interval" +Specify an +.IR interval , +in seconds, between attempts to dismount +filesystems that have exceeded their cached times. +The default is 2 minutes. + +.TP +.BI \-x " options" +Specify run-time logging options. The options are a comma separated +list chosen from: fatal, error, user, warn, info, map, stats, all. + +.TP +.BI \-y " domain" +Specify an alternative NIS domain from which to fetch the NIS maps. +The default is the system domain name. This option is ignored if NIS +support is not available. + +.TP +.BI \-A " arch" +Specifies the OS architecture. This is used solely to set the ${arch} +selector. + +.TP +.BI \-C " cluster-name" +Specify an alternative HP-UX cluster name to use. + +.TP +.BI \-D " option" +Select from a variety of debug options. Prefixing an +option with the strings +.B no +reverses the effect of that option. Options are cumulative. +The most useful option is +.BR all . +Since +.I \-D +is only used for debugging other options are not documented here: +the current supported set of options is listed by the \-v option +and a fuller description is available in the program source. + +.TP +.BI \-F " conf_file" +Specify an amd configuration file to use. See +.BR amd.conf (5) +for description of this file's format. This configuration file is used to +specify any options in lieu of typing many of them on the command line. The +.I amd.conf +file includes directives for every command line option amd has, and many +more that are only available via the configuration file facility. The +configuration file specified by this option is processed after all other +options had been processed, regardless of the actual location of this option +on the command line. + +.TP +.B \-H +Print help and usage string. + +.TP +.BI \-O " op_sys_name" +Override the compiled-in name of the operating system. Useful when the +built in name is not desired for backward compatibility reasons. For +example, if the build in name is ``sunos5'', you can override it to +``sos5'', and use older maps which were written with the latter in mind. + +.TP +.B \-S +Do not lock the running executable pages of amd into memory. To improve +amd's performance, systems that support the +.BR plock (3) +call, could lock the amd process into memory. This way there is less chance +the operating system will schedule, page out, and swap the amd process as +needed. This tends improves amd's performance, at the cost of reserving the +memory used by the amd process (making it unavailable for other processes). +If this behavior is not desired, use the +.B \-S +option. + +.TP +.BI \-T " tag" +Specify a tag to use with +.BR amd.conf (5). +All map entries tagged with +.I tag +will be processed. Map entries that are not tagged are always processed. +Map entries that are tagged with a tag other than +.I tag +will not be processed. + +.SH FILES +.TP 5 +.B /a +directory under which filesystems are dynamically mounted +.TP 5 +.B /etc/amd.conf +default configuration file +.SH CAVEATS +Some care may be required when creating a mount map. +.LP +Symbolic links on an NFS filesystem can be incredibly inefficient. +In most implementations of NFS, their interpolations are not cached +by the kernel and each time a symlink is encountered during a +.I lookuppn +translation it costs an RPC call to the NFS server. +It would appear that a large improvement in real-time +performance could be gained by adding a cache somewhere. +Replacing symlinks with a suitable incarnation of the auto-mounter +results in a large real-time speedup, but also causes a large +number of process context switches. +.LP +A weird imagination is most useful to gain full advantage of all +the features. +.SH "SEE ALSO" +.BR amd.conf (5), +.BR amq (8), +.BR domainname (1), +.BR hostname (1), +.BR automount (8), +.BR mount (8), +.BR umount (8), +.BR mtab (5), +.BR syslog (3). +.LP +``am-utils'' +.BR info (1) +entry. +.LP +.I "Linux NFS and Automounter Administration" +by Erez Zadok, ISBN 0-7821-2739-8, (Sybex, 2001). +.LP +.I http://www.am-utils.org +.LP +.I "Amd \- The 4.4 BSD Automounter" +.SH AUTHORS +Jan-Simon Pendry <jsp@doc.ic.ac.uk>, Department of Computing, Imperial College, London, UK. +.P +Erez Zadok <ezk@cs.sunysb.edu>, Computer Science Department, Stony Brook +University, Stony Brook, New York, USA. +.P +Other authors and contributors to am-utils are listed in the +.B AUTHORS +file distributed with am-utils. diff --git a/amd/amd.c b/amd/amd.c new file mode 100644 index 000000000000..37eab5d8442c --- /dev/null +++ b/amd/amd.c @@ -0,0 +1,577 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * 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. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/amd.c + * + */ + +/* + * Automounter + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +struct amu_global_options gopt; /* where global options are stored */ + +char pid_fsname[SIZEOF_PID_FSNAME]; /* "kiska.southseas.nz:(pid%d)" */ +char *hostdomain = "unknown.domain"; +#define SIZEOF_HOSTD (2 * MAXHOSTNAMELEN + 1) /* Host+domain */ +char hostd[SIZEOF_HOSTD]; /* Host+domain */ +char *endian = ARCH_ENDIAN; /* Big or Little endian */ +char *cpu = HOST_CPU; /* CPU type */ +char *PrimNetName; /* name of primary network */ +char *PrimNetNum; /* number of primary network */ + +int immediate_abort; /* Should close-down unmounts be retried */ +int orig_umask = 022; +int select_intr_valid; + +jmp_buf select_intr; +struct amd_stats amd_stats; /* Server statistics */ +struct in_addr myipaddr; /* (An) IP address of this host */ +time_t do_mapc_reload = 0; /* mapc_reload() call required? */ + +#ifdef HAVE_FS_AUTOFS +int amd_use_autofs = 0; +#endif /* HAVE_FS_AUTOFS */ + +#ifdef HAVE_SIGACTION +sigset_t masked_sigs; +#endif /* HAVE_SIGACTION */ + + +/* + * Signal handler: + * SIGINT - tells amd to do a full shutdown, including unmounting all + * filesystem. + * SIGTERM - tells amd to shutdown now. Just unmounts the automount nodes. + */ +static RETSIGTYPE +sigterm(int sig) +{ +#ifdef REINSTALL_SIGNAL_HANDLER + signal(sig, sigterm); +#endif /* REINSTALL_SIGNAL_HANDLER */ + + switch (sig) { + case SIGINT: + immediate_abort = 15; + break; + + case SIGTERM: + immediate_abort = -1; + /* fall through... */ + + default: + plog(XLOG_WARNING, "WARNING: automounter going down on signal %d", sig); + break; + } + if (select_intr_valid) + longjmp(select_intr, sig); +} + + +/* + * Hook for cache reload. + * When a SIGHUP arrives it schedules a call to mapc_reload + */ +static RETSIGTYPE +sighup(int sig) +{ +#ifdef REINSTALL_SIGNAL_HANDLER + signal(sig, sighup); +#endif /* REINSTALL_SIGNAL_HANDLER */ + + if (sig != SIGHUP) + dlog("spurious call to sighup"); + /* + * Force a reload by zero'ing the timer + */ + if (amd_state == Run) + do_mapc_reload = 0; +} + + +static RETSIGTYPE +parent_exit(int sig) +{ + /* + * This signal handler is called during Amd initialization. The parent + * forks a child to do all the hard automounting work, and waits for a + * SIGQUIT signal from the child. When the parent gets the signal it's + * supposed to call this handler and exit(3), thus completing the + * daemonizing process. Alas, on some systems, especially Linux 2.4/2.6 + * with Glibc, exit(3) doesn't always terminate the parent process. + * Worse, the parent process now refuses to accept any more SIGQUIT + * signals -- they are blocked. What's really annoying is that this + * doesn't happen all the time, suggesting a race condition somewhere. + * (This happens even if I change the logic to use another signal.) I + * traced this to something which exit(3) does in addition to exiting the + * process, probably some atexit() stuff or other side-effects related to + * signal handling. Either way, since at this stage the parent process + * just needs to terminate, I'm simply calling _exit(2). Note also that + * the OpenGroup doesn't list exit(3) as a recommended "Base Interface" + * but they do list _exit(2) as one. This fix seems to work reliably all + * the time. -Erez (2/27/2005) + */ + _exit(0); +} + + +static int +daemon_mode(void) +{ + int bgpid; + +#ifdef HAVE_SIGACTION + struct sigaction sa, osa; + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = parent_exit; + sa.sa_flags = 0; + sigemptyset(&(sa.sa_mask)); + sigaddset(&(sa.sa_mask), SIGQUIT); + sigaction(SIGQUIT, &sa, &osa); +#else /* not HAVE_SIGACTION */ + signal(SIGQUIT, parent_exit); +#endif /* not HAVE_SIGACTION */ + + bgpid = background(); + + if (bgpid != 0) { + /* + * Now wait for the automount points to + * complete. + */ + for (;;) + pause(); + /* should never reach here */ + } +#ifdef HAVE_SIGACTION + sigaction(SIGQUIT, &osa, NULL); +#else /* not HAVE_SIGACTION */ + signal(SIGQUIT, SIG_DFL); +#endif /* not HAVE_SIGACTION */ + + /* + * Record our pid to make it easier to kill the correct amd. + */ + if (gopt.flags & CFM_PRINT_PID) { + if (STREQ(gopt.pid_file, "/dev/stdout")) { + printf("%ld\n", (long) am_mypid); + /* flush stdout, just in case */ + fflush(stdout); + } else { + FILE *f; + mode_t prev_umask = umask(0022); /* set secure temporary umask */ + + f = fopen(gopt.pid_file, "w"); + if (f) { + fprintf(f, "%ld\n", (long) am_mypid); + (void) fclose(f); + } else { + fprintf(stderr, "cannot open %s (errno=%d)\n", gopt.pid_file, errno); + } + umask(prev_umask); /* restore umask */ + } + } + + /* + * Pretend we are in the foreground again + */ + foreground = 1; + + /* + * Dissociate from the controlling terminal + */ + amu_release_controlling_tty(); + + return getppid(); +} + + +/* + * Initialize global options structure. + */ +static void +init_global_options(void) +{ +#if defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME) + static struct utsname un; +#endif /* defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME) */ + int i; + + memset(&gopt, 0, sizeof(struct amu_global_options)); + + /* name of current architecture */ + gopt.arch = HOST_ARCH; + + /* automounter temp dir */ + gopt.auto_dir = "/a"; + + /* toplevel attribute cache timeout */ + gopt.auto_attrcache = 0; + + /* cluster name */ + gopt.cluster = NULL; + + /* executable map timeout */ + gopt.exec_map_timeout = AMFS_EXEC_MAP_TIMEOUT; + + /* + * kernel architecture: this you must get from uname() if possible. + */ +#if defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME) + if (uname(&un) >= 0) + gopt.karch = un.machine; + else +#endif /* defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME) */ + gopt.karch = HOST_ARCH; + + /* amd log file */ + gopt.logfile = NULL; + + /* operating system name */ + gopt.op_sys = HOST_OS_NAME; + + /* OS version */ + gopt.op_sys_ver = HOST_OS_VERSION; + + /* full OS name and version */ + gopt.op_sys_full = HOST_OS; + + /* OS version */ + gopt.op_sys_vendor = HOST_VENDOR; + + /* pid file */ + gopt.pid_file = "/dev/stdout"; + + /* local domain */ + gopt.sub_domain = NULL; + + /* reset NFS (and toplvl) retransmit counter and retry interval */ + for (i=0; i<AMU_TYPE_MAX; ++i) { + gopt.amfs_auto_retrans[i] = -1; /* -1 means "never set before" */ + gopt.amfs_auto_timeo[i] = -1; /* -1 means "never set before" */ + } + + /* cache duration */ + gopt.am_timeo = AM_TTL; + + /* dismount interval */ + gopt.am_timeo_w = AM_TTL_W; + + /* map reload intervl */ + gopt.map_reload_interval = ONE_HOUR; + + /* + * various CFM_* flags that are on by default. + */ + gopt.flags = CFM_DEFAULT_FLAGS; + +#ifdef HAVE_MAP_HESIOD + /* Hesiod rhs zone */ + gopt.hesiod_base = "automount"; +#endif /* HAVE_MAP_HESIOD */ + +#ifdef HAVE_MAP_LDAP + /* LDAP base */ + gopt.ldap_base = NULL; + + /* LDAP host ports */ + gopt.ldap_hostports = NULL; + + /* LDAP cache */ + gopt.ldap_cache_seconds = 0; + gopt.ldap_cache_maxmem = 131072; + + /* LDAP protocol version */ + gopt.ldap_proto_version = 2; +#endif /* HAVE_MAP_LDAP */ + +#ifdef HAVE_MAP_NIS + /* YP domain name */ + gopt.nis_domain = NULL; +#endif /* HAVE_MAP_NIS */ +} + + +/* + * Lock process text and data segment in memory (after forking the daemon) + */ +static void +do_memory_locking(void) +{ +#if defined(HAVE_PLOCK) || defined(HAVE_MLOCKALL) + int locked_ok = 0; +#else /* not HAVE_PLOCK and not HAVE_MLOCKALL */ + plog(XLOG_WARNING, "Process memory locking not supported by the OS"); +#endif /* not HAVE_PLOCK and not HAVE_MLOCKALL */ +#ifdef HAVE_PLOCK +# ifdef _AIX + /* + * On AIX you must lower the stack size using ulimit() before calling + * plock. Otherwise plock will reserve a lot of memory space based on + * your maximum stack size limit. Since it is not easily possible to + * tell what should the limit be, I print a warning before calling + * plock(). See the manual pages for ulimit(1,3,4) on your AIX system. + */ + plog(XLOG_WARNING, "AIX: may need to lower stack size using ulimit(3) before calling plock"); +# endif /* _AIX */ + if (!locked_ok && plock(PROCLOCK) != 0) + plog(XLOG_WARNING, "Couldn't lock process pages in memory using plock(): %m"); + else + locked_ok = 1; +#endif /* HAVE_PLOCK */ +#ifdef HAVE_MLOCKALL + if (!locked_ok && mlockall(MCL_CURRENT|MCL_FUTURE) != 0) + plog(XLOG_WARNING, "Couldn't lock process pages in memory using mlockall(): %m"); + else + locked_ok = 1; +#endif /* HAVE_MLOCKALL */ +#if defined(HAVE_PLOCK) || defined(HAVE_MLOCKALL) + if (locked_ok) + plog(XLOG_INFO, "Locked process pages in memory"); +#endif /* HAVE_PLOCK || HAVE_MLOCKALL */ + +#if defined(HAVE_MADVISE) && defined(MADV_PROTECT) + madvise(0, 0, MADV_PROTECT); /* may be redundant of the above worked out */ +#endif /* defined(HAVE_MADVISE) && defined(MADV_PROTECT) */ +} + + +int +main(int argc, char *argv[]) +{ + char *domdot, *verstr, *vertmp; + int ppid = 0; + int error; + char *progname = NULL; /* "amd" */ + char hostname[MAXHOSTNAMELEN + 1] = "localhost"; /* Hostname */ + + /* + * Make sure some built-in assumptions are true before we start + */ + assert(sizeof(nfscookie) >= sizeof(u_int)); + assert(sizeof(int) >= 4); + + /* + * Set processing status. + */ + amd_state = Start; + + /* + * Determine program name + */ + if (argv[0]) { + progname = strrchr(argv[0], '/'); + if (progname && progname[1]) + progname++; + else + progname = argv[0]; + } + if (!progname) + progname = "amd"; + am_set_progname(progname); + + /* + * Initialize process id. This is kept + * cached since it is used for generating + * and using file handles. + */ + am_set_mypid(); + + /* + * Get local machine name + */ + if (gethostname(hostname, sizeof(hostname)) < 0) { + plog(XLOG_FATAL, "gethostname: %m"); + going_down(1); + } + hostname[sizeof(hostname) - 1] = '\0'; + + /* + * Check it makes sense + */ + if (!*hostname) { + plog(XLOG_FATAL, "host name is not set"); + going_down(1); + } + + /* + * Initialize global options structure. + */ + init_global_options(); + + /* + * Partially initialize hostd[]. This + * is completed in get_args(). + */ + if ((domdot = strchr(hostname, '.'))) { + /* + * Hostname already contains domainname. + * Split out hostname and domainname + * components + */ + *domdot++ = '\0'; + hostdomain = domdot; + } + xstrlcpy(hostd, hostname, sizeof(hostd)); + am_set_hostname(hostname); + + /* + * Setup signal handlers + */ + /* SIGINT: trap interrupts for shutdowns */ + setup_sighandler(SIGINT, sigterm); + /* SIGTERM: trap terminate so we can shutdown cleanly (some chance) */ + setup_sighandler(SIGTERM, sigterm); + /* SIGHUP: hangups tell us to reload the cache */ + setup_sighandler(SIGHUP, sighup); + /* + * SIGCHLD: trap Death-of-a-child. These allow us to pick up the exit + * status of backgrounded mounts. See "sched.c". + */ + setup_sighandler(SIGCHLD, sigchld); +#ifdef HAVE_SIGACTION + /* construct global "masked_sigs" used in nfs_start.c */ + sigemptyset(&masked_sigs); + sigaddset(&masked_sigs, SIGINT); + sigaddset(&masked_sigs, SIGTERM); + sigaddset(&masked_sigs, SIGHUP); + sigaddset(&masked_sigs, SIGCHLD); +#endif /* HAVE_SIGACTION */ + + /* + * Fix-up any umask problems. Most systems default + * to 002 which is not too convenient for our purposes + */ + orig_umask = umask(0); + + /* + * Figure out primary network name + */ + getwire(&PrimNetName, &PrimNetNum); + + /* + * Determine command-line arguments + */ + get_args(argc, argv); + + /* + * Log version information. + */ + vertmp = get_version_string(); + verstr = strtok(vertmp, "\n"); + plog(XLOG_INFO, "AM-UTILS VERSION INFORMATION:"); + while (verstr) { + plog(XLOG_INFO, "%s", verstr); + verstr = strtok(NULL, "\n"); + } + XFREE(vertmp); + + /* + * Get our own IP address so that we can mount the automounter. We pass + * localhost_address which could be used as the default localhost + * name/address in amu_get_myaddress(). + */ + amu_get_myaddress(&myipaddr, gopt.localhost_address); + plog(XLOG_INFO, "My ip addr is %s", inet_ntoa(myipaddr)); + + /* avoid hanging on other NFS servers if started elsewhere */ + if (chdir("/") < 0) + plog(XLOG_INFO, "cannot chdir to /: %m"); + + /* + * Now check we are root. + */ + if (geteuid() != 0) { + plog(XLOG_FATAL, "Must be root to mount filesystems (euid = %ld)", (long) geteuid()); + going_down(1); + } + +#ifdef HAVE_MAP_NIS + /* + * If the domain was specified then bind it here + * to circumvent any default bindings that may + * be done in the C library. + */ + if (gopt.nis_domain && yp_bind(gopt.nis_domain)) { + plog(XLOG_FATAL, "Can't bind to NIS domain \"%s\"", gopt.nis_domain); + going_down(1); + } +#endif /* HAVE_MAP_NIS */ + + if (!amuDebug(D_DAEMON)) + ppid = daemon_mode(); + + /* + * Lock process text and data segment in memory. + */ + if (gopt.flags & CFM_PROCESS_LOCK) { + do_memory_locking(); + } + + do_mapc_reload = clocktime(NULL) + gopt.map_reload_interval; + + /* + * Register automounter with system. + */ + error = mount_automounter(ppid); + if (error && ppid) + kill(ppid, SIGALRM); + +#ifdef HAVE_FS_AUTOFS + /* + * XXX this should be part of going_down(), but I can't move it there + * because it would be calling non-library code from the library... ugh + */ + if (amd_use_autofs) + destroy_autofs_service(); +#endif /* HAVE_FS_AUTOFS */ + + going_down(error); + + abort(); + return 1; /* should never get here */ +} diff --git a/amd/amd.h b/amd/amd.h new file mode 100644 index 000000000000..0e371b72a5b0 --- /dev/null +++ b/amd/amd.h @@ -0,0 +1,867 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/amd.h + * + */ + +#ifndef _AMD_H +#define _AMD_H + + +/* + * MACROS: + */ + +/* + * Define a default debug mtab path for systems + * that support mtab on file. + */ +#ifdef MOUNT_TABLE_ON_FILE +# define DEBUG_MNTTAB_FILE "/tmp/mnttab" +#endif /* MOUNT_TABLE_ON_FILE */ + +/* options for amd.conf */ +#define CFM_BROWSABLE_DIRS 0x00000001 +#define CFM_MOUNT_TYPE_AUTOFS 0x00000002 /* use kernel autofs support */ +#define CFM_SELECTORS_IN_DEFAULTS 0x00000004 +#define CFM_NORMALIZE_HOSTNAMES 0x00000008 +#define CFM_PROCESS_LOCK 0x00000010 +#define CFM_PRINT_PID 0x00000020 +#define CFM_RESTART_EXISTING_MOUNTS 0x00000040 +#define CFM_SHOW_STATFS_ENTRIES 0x00000080 +#define CFM_FULLY_QUALIFIED_HOSTS 0x00000100 +#define CFM_BROWSABLE_DIRS_FULL 0x00000200 /* allow '/' in readdir() */ +#define CFM_UNMOUNT_ON_EXIT 0x00000400 /* when amd finishing */ +#define CFM_USE_TCPWRAPPERS 0x00000800 +#define CFM_AUTOFS_USE_LOFS 0x00001000 +#define CFM_NFS_INSECURE_PORT 0x00002000 +#define CFM_DOMAIN_STRIP 0x00004000 +#define CFM_NORMALIZE_SLASHES 0x00008000 /* normalize slashes? */ +#define CFM_FORCED_UNMOUNTS 0x00010000 /* forced unmounts? */ +#define CFM_TRUNCATE_LOG 0x00020000 /* truncate log file? */ +#if 0 +/* XXX: reserved to sync up with am-utils-6.2 */ +#define CFM_SUN_MAP_SYNTAX 0x00040000 /* Sun map syntax? */ +#endif +#define CFM_NFS_ANY_INTERFACE 0x00080000 /* all interfaces are acceptable */ + +/* defaults global flags: plock, tcpwrappers, and autofs/lofs */ +#define CFM_DEFAULT_FLAGS (CFM_PROCESS_LOCK|CFM_USE_TCPWRAPPERS|CFM_AUTOFS_USE_LOFS|CFM_DOMAIN_STRIP|CFM_NORMALIZE_SLASHES) + +/* + * macro definitions for automounter vfs/vnode operations. + */ +#define VLOOK_CREATE 0x1 +#define VLOOK_DELETE 0x2 +#define VLOOK_LOOKUP 0x3 + +/* + * macro definitions for automounter vfs capabilities + */ +#define FS_DIRECTORY 0x0001 /* This looks like a dir, not a link */ +#define FS_MBACKGROUND 0x0002 /* Should background this mount */ +#define FS_NOTIMEOUT 0x0004 /* Don't bother with timeouts */ +#define FS_MKMNT 0x0008 /* Need to make the mount point */ +#define FS_UBACKGROUND 0x0010 /* Unmount in background */ +#define FS_BACKGROUND (FS_MBACKGROUND|FS_UBACKGROUND) +#define FS_DISCARD 0x0020 /* Discard immediately on last reference */ +#define FS_AMQINFO 0x0040 /* Amq is interested in this fs type */ +#define FS_AUTOFS 0x0080 /* This filesystem can be an autofs f/s */ +#define FS_DIRECT 0x0100 /* Direct mount */ +#define FS_ON_AUTOFS 0x0200 /* This filesystem can be mounted directly + onto an autofs mountpoint */ + +/* + * macros for struct am_node (map of auto-mount points). + */ +#define AMF_NOTIMEOUT 0x0001 /* This node never times out */ +#define AMF_ROOT 0x0002 /* This is a root node */ +#define AMF_AUTOFS 0x0004 /* This node is part of an autofs filesystem */ +#define AMF_REMOUNT 0x0008 /* This node needs to be remounted */ +#define AMF_SOFTLOOKUP 0x0010 /* This node returns EIO if server is down */ + +/* + * macros for struct mntfs (list of mounted filesystems) + */ +#define MFF_MOUNTED 0x0001 /* Node is mounted */ +#define MFF_MOUNTING 0x0002 /* Mount is in progress */ +#define MFF_UNMOUNTING 0x0004 /* Unmount is in progress */ +#define MFF_RESTART 0x0008 /* Restarted node */ +#define MFF_MKMNT 0x0010 /* Delete this node's am_mount */ +#define MFF_ERROR 0x0020 /* This node failed to mount */ +#define MFF_LOGDOWN 0x0040 /* Logged that this mount is down */ +#define MFF_RSTKEEP 0x0080 /* Don't timeout this filesystem - restarted */ +#define MFF_WANTTIMO 0x0100 /* Need a timeout call when not busy */ +#define MFF_NFSLINK 0x0200 /* nfsl type, and deemed a link */ +#define MFF_IS_AUTOFS 0x0400 /* this filesystem is of type autofs */ +#define MFF_NFS_SCALEDOWN 0x0800 /* the mount failed, retry with v2/UDP */ +#define MFF_ON_AUTOFS 0x1000 /* autofs has a lofs/link to this f/s */ +#define MFF_WEBNFS 0x2000 /* use public filehandle */ + +/* + * macros for struct fserver. + */ +#define FSF_VALID 0x0001 /* Valid information available */ +#define FSF_DOWN 0x0002 /* This fileserver is thought to be down */ +#define FSF_ERROR 0x0004 /* Permanent error has occurred */ +#define FSF_WANT 0x0008 /* Want a wakeup call */ +#define FSF_PINGING 0x0010 /* Already doing pings */ +#define FSF_WEBNFS 0x0020 /* Don't try to contact portmapper */ +#define FSF_PING_UNINIT 0x0040 /* ping values have not been initilized */ +#define FSF_FORCE_UNMOUNT 0x0080 /* force umount of this fserver */ +#define FSRV_ERROR(fs) ((fs) && (((fs)->fs_flags & FSF_ERROR) == FSF_ERROR)) +#define FSRV_ISDOWN(fs) ((fs) && (((fs)->fs_flags & (FSF_DOWN|FSF_VALID)) == (FSF_DOWN|FSF_VALID))) +#define FSRV_ISUP(fs) (!(fs) || (((fs)->fs_flags & (FSF_DOWN|FSF_VALID)) == (FSF_VALID))) + +/* some systems (SunOS 4.x) neglect to define the mount null message */ +#ifndef MOUNTPROC_NULL +# define MOUNTPROC_NULL ((u_long)(0)) +#endif /* not MOUNTPROC_NULL */ + +/* + * Error to return if remote host is not available. + * Try, in order, "host down", "host unreachable", "invalid argument". + */ +#ifdef EHOSTDOWN +# define AM_ERRNO_HOST_DOWN EHOSTDOWN +#else /* not EHOSTDOWN */ +# ifdef EHOSTUNREACH +# define AM_ERRNO_HOST_DOWN EHOSTUNREACH +# else /* not EHOSTUNREACH */ +# define AM_ERRNO_HOST_DOWN EINVAL +# endif /* not EHOSTUNREACH */ +#endif /* not EHOSTDOWN */ + +/* Hash table size */ +#define NKVHASH (1 << 2) /* Power of two */ + +/* Max entries to return in one call */ +#define MAX_READDIR_ENTRIES 16 + +/* + * default amfs_auto retrans - 1/10th seconds + */ +#define AMFS_AUTO_RETRANS(x) ((ALLOWED_MOUNT_TIME*10+5*gopt.amfs_auto_timeo[(x)])/gopt.amfs_auto_timeo[(x)] * 2) + +/* + * The following values can be tuned... + */ +#define AM_TTL (300) /* Default cache period (5 min) */ +#define AM_TTL_W (120) /* Default unmount interval (2 min) */ +#define AM_PINGER 30 /* NFS ping interval for live systems */ +#define AMFS_AUTO_TIMEO 8 /* Default amfs_auto timeout - .8s */ +#define AMFS_EXEC_MAP_TIMEOUT 10 /* default 10sec exec map timeout */ + +/* interval between forced retries of a mount */ +#define RETRY_INTERVAL 2 + +#ifndef ROOT_MAP +# define ROOT_MAP "\"root\"" +#endif /* not ROOT_MAP */ + +#define ereturn(x) do { *error_return = x; return 0; } while (0) + +#define NEVER (time_t) 0 + +#if defined(HAVE_TCPD_H) && defined(HAVE_LIBWRAP) +# define AMD_SERVICE_NAME "amd" /* for tcpwrappers */ +#endif /* defined(HAVE_TCPD_H) && defined(HAVE_LIBWRAP) */ + +/* + * TYPEDEFS: + */ + +typedef struct cf_map cf_map_t; +typedef struct kv kv; +typedef struct am_node am_node; +typedef struct mntfs mntfs; +typedef struct am_opts am_opts; +typedef struct am_ops am_ops; +typedef struct am_stats am_stats; +typedef struct fserver fserver; + +typedef voidp wchan_t; +typedef voidp opaque_t; + +/* + * Cache map operations + */ +typedef void add_fn(mnt_map *, char *, char *); +typedef int init_fn(mnt_map *, char *, time_t *); +typedef int mtime_fn(mnt_map *, char *, time_t *); +typedef int isup_fn(mnt_map *, char *); +typedef int reload_fn(mnt_map *, char *, add_fn *); +typedef int search_fn(mnt_map *, char *, char *, char **, time_t *); +typedef int task_fun(opaque_t); +typedef void cb_fun(int, int, opaque_t); +typedef void fwd_fun(voidp, int, struct sockaddr_in *, + struct sockaddr_in *, opaque_t, int); +typedef int key_fun(char *, opaque_t); +typedef void callout_fun(opaque_t); + +/* + * automounter vfs/vnode operations. + */ +typedef char *(*vfs_match) (am_opts *); +typedef int (*vfs_init) (mntfs *); +typedef int (*vmount_fs) (am_node *, mntfs *); +typedef int (*vumount_fs) (am_node *, mntfs *); +typedef am_node *(*vlookup_child) (am_node *, char *, int *, int); +typedef am_node *(*vmount_child) (am_node *, int *); +typedef int (*vreaddir) (am_node *, nfscookie, nfsdirlist *, nfsentry *, u_int); +typedef am_node *(*vreadlink) (am_node *, int *); +typedef void (*vmounted) (mntfs *); +typedef void (*vumounted) (mntfs *); +typedef fserver *(*vffserver) (mntfs *); +typedef wchan_t (*vget_wchan) (mntfs *); + + + +/* + * STRUCTURES: + */ + +/* global amd options that are manipulated by conf.c */ +struct amu_global_options { + char *arch; /* name of current architecture */ + char *auto_dir; /* automounter temp dir */ + int auto_attrcache; /* attribute cache timeout for auto dirs */ + char *cluster; /* cluster name */ + char *karch; /* kernel architecture */ + char *logfile; /* amd log file */ + char *op_sys; /* operating system name ${os} */ + char *op_sys_ver; /* OS version ${osver} */ + char *op_sys_full; /* full OS name ${full_os} */ + char *op_sys_vendor; /* name of OS vendor ${vendor} */ + char *pid_file; /* PID file */ + char *sub_domain; /* local domain */ + char *localhost_address; /* localhost address (NULL means use 127.0.0.1) */ + char *map_defaults; /* global map /default options */ + char *map_options; /* global map options */ + int map_reload_interval; /* map reload interval */ + char *map_type; /* global map type */ + char *search_path; /* search path for maps */ + char *mount_type; /* mount type for map */ + char *debug_mtab_file; /* path for the mtab file during debug mode */ + u_int flags; /* various CFM_* flags */ + +#define AMU_TYPE_NONE -1 /* for amfs_auto_{timeo,retrans,toplvl} */ +#define AMU_TYPE_UDP 0 /* for amfs_auto_{timeo,retrans,toplvl} */ +#define AMU_TYPE_TCP 1 /* for amfs_auto_{timeo,retrans,toplvl} */ + /* + * Note: toplvl is only UDP, but we want to separate it from regular + * NFS mounts which Amd makes, because the toplvl mount is a localhost + * mount for which different timeo/retrans parameters may be desired. + */ +#define AMU_TYPE_TOPLVL 2 /* for amfs_auto_{timeo,retrans,toplvl} */ +#define AMU_TYPE_MAX 3 /* for amfs_auto_{timeo,retrans,toplvl} */ + int amfs_auto_retrans[AMU_TYPE_MAX]; /* NFS retransmit counter */ + int amfs_auto_timeo[AMU_TYPE_MAX]; /* NFS retry interval */ + + int am_timeo; /* cache duration */ + int am_timeo_w; /* dismount interval */ + u_long portmap_program; /* amd RPC program number */ + u_short preferred_amq_port; /* preferred amq service RPC port number (0 means "any") */ +#ifdef HAVE_MAP_HESIOD + char *hesiod_base; /* Hesiod rhs */ +#endif /* HAVE_MAP_HESIOD */ +#ifdef HAVE_MAP_LDAP + char *ldap_base; /* LDAP base */ + char *ldap_hostports; /* LDAP host ports */ + long ldap_cache_seconds; /* LDAP internal cache - keep seconds */ + long ldap_cache_maxmem; /* LDAP internal cache - max memory (bytes) */ + long ldap_proto_version; /* LDAP protocol version */ +#endif /* HAVE_MAP_LDAP */ +#ifdef HAVE_MAP_NIS + char *nis_domain; /* YP domain name */ +#endif /* HAVE_MAP_NIS */ + char *nfs_proto; /* NFS protocol (NULL, udp, tcp) */ + int nfs_vers; /* NFS version (0, 2, 3, 4) */ + u_int exec_map_timeout; /* timeout (seconds) for executable maps */ +}; + +/* if you add anything here, update conf.c:reset_cf_map() */ +struct cf_map { + char *cfm_dir; /* /home, /u, /src */ + char *cfm_name; /* amd.home, /etc/amd.home ... */ + char *cfm_type; /* file, hesiod, ndbm, nis ... */ + char *cfm_defaults; /* map /defaults options in amd.conf */ + char *cfm_opts; /* -cache:=all, etc. */ + char *cfm_search_path; /* /etc/local:/etc/amdmaps:/misc/yp */ + char *cfm_tag; /* optional map tag for amd -T */ + u_int cfm_flags; /* browsable_dirs? mount_type? */ + struct cf_map *cfm_next; /* pointer to next in list (if any) */ +}; + +/* + * Key-value pair + */ +struct kv { + kv *next; + char *key; +#ifdef HAVE_REGEXEC + regex_t re; /* store the regexp from regcomp() */ +#endif /* HAVE_REGEXEC */ + char *val; +}; + +struct mnt_map { + qelem hdr; + int refc; /* Reference count */ + short flags; /* Allocation flags */ + short alloc; /* Allocation mode */ + time_t modify; /* Modify time of map */ + u_int reloads; /* Number of times map was reloaded */ + char *map_name; /* Name of this map */ + char *wildcard; /* Wildcard value */ + reload_fn *reload; /* Function to be used for reloads */ + isup_fn *isup; /* Is service up or not? (1=up, 0=down) */ + search_fn *search; /* Function to be used for searching */ + mtime_fn *mtime; /* Modify time function */ + kv *kvhash[NKVHASH]; /* Cached data */ + cf_map_t *cfm; /* pointer to per-map amd.conf opts, if any */ + void *map_data; /* Map data black box */ +}; + +/* + * Options + */ +struct am_opts { + char *fs_glob; /* Smashed copy of global options */ + char *fs_local; /* Expanded copy of local options */ + char *fs_mtab; /* Mount table entry */ + /* Other options ... */ + char *opt_dev; + char *opt_delay; + char *opt_dir; + char *opt_fs; + char *opt_group; + char *opt_mount; + char *opt_opts; + char *opt_remopts; + char *opt_pref; + char *opt_cache; + char *opt_rfs; + char *opt_rhost; + char *opt_sublink; + char *opt_type; + char *opt_mount_type; /* "nfs" or "autofs" */ + char *opt_unmount; + char *opt_umount; /* an "alias" for opt_unmount (type:=program) */ + char *opt_user; + char *opt_maptype; /* map type: file, nis, hesiod, etc. */ + char *opt_cachedir; /* cache directory */ + char *opt_addopts; /* options to add to opt_opts */ +}; + +struct am_ops { + char *fs_type; /* type of filesystems e.g. "nfsx" */ + vfs_match fs_match; /* fxn: match */ + vfs_init fs_init; /* fxn: initialization */ + vmount_fs mount_fs; /* fxn: mount my own vnode */ + vumount_fs umount_fs; /* fxn: unmount my own vnode */ + vlookup_child lookup_child; /* fxn: lookup path-name */ + vmount_child mount_child; /* fxn: mount path-name */ + vreaddir readdir; /* fxn: read directory */ + vreadlink readlink; /* fxn: read link */ + vmounted mounted; /* fxn: after-mount extra actions */ + vumounted umounted; /* fxn: after-umount extra actions */ + vffserver ffserver; /* fxn: find a file server */ + vget_wchan get_wchan; /* fxn: get the waiting channel */ + int nfs_fs_flags; /* filesystem flags FS_* for nfs mounts */ +#ifdef HAVE_FS_AUTOFS + int autofs_fs_flags;/* filesystem flags FS_* for autofs mounts */ +#endif /* HAVE_FS_AUTOFS */ +}; + +/* + * List of mounted filesystems + */ +struct mntfs { + qelem mf_q; /* List of mounted filesystems */ + am_ops *mf_ops; /* Operations on this mountpoint */ + am_opts *mf_fo; /* File opts */ + char *mf_mount; /* "/a/kiska/home/kiska" */ + char *mf_info; /* Mount info */ + char *mf_auto; /* Automount opts */ + char *mf_mopts; /* FS mount opts */ + char *mf_remopts; /* Remote FS mount opts */ + char *mf_loopdev; /* loop device name for /dev/loop mounts */ + fserver *mf_server; /* File server */ + int mf_fsflags; /* Flags FS_* copied from mf_ops->*_fs_flags */ + int mf_flags; /* Flags MFF_* */ + int mf_error; /* Error code from background mount */ + int mf_refc; /* Number of references to this node */ + int mf_cid; /* Callout id */ + void (*mf_prfree) (opaque_t); /* Free private space */ + opaque_t mf_private; /* Private - per-fs data */ +}; + +/* + * List of fileservers + */ +struct fserver { + qelem fs_q; /* List of fileservers */ + int fs_refc; /* Number of references to this server */ + char *fs_host; /* Normalized hostname of server */ + struct sockaddr_in *fs_ip; /* Network address of server */ + int fs_cid; /* Callout id */ + int fs_pinger; /* Ping (keepalive) interval */ + int fs_flags; /* Flags */ + char *fs_type; /* File server type */ + u_long fs_version; /* NFS version of server (2, 3, etc.)*/ + char *fs_proto; /* NFS protocol of server (tcp, udp, etc.) */ + opaque_t fs_private; /* Private data */ + void (*fs_prfree) (opaque_t); /* Free private data */ +}; + +/* + * Per-mountpoint statistics + */ +struct am_stats { + time_t s_mtime; /* Mount time */ + u_short s_uid; /* Uid of mounter */ + int s_getattr; /* Count of getattrs */ + int s_lookup; /* Count of lookups */ + int s_readdir; /* Count of readdirs */ + int s_readlink; /* Count of readlinks */ + int s_statfs; /* Count of statfs */ +}; + +/* + * System statistics + */ +struct amd_stats { + int d_drops; /* Dropped requests */ + int d_stale; /* Stale NFS handles */ + int d_mok; /* Successful mounts */ + int d_merr; /* Failed mounts */ + int d_uerr; /* Failed unmounts */ +}; +extern struct amd_stats amd_stats; + +/* + * Map of auto-mount points. + */ +struct am_node { + int am_mapno; /* Map number */ + mntfs *am_mnt; /* Mounted filesystem */ + mntfs **am_mfarray; /* Filesystem sources to try to mount */ + char *am_name; /* "kiska": name of this node */ + char *am_path; /* "/home/kiska": path of this node's mount point */ + char *am_link; /* "/a/kiska/home/kiska/this/that": link to sub-dir */ + am_node *am_parent; /* Parent of this node */ + am_node *am_ysib; /* Younger sibling of this node */ + am_node *am_osib; /* Older sibling of this node */ + am_node *am_child; /* First child of this node */ + nfsattrstat am_attr; /* File attributes */ +#define am_fattr am_attr.ns_u.ns_attr_u + int am_flags; /* Boolean flags AMF_* */ + int am_error; /* Specific mount error */ + time_t am_ttl; /* Time to live */ + int am_timeo_w; /* Dismount wait interval */ + int am_timeo; /* Cache timeout interval */ + u_int am_gen; /* Generation number */ + char *am_pref; /* Mount info prefix */ + am_stats am_stats; /* Statistics gathering */ + SVCXPRT *am_transp; /* Info for quick reply */ + dev_t am_dev; /* Device number */ + dev_t am_rdev; /* Remote/real device number */ +#ifdef HAVE_FS_AUTOFS + autofs_fh_t *am_autofs_fh; + time_t am_autofs_ttl; /* Time to expire autofs nodes */ +#endif /* HAVE_FS_AUTOFS */ +}; + +/* + * EXTERNALS: + */ + +/* + * Amq server global functions + */ +extern amq_mount_info_list *amqproc_getmntfs_1_svc(voidp argp, struct svc_req *rqstp); +extern amq_mount_stats *amqproc_stats_1_svc(voidp argp, struct svc_req *rqstp); +extern amq_mount_tree_list *amqproc_export_1_svc(voidp argp, struct svc_req *rqstp); +extern amq_mount_tree_p *amqproc_mnttree_1_svc(voidp argp, struct svc_req *rqstp); +extern amq_string *amqproc_getvers_1_svc(voidp argp, struct svc_req *rqstp); +extern amq_string *amqproc_pawd_1_svc(voidp argp, struct svc_req *rqstp); +extern int *amqproc_getpid_1_svc(voidp argp, struct svc_req *rqstp); +extern int *amqproc_mount_1_svc(voidp argp, struct svc_req *rqstp); +extern int *amqproc_setopt_1_svc(voidp argp, struct svc_req *rqstp); +extern voidp amqproc_null_1_svc(voidp argp, struct svc_req *rqstp); +extern voidp amqproc_umnt_1_svc(voidp argp, struct svc_req *rqstp); + +/* other external definitions */ +extern am_nfs_fh *get_root_nfs_fh(char *dir); +extern am_node *find_ap(char *); +extern am_node *get_ap_child(am_node *, char *); +extern bool_t xdr_amq_mount_info_qelem(XDR *xdrs, qelem *qhead); +extern fserver *find_nfs_srvr(mntfs *mf); +extern int mount_nfs_fh(am_nfs_handle_t *fhp, char *mntdir, char *fs_name, mntfs *mf); +extern int process_all_regular_maps(void); +extern cf_map_t *find_cf_map(const char *name); +extern int set_conf_kv(const char *section, const char *k, const char *v); +extern int mount_node(opaque_t arg); +extern int unmount_mp(am_node *mp); +extern int yyparse (void); + +extern void amfs_mkcacheref(mntfs *mf); +extern int amfs_mount(am_node *mp, mntfs *mf, char *opts); +extern void assign_error_mntfs(am_node *mp); +extern am_node *next_nonerror_node(am_node *xp); +extern void flush_srvr_nfs_cache(fserver *fs); +extern void am_mounted(am_node *); +extern void mf_mounted(mntfs *mf, bool_t call_free_opts); +extern void am_unmounted(am_node *); +extern am_node *get_exported_ap(int index); +extern am_node *get_first_exported_ap(int *index); +extern am_node *get_next_exported_ap(int *index); +extern am_node *path_to_exported_ap(char *path); +extern am_node *exported_ap_alloc(void); +extern am_node *find_mf(mntfs *); +extern am_node *next_map(int *); +extern am_ops *ops_match(am_opts *, char *, char *, char *, char *, char *); +extern am_ops *ops_search(char *); +extern fserver *dup_srvr(fserver *); +extern void srvrlog(fserver *, char *); +extern int get_mountd_port(fserver *, u_short *, wchan_t); +extern void flush_nfs_fhandle_cache(fserver *); + +extern mntfs *dup_mntfs(mntfs *); +extern mntfs *find_mntfs(am_ops *, am_opts *, char *, char *, char *, char *, char *); +extern mntfs *locate_mntfs(am_ops *, am_opts *, char *, char *, char *, char *, char *); +extern mntfs *new_mntfs(void); +extern mntfs *realloc_mntfs(mntfs *, am_ops *, am_opts *, char *, char *, char *, char *, char *); +extern void flush_mntfs(void); +extern void free_mntfs(voidp); + + +extern void amq_program_1(struct svc_req *rqstp, SVCXPRT *transp); +extern int background(void); +extern void deslashify(char *); +extern void do_task_notify(void); +extern int eval_fs_opts(am_opts *, char *, char *, char *, char *, char *); +extern void forcibly_timeout_mp(am_node *); +extern void free_map(am_node *); +extern void free_opts(am_opts *); +extern void free_srvr(fserver *); +extern int fwd_init(void); +extern int fwd_packet(int, char *, int, struct sockaddr_in *, struct sockaddr_in *, opaque_t, fwd_fun *); +extern void fwd_reply(void); +extern void get_args(int argc, char *argv[]); +extern wchan_t get_mntfs_wchan(mntfs *mf); +extern void host_normalize(char **); +extern void init_map(am_node *, char *); +extern void ins_que(qelem *, qelem *); +extern void insert_am(am_node *, am_node *); +extern int make_nfs_auth(void); +extern void make_root_node(void); +extern void map_flush_srvr(fserver *); +extern void mapc_add_kv(mnt_map *, char *, char *); +extern mnt_map *mapc_find(char *, char *, const char *); +extern void mapc_free(opaque_t); +extern int mapc_keyiter(mnt_map *, key_fun, opaque_t); +extern void mapc_reload(void); +extern int mapc_search(mnt_map *, char *, char **); +extern void mapc_showtypes(char *buf, size_t l); +extern int mapc_type_exists(const char *type); +extern void mk_fattr(nfsfattr *, nfsftype); +extern int mount_auto_node(char *, opaque_t); +extern int mount_automounter(int); +extern int mount_exported(void); +extern void mp_to_fh(am_node *, am_nfs_fh *); +extern void new_ttl(am_node *); +extern void nfs_quick_reply(am_node *mp, int error); +extern void normalize_slash(char *); +extern void ops_showamfstypes(char *buf, size_t l); +extern void ops_showfstypes(char *outbuf, size_t l); +extern void rem_que(qelem *); +extern void reschedule_timeout_mp(void); +extern void restart(void); +extern void restart_automounter_nodes(void); +extern int root_keyiter(key_fun *, opaque_t); +extern void root_newmap(const char *, const char *, const char *, const cf_map_t *); +extern void run_task(task_fun *, opaque_t, cb_fun *, opaque_t); +extern void sched_task(cb_fun *, opaque_t, wchan_t); +extern int softclock(void); +extern int timeout(u_int, void (*fn)(opaque_t), opaque_t); +extern void timeout_mp(opaque_t); +extern void untimeout(int); +extern void umount_exported(void); +extern int valid_key(char *); +extern void wakeup(wchan_t); +extern void wakeup_srvr(fserver *); +extern void wakeup_task(int, int, wchan_t); +#define SIZEOF_PID_FSNAME (16 + MAXHOSTNAMELEN) +extern char pid_fsname[SIZEOF_PID_FSNAME]; /* "kiska.southseas.nz:(pid%d)" */ +#define SIZEOF_HOSTD (2 * MAXHOSTNAMELEN + 1) +extern char hostd[SIZEOF_HOSTD]; /* Host+domain */ +#define SIZEOF_OPTS 256 /* used for char opts[] and preopts[] */ + +/* + * Global variables. + */ +extern FILE *yyin; +extern SVCXPRT *current_transp; /* For nfs_quick_reply() */ +extern char *conf_tag; +#define SIZEOF_UID_STR 12 +#define SIZEOF_GID_STR 12 +extern char *opt_gid, gid_str[SIZEOF_GID_STR]; +extern char *opt_uid, uid_str[SIZEOF_UID_STR]; +extern int NumChildren; +extern int fwd_sock; +extern int select_intr_valid; +extern int immediate_abort; /* Should close-down unmounts be retried */ +extern int usage; +extern int use_conf_file; /* use amd configuration file */ +extern int task_notify_todo; /* Task notifier needs running */ +extern jmp_buf select_intr; +extern qelem mfhead; +extern struct amu_global_options gopt; /* where global options are stored */ +extern time_t do_mapc_reload; /* Flush & reload mount map cache */ +extern time_t next_softclock; /* Time to call softclock() */ + +#ifdef HAVE_SIGACTION +extern sigset_t masked_sigs; +#endif /* HAVE_SIGACTION */ + +#if defined(HAVE_AMU_FS_LINK) || defined(HAVE_AMU_FS_LINKX) +extern char *amfs_link_match(am_opts *fo); +#endif /* defined(HAVE_AMU_FS_LINK) || defined(HAVE_AMU_FS_LINKX) */ + +#ifdef HAVE_FS_AUTOFS +extern int amd_use_autofs; + +extern int autofs_get_fh(am_node *mp); +extern void autofs_release_fh(am_node *mp); +extern void autofs_get_mp(am_node *mp); +extern void autofs_release_mp(am_node *mp); +extern void autofs_add_fdset(fd_set *readfds); +extern int autofs_handle_fdset(fd_set *readfds, int nsel); +extern void autofs_mounted(am_node *mp); +extern void autofs_mount_succeeded(am_node *mp); +extern void autofs_mount_failed(am_node *mp); +extern int autofs_umount_succeeded(am_node *mp); +extern int autofs_umount_failed(am_node *mp); +extern int autofs_mount_fs(am_node *mp, mntfs *mf); +extern int autofs_umount_fs(am_node *mp, mntfs *mf); +extern void autofs_get_opts(char *opts, size_t l, autofs_fh_t *fh); +extern int autofs_compute_mount_flags(mntent_t *); +extern void autofs_timeout_mp(am_node *); +extern int create_autofs_service(void); +extern int destroy_autofs_service(void); +#endif /* HAVE_FS_AUTOFS */ + +/**************************************************************************/ +/*** Generic file-system types, implemented as part of the native O/S. ***/ +/**************************************************************************/ + +/* + * Loopback File System + * Many systems can't support this, and in any case most of the + * functionality is available with Symlink FS. + */ +#ifdef HAVE_FS_LOFS +extern am_ops lofs_ops; +extern int mount_lofs(char *mntdir, char *fs_name, char *opts, int on_autofs); +#endif /* HAVE_FS_LOFS */ + +/* + * CD-ROM File System (CD-ROM) + * (HSFS: High Sierra F/S on some machines) + * Many systems can't support this, and in any case most of the + * functionality is available with program FS. + */ +#ifdef HAVE_FS_CDFS +extern am_ops cdfs_ops; +#endif /* HAVE_FS_CDFS */ + +/* + * PC File System (MS-DOS) + * Many systems can't support this, and in any case most of the + * functionality is available with program FS. + */ +#ifdef HAVE_FS_PCFS +extern am_ops pcfs_ops; +#endif /* HAVE_FS_PCFS */ + +/* + * Caching File System (Solaris) + */ +#ifdef HAVE_FS_CACHEFS +extern am_ops cachefs_ops; +#endif /* HAVE_FS_CACHEFS */ + +/* + * Network File System + * Good, slow, NFS V.2. + */ +#ifdef HAVE_FS_NFS +extern am_ops nfs_ops; /* NFS */ +extern fserver *find_nfs_srvr (mntfs *); +extern qelem nfs_srvr_list; +#endif /* HAVE_FS_NFS */ + +/* + * Un*x File System + * Normal local disk file system. + */ +#ifdef HAVE_FS_UFS +extern am_ops ufs_ops; /* Un*x file system */ +#endif /* HAVE_FS_UFS */ + +/* Unix file system (irix) */ +#ifdef HAVE_FS_XFS +extern am_ops xfs_ops; /* Un*x file system */ +#endif /* HAVE_FS_XFS */ + +/* Unix file system (irix) */ +#ifdef HAVE_FS_EFS +extern am_ops efs_ops; /* Un*x file system */ +#endif /* HAVE_FS_EFS */ + +/**************************************************************************/ +/*** Automounter file-system types, implemented by amd. ***/ +/**************************************************************************/ + +/* + * Root AMD File System + */ +extern am_ops amfs_root_ops; /* Root file system */ + +/* + * Generic amfs helper methods + */ +extern am_node *amfs_generic_lookup_child(am_node *mp, char *fname, int *error_return, int op); +extern am_node *amfs_generic_mount_child(am_node *ap, int *error_return); +extern int amfs_generic_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, u_int count); +extern int amfs_generic_umount(am_node *mp, mntfs *mf); +extern void amfs_generic_mounted(mntfs *mf); +extern char *amfs_generic_match(am_opts *fo); +extern fserver *amfs_generic_find_srvr(mntfs *); + +/* + * Automount File System + */ +#ifdef HAVE_AMU_FS_AUTO +extern am_ops amfs_auto_ops; /* Automount file system (this!) */ +#endif /* HAVE_AMU_FS_AUTO */ + +/* + * Toplvl Automount File System + */ +#ifdef HAVE_AMU_FS_TOPLVL +extern am_ops amfs_toplvl_ops; /* Toplvl Automount file system */ +extern int amfs_toplvl_mount(am_node *mp, mntfs *mf); +extern int amfs_toplvl_umount(am_node *mp, mntfs *mf); +#endif /* HAVE_AMU_FS_TOPLVL */ + +/* + * Direct Automount File System + */ +#ifdef HAVE_AMU_FS_DIRECT +extern am_ops amfs_direct_ops; /* Direct Automount file system (this too) */ +#endif /* HAVE_AMU_FS_DIRECT */ + +/* + * Error File System + */ +#ifdef HAVE_AMU_FS_ERROR +extern am_ops amfs_error_ops; /* Error file system */ +extern am_node *amfs_error_lookup_child(am_node *mp, char *fname, int *error_return, int op); +extern am_node *amfs_error_mount_child(am_node *ap, int *error_return); +extern int amfs_error_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, u_int count); +#endif /* HAVE_AMU_FS_ERROR */ + +/* + * NFS mounts with local existence check. + */ +#ifdef HAVE_AMU_FS_NFSL +extern am_ops amfs_nfsl_ops; /* NFSL */ +#endif /* HAVE_AMU_FS_NFSL */ + +/* + * Multi-nfs mounts. + */ +#ifdef HAVE_AMU_FS_NFSX +extern am_ops amfs_nfsx_ops; /* NFSX */ +#endif /* HAVE_AMU_FS_NFSX */ + +/* + * NFS host - a whole tree. + */ +#ifdef HAVE_AMU_FS_HOST +extern am_ops amfs_host_ops; /* NFS host */ +#endif /* HAVE_AMU_FS_HOST */ + +/* + * Program File System + * This is useful for things like RVD. + */ +#ifdef HAVE_AMU_FS_PROGRAM +extern am_ops amfs_program_ops; /* Program File System */ +#endif /* HAVE_AMU_FS_PROGRAM */ + +/* + * Symbolic-link file system. + * A "filesystem" which is just a symbol link. + */ +#ifdef HAVE_AMU_FS_LINK +extern am_ops amfs_link_ops; /* Symlink FS */ +#endif /* HAVE_AMU_FS_LINK */ + +/* + * Symbolic-link file system, which also checks that the target of + * the symlink exists. + * A "filesystem" which is just a symbol link. + */ +#ifdef HAVE_AMU_FS_LINKX +extern am_ops amfs_linkx_ops; /* Symlink FS with existence check */ +#endif /* HAVE_AMU_FS_LINKX */ + +/* + * Union file system + */ +#ifdef HAVE_AMU_FS_UNION +extern am_ops amfs_union_ops; /* Union FS */ +#endif /* HAVE_AMU_FS_UNION */ + +#endif /* not _AMD_H */ diff --git a/amd/amfs_auto.c b/amd/amfs_auto.c new file mode 100644 index 000000000000..75a451fd111d --- /dev/null +++ b/amd/amfs_auto.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/amfs_auto.c + * + */ + +/* + * Automount file system + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/**************************************************************************** + *** MACROS *** + ****************************************************************************/ + + +/**************************************************************************** + *** STRUCTURES *** + ****************************************************************************/ + + +/**************************************************************************** + *** FORWARD DEFINITIONS *** + ****************************************************************************/ +static int amfs_auto_mount(am_node *mp, mntfs *mf); + + +/**************************************************************************** + *** OPS STRUCTURES *** + ****************************************************************************/ +am_ops amfs_auto_ops = +{ + "auto", + amfs_generic_match, + 0, /* amfs_auto_init */ + amfs_auto_mount, + amfs_generic_umount, + amfs_generic_lookup_child, + amfs_generic_mount_child, + amfs_generic_readdir, + 0, /* amfs_auto_readlink */ + amfs_generic_mounted, + 0, /* amfs_auto_umounted */ + amfs_generic_find_srvr, + 0, /* amfs_auto_get_wchan */ + FS_AMQINFO | FS_DIRECTORY, +#ifdef HAVE_FS_AUTOFS + AUTOFS_AUTO_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ +}; + + +/**************************************************************************** + *** FUNCTIONS *** + ****************************************************************************/ +/* + * Mount a sub-mount + */ +static int +amfs_auto_mount(am_node *mp, mntfs *mf) +{ + /* + * Pseudo-directories are used to provide some structure + * to the automounted directories instead + * of putting them all in the top-level automount directory. + * + * Here, just increment the parent's link count. + */ + mp->am_parent->am_fattr.na_nlink++; + + /* + * Info field of . means use parent's info field. + * Historical - not documented. + */ + if (mf->mf_info[0] == '.' && mf->mf_info[1] == '\0') + mf->mf_info = strealloc(mf->mf_info, mp->am_parent->am_mnt->mf_info); + + /* + * Compute prefix: + * + * If there is an option prefix then use that else + * If the parent had a prefix then use that with name + * of this node appended else + * Use the name of this node. + * + * That means if you want no prefix you must say so + * in the map. + */ + if (mf->mf_fo->opt_pref) { + /* allow pref:=null to set a real null prefix */ + if (STREQ(mf->mf_fo->opt_pref, "null")) { + mp->am_pref = strdup(""); + } else { + /* + * the prefix specified as an option + */ + mp->am_pref = strdup(mf->mf_fo->opt_pref); + } + } else { + /* + * else the parent's prefix + * followed by the name + * followed by / + */ + char *ppref = mp->am_parent->am_pref; + if (ppref == 0) + ppref = ""; + mp->am_pref = str3cat((char *) 0, ppref, mp->am_name, "/"); + } + +#ifdef HAVE_FS_AUTOFS + if (mf->mf_flags & MFF_IS_AUTOFS) { + char opts[SIZEOF_OPTS]; + int error; + + autofs_get_opts(opts, sizeof(opts), mp->am_autofs_fh); + + /* now do the mount */ + error = amfs_mount(mp, mf, opts); + if (error) { + errno = error; + plog(XLOG_FATAL, "amfs_auto_mount: amfs_mount failed: %m"); + return error; + } + } +#endif /* HAVE_FS_AUTOFS */ + + /* + * Attach a map cache + */ + amfs_mkcacheref(mf); + + return 0; +} diff --git a/amd/amfs_direct.c b/amd/amfs_direct.c new file mode 100644 index 000000000000..dbef743d0d7e --- /dev/null +++ b/amd/amfs_direct.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/amfs_direct.c + * + */ + +/* + * Direct file system + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/**************************************************************************** + *** FORWARD DEFINITIONS *** + ****************************************************************************/ +static am_node *amfs_direct_readlink(am_node *mp, int *error_return); + +/**************************************************************************** + *** OPS STRUCTURES *** + ****************************************************************************/ +am_ops amfs_direct_ops = +{ + "direct", + amfs_generic_match, + 0, /* amfs_direct_init */ + amfs_toplvl_mount, + amfs_toplvl_umount, + amfs_generic_lookup_child, + amfs_generic_mount_child, + amfs_error_readdir, + amfs_direct_readlink, + amfs_generic_mounted, + 0, /* amfs_direct_umounted */ + amfs_generic_find_srvr, + 0, /* amfs_direct_get_wchan */ + FS_DIRECT | FS_MKMNT | FS_NOTIMEOUT | FS_BACKGROUND | FS_AMQINFO, +#ifdef HAVE_FS_AUTOFS + AUTOFS_DIRECT_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ +}; + + +/**************************************************************************** + *** FUNCTIONS *** + ****************************************************************************/ + +static am_node * +amfs_direct_readlink(am_node *mp, int *error_return) +{ + am_node *xp; + int rc = 0; + + xp = next_nonerror_node(mp->am_child); + if (!xp) { + if (!mp->am_mnt->mf_private) + amfs_mkcacheref(mp->am_mnt); /* XXX */ + xp = amfs_generic_lookup_child(mp, mp->am_path + 1, &rc, VLOOK_CREATE); + if (xp && rc < 0) + xp = amfs_generic_mount_child(xp, &rc); + } + if (xp) { + new_ttl(xp); /* (7/12/89) from Rein Tollevik */ + return xp; + } + if (amd_state == Finishing) + rc = ENOENT; + *error_return = rc; + return 0; +} diff --git a/amd/amfs_error.c b/amd/amfs_error.c new file mode 100644 index 000000000000..51bdaa6db14d --- /dev/null +++ b/amd/amfs_error.c @@ -0,0 +1,151 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * 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. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/amfs_error.c + * + */ + +/* + * Error file system. + * This is used as a last resort catchall if + * nothing else worked. EFS just returns lots + * of error codes, except for unmount which + * always works of course. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +static char *amfs_error_match(am_opts *fo); +static int amfs_error_mount(am_node *am, mntfs *mf); +static int amfs_error_umount(am_node *am, mntfs *mf); + + +/* + * Ops structure + */ +am_ops amfs_error_ops = +{ + "error", + amfs_error_match, + 0, /* amfs_error_init */ + amfs_error_mount, + amfs_error_umount, + amfs_error_lookup_child, + amfs_error_mount_child, + amfs_error_readdir, + 0, /* amfs_error_readlink */ + 0, /* amfs_error_mounted */ + 0, /* amfs_error_umounted */ + amfs_generic_find_srvr, + 0, /* amfs_error_get_wchan */ + FS_DISCARD, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_ERROR_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ +}; + + + +/* + * EFS file system always matches + */ +static char * +amfs_error_match(am_opts *fo) +{ + return strdup("(error-hook)"); +} + + +static int +amfs_error_mount(am_node *am, mntfs *mf) +{ + return ENOENT; +} + + +static int +amfs_error_umount(am_node *am, mntfs *mf) +{ + /* + * Always succeed + */ + return 0; +} + + +/* + * EFS interface to RPC lookup() routine. + * Should never get here in the automounter. + * If we do then just give an error. + */ +am_node * +amfs_error_lookup_child(am_node *mp, char *fname, int *error_return, int op) +{ + *error_return = ESTALE; + return 0; +} + + +/* + * EFS interface to RPC lookup() routine. + * Should never get here in the automounter. + * If we do then just give an error. + */ +am_node * +amfs_error_mount_child(am_node *ap, int *error_return) +{ + *error_return = ESTALE; + return 0; +} + + +/* + * EFS interface to RPC readdir() routine. + * Should never get here in the automounter. + * If we do then just give an error. + */ +int +amfs_error_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, u_int count) +{ + return ESTALE; +} diff --git a/amd/amfs_generic.c b/amd/amfs_generic.c new file mode 100644 index 000000000000..3e7c365a7593 --- /dev/null +++ b/amd/amfs_generic.c @@ -0,0 +1,1262 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/amfs_generic.c + * + */ + +/* + * generic functions used by amfs filesystems, ripped out of amfs_auto.c. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + + +/**************************************************************************** + *** MACROS *** + ****************************************************************************/ +#define IN_PROGRESS(cp) ((cp)->mp->am_mnt->mf_flags & MFF_MOUNTING) + + +/**************************************************************************** + *** STRUCTURES *** + ****************************************************************************/ +/* + * Mounting a file system may take a significant period of time. The + * problem is that if this is done in the main process thread then the + * entire automounter could be blocked, possibly hanging lots of processes + * on the system. Instead we use a continuation scheme to allow mounts to + * be attempted in a sub-process. When the sub-process exits we pick up the + * exit status (by convention a UN*X error number) and continue in a + * notifier. The notifier gets handed a data structure and can then + * determine whether the mount was successful or not. If not, it updates + * the data structure and tries again until there are no more ways to try + * the mount, or some other permanent error occurs. In the mean time no RPC + * reply is sent, even after the mount is successful. We rely on the RPC + * retry mechanism to resend the lookup request which can then be handled. + */ +struct continuation { + am_node *mp; /* Node we are trying to mount */ + int retry; /* Try again? */ + time_t start; /* Time we started this mount */ + int callout; /* Callout identifier */ + mntfs **mf; /* Current mntfs */ +}; + + +/**************************************************************************** + *** FORWARD DEFINITIONS *** + ****************************************************************************/ +static am_node *amfs_lookup_node(am_node *mp, char *fname, int *error_return); +static mntfs *amfs_lookup_one_mntfs(am_node *new_mp, mntfs *mf, char *ivec, + char *def_opts, char *pfname); +static mntfs **amfs_lookup_mntfs(am_node *new_mp, int *error_return); +static void amfs_cont(int rc, int term, opaque_t arg); +static void amfs_retry(int rc, int term, opaque_t arg); +static void free_continuation(struct continuation *cp); +static int amfs_bgmount(struct continuation *cp); +static char *amfs_parse_defaults(am_node *mp, mntfs *mf, char *def_opts); + + +/**************************************************************************** + *** 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 = 0; + + 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_mnt == 0 || mp->am_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_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 == 0) + ereturn(ENOSPC); + + *error_return = -1; + return new_mp; +} + + + +static mntfs * +amfs_lookup_one_mntfs(am_node *new_mp, mntfs *mf, char *ivec, + char *def_opts, char *pfname) +{ + am_ops *p; + am_opts *fs_opts; + mntfs *new_mf; + char *mp_dir = 0; +#ifdef HAVE_FS_AUTOFS + int on_autofs = 1; +#endif /* HAVE_FS_AUTOFS */ + + /* match the operators */ + fs_opts = CALLOC(am_opts); + p = ops_match(fs_opts, ivec, def_opts, new_mp->am_path, + pfname, mf->mf_info); +#ifdef HAVE_FS_AUTOFS + /* XXX: this should be factored out into an autofs-specific function */ + if (new_mp->am_flags & AMF_AUTOFS) { + /* ignore user-provided fs if we're using autofs */ + if (fs_opts->opt_sublink) { + /* + * For sublinks we need to use a hack with autofs: + * mount the filesystem on the original opt_fs (which is NOT an + * autofs mountpoint) and symlink (or lofs-mount) to it from + * the autofs mountpoint. + */ + on_autofs = 0; + mp_dir = fs_opts->opt_fs; + } else { + if (p->autofs_fs_flags & FS_ON_AUTOFS) { + mp_dir = new_mp->am_path; + } else { + mp_dir = fs_opts->opt_fs; + on_autofs = 0; + } + } + } else +#endif /* HAVE_FS_AUTOFS */ + mp_dir = fs_opts->opt_fs; + + /* + * Find or allocate a filesystem for this node. + */ + new_mf = find_mntfs(p, fs_opts, + mp_dir, + fs_opts->fs_mtab, + def_opts, + fs_opts->opt_opts, + fs_opts->opt_remopts); + + /* + * See whether this is a real filesystem + */ + p = new_mf->mf_ops; + if (p == &amfs_error_ops) { + plog(XLOG_MAP, "Map entry %s for %s did not match", ivec, new_mp->am_path); + free_mntfs(new_mf); + return NULL; + } + + dlog("Got a hit with %s", p->fs_type); + +#ifdef HAVE_FS_AUTOFS + if (new_mp->am_flags & AMF_AUTOFS && on_autofs) { + new_mf->mf_flags |= MFF_ON_AUTOFS; + new_mf->mf_fsflags = new_mf->mf_ops->autofs_fs_flags; + } + /* + * A new filesystem is an autofs filesystems if: + * 1. it claims it can be one (has the FS_AUTOFS flag) + * 2. autofs is enabled system-wide + * 3. either has an autofs parent, + * or it is explicitly requested to be autofs. + */ + if (new_mf->mf_ops->autofs_fs_flags & FS_AUTOFS && + amd_use_autofs && + ((mf->mf_flags & MFF_IS_AUTOFS) || + (new_mf->mf_fo && new_mf->mf_fo->opt_mount_type && + STREQ(new_mf->mf_fo->opt_mount_type, "autofs")))) + new_mf->mf_flags |= MFF_IS_AUTOFS; +#endif /* HAVE_FS_AUTOFS */ + + return new_mf; +} + + +static mntfs ** +amfs_lookup_mntfs(am_node *new_mp, int *error_return) +{ + am_node *mp; + char *info; /* Mount info - where to get the file system */ + char **ivecs, **cur_ivec; /* Split version of info */ + int num_ivecs; + char *orig_def_opts; /* Original Automount options */ + char *def_opts; /* Automount options */ + int error = 0; /* Error so far */ + char path_name[MAXPATHLEN]; /* General path name buffer */ + char *pfname; /* Path for database lookup */ + mntfs *mf, **mf_array; + int count; + + dlog("in amfs_lookup_mntfs"); + + mp = new_mp->am_parent; + + /* + * If we get here then this is a reference to an, + * as yet, unknown name so we need to search the mount + * map for it. + */ + if (mp->am_pref) { + if (strlen(mp->am_pref) + strlen(new_mp->am_name) >= sizeof(path_name)) + ereturn(ENAMETOOLONG); + xsnprintf(path_name, sizeof(path_name), "%s%s", mp->am_pref, new_mp->am_name); + pfname = path_name; + } else { + pfname = new_mp->am_name; + } + + mf = mp->am_mnt; + + dlog("will search map info in %s to find %s", mf->mf_info, pfname); + /* + * Consult the oracle for some mount information. + * info is malloc'ed and belongs to this routine. + * It ends up being free'd in free_continuation(). + * + * Note that this may return -1 indicating that information + * is not yet available. + */ + error = mapc_search((mnt_map *) mf->mf_private, pfname, &info); + if (error) { + if (error > 0) + plog(XLOG_MAP, "No map entry for %s", pfname); + else + plog(XLOG_MAP, "Waiting on map entry for %s", pfname); + ereturn(error); + } + dlog("mount info is %s", info); + + /* + * Split info into an argument vector. + * The vector is malloc'ed and belongs to + * this routine. It is free'd further down. + * + * Note: the vector pointers point into info, so don't free it! + */ + ivecs = strsplit(info, ' ', '\"'); + + if (mf->mf_auto) + def_opts = mf->mf_auto; + else + def_opts = ""; + + orig_def_opts = amfs_parse_defaults(mp, mf, strdup(def_opts)); + def_opts = strdup(orig_def_opts); + + /* first build our defaults */ + num_ivecs = 0; + for (cur_ivec = ivecs; *cur_ivec; cur_ivec++) { + if (**cur_ivec == '-') { + /* + * Pick up new defaults + */ + char *new_def_opts = str3cat(NULL, def_opts, ";", *cur_ivec + 1); + XFREE(def_opts); + def_opts = new_def_opts; + dlog("Setting def_opts to \"%s\"", def_opts); + continue; + } else + num_ivecs++; + } + + mf_array = calloc(num_ivecs + 1, sizeof(mntfs *)); + + /* construct the array of struct mntfs for this mount point */ + for (count = 0, cur_ivec = ivecs; *cur_ivec; cur_ivec++) { + mntfs *new_mf; + + if (**cur_ivec == '-') { + XFREE(def_opts); + if ((*cur_ivec)[1] == '\0') { + /* + * If we have a single dash '-' than we need to reset the + * default options. + */ + def_opts = strdup(orig_def_opts); + dlog("Resetting the default options, a single dash '-' was found."); + } else { + /* append options to /default options */ + def_opts = str3cat((char *) 0, orig_def_opts, ";", *cur_ivec + 1); + dlog("Resetting def_opts to \"%s\"", def_opts); + } + continue; + } + + /* + * If a mntfs has already been found, and we find + * a cut then don't try any more locations. + * + * XXX: we do not know when the "/" was added as an equivalent for "||". + * It's undocumented, it might go away at any time. Caveat emptor. + */ + if (STREQ(*cur_ivec, "/") || STREQ(*cur_ivec, "||")) { + if (count > 0) { + dlog("Cut: not trying any more locations for %s", mp->am_path); + break; + } + continue; + } + + new_mf = amfs_lookup_one_mntfs(new_mp, mf, *cur_ivec, def_opts, pfname); + if (new_mf == NULL) + continue; + mf_array[count++] = new_mf; + } + + /* We're done with ivecs */ + XFREE(ivecs); + XFREE(info); + XFREE(orig_def_opts); + XFREE(def_opts); + if (count == 0) { /* no match */ + XFREE(mf_array); + ereturn(ENOENT); + } + + return mf_array; +} + + +/* + * The continuation function. This is called by + * the task notifier when a background mount attempt + * completes. + */ +static void +amfs_cont(int rc, int term, opaque_t arg) +{ + struct continuation *cp = (struct continuation *) arg; + am_node *mp = cp->mp; + mntfs *mf = mp->am_mnt; + + dlog("amfs_cont: '%s'", mp->am_path); + + /* + * Definitely not trying to mount at the moment + */ + mf->mf_flags &= ~MFF_MOUNTING; + + /* + * While we are mounting - try to avoid race conditions + */ + new_ttl(mp); + + /* + * Wakeup anything waiting for this mount + */ + wakeup(get_mntfs_wchan(mf)); + + /* + * Check for termination signal or exit status... + */ + if (rc || term) { +#ifdef HAVE_FS_AUTOFS + if (mf->mf_flags & MFF_IS_AUTOFS && + !(mf->mf_flags & MFF_MOUNTED)) + autofs_release_fh(mp); +#endif /* HAVE_FS_AUTOFS */ + + if (term) { + /* + * Not sure what to do for an error code. + */ + mf->mf_error = EIO; /* XXX ? */ + mf->mf_flags |= MFF_ERROR; + plog(XLOG_ERROR, "mount for %s got signal %d", mp->am_path, term); + } else { + /* + * Check for exit status... + */ +#ifdef __linux__ + /* + * HACK ALERT! + * + * On Linux (and maybe not only) it's possible to run + * an amd which "knows" how to mount certain combinations + * of nfs_proto/nfs_version which the kernel doesn't grok. + * So if we got an EINVAL and we have a server that's not + * using NFSv2/UDP, try again with NFSv2/UDP. + * + * Too bad that there is no way to dynamically determine + * what combinations the _client_ supports, as opposed to + * what the _server_ supports... + */ + if (rc == EINVAL && + mf->mf_server && + (mf->mf_server->fs_version != 2 || + !STREQ(mf->mf_server->fs_proto, "udp"))) + mf->mf_flags |= MFF_NFS_SCALEDOWN; + else +#endif /* __linux__ */ + { + mf->mf_error = rc; + mf->mf_flags |= MFF_ERROR; + errno = rc; /* XXX */ + if (!STREQ(mp->am_mnt->mf_ops->fs_type, "linkx")) + plog(XLOG_ERROR, "%s: mount (amfs_cont): %m", mp->am_path); + } + } + + if (!(mf->mf_flags & MFF_NFS_SCALEDOWN)) { + /* + * If we get here then that attempt didn't work, so + * move the info vector pointer along by one and + * call the background mount routine again + */ + amd_stats.d_merr++; + cp->mf++; + } + amfs_bgmount(cp); + if (mp->am_error > 0) + assign_error_mntfs(mp); + } else { + /* + * The mount worked. + */ + dlog("Mounting %s returned success", cp->mp->am_path); + am_mounted(cp->mp); + free_continuation(cp); + } + + reschedule_timeout_mp(); +} + + +/* + * Retry a mount + */ +static void +amfs_retry(int rc, int term, opaque_t arg) +{ + struct continuation *cp = (struct continuation *) arg; + am_node *mp = cp->mp; + int error = 0; + + dlog("Commencing retry for mount of %s", mp->am_path); + + new_ttl(mp); + + if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime(NULL)) { + /* + * The entire mount has timed out. Set the error code and skip past all + * the mntfs's so that amfs_bgmount will not have any more + * ways to try the mount, thus causing an error. + */ + plog(XLOG_INFO, "mount of \"%s\" has timed out", mp->am_path); + error = ETIMEDOUT; + while (*cp->mf) + cp->mf++; + /* explicitly forbid further retries after timeout */ + cp->retry = FALSE; + } + if (error || !IN_PROGRESS(cp)) + error = amfs_bgmount(cp); + + reschedule_timeout_mp(); +} + + +/* + * Discard an old continuation + */ +static void +free_continuation(struct continuation *cp) +{ + mntfs **mfp; + + dlog("free_continuation"); + if (cp->callout) + untimeout(cp->callout); + /* + * we must free the mntfs's in the list. + * so free all of them if there was an error, + * or free all but the used one, if the mount succeeded. + */ + for (mfp = cp->mp->am_mfarray; *mfp; mfp++) { + free_mntfs(*mfp); + } + XFREE(cp->mp->am_mfarray); + cp->mp->am_mfarray = 0; + XFREE(cp); +} + + +/* + * Pick a file system to try mounting and + * do that in the background if necessary + * +For each location: + discard previous mount location if required + fetch next mount location + if the filesystem failed to be mounted then + this_error = error from filesystem + goto failed + if the filesystem is mounting or unmounting then + goto retry; + if the fileserver is down then + this_error = EIO + continue; + if the filesystem is already mounted + break + fi + + this_error = initialize mount point + + if no error on this mount and mount is delayed then + this_error = -1 + fi + if this_error < 0 then + retry = true + fi + if no error on this mount then + if mount in background then + run mount in background + return -1 + else + this_error = mount in foreground + fi + fi + if an error occurred on this mount then + update stats + save error in mount point + fi +endfor + */ +static int +amfs_bgmount(struct continuation *cp) +{ + am_node *mp = cp->mp; + mntfs *mf; /* Current mntfs */ + int this_error = -1; /* Per-mount error */ + int hard_error = -1; /* Cumulative per-node error */ + + if (mp->am_mnt) + free_mntfs(mp->am_mnt); + + /* + * Try to mount each location. + * At the end: + * hard_error == 0 indicates something was mounted. + * hard_error > 0 indicates everything failed with a hard error + * hard_error < 0 indicates nothing could be mounted now + */ + for (mp->am_mnt = *cp->mf; *cp->mf; cp->mf++, mp->am_mnt = *cp->mf) { + am_ops *p; + + mf = dup_mntfs(mp->am_mnt); + p = mf->mf_ops; + + if (hard_error < 0) + hard_error = this_error; + this_error = 0; + + if (mf->mf_error > 0) { + this_error = mf->mf_error; + goto failed; + } + + if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) { + /* + * Still mounting - retry later + */ + dlog("mount of \"%s\" already pending", mf->mf_info); + goto retry; + } + + if (FSRV_ISDOWN(mf->mf_server)) { + /* + * Would just mount from the same place + * as a hung mount - so give up + */ + dlog("%s is already hung - giving up", mf->mf_server->fs_host); + this_error = EIO; + goto failed; + } + + if (mp->am_link) { + XFREE(mp->am_link); + mp->am_link = NULL; + } + if (mf->mf_fo && mf->mf_fo->opt_sublink) + mp->am_link = strdup(mf->mf_fo->opt_sublink); + + /* + * Will usually need to play around with the mount nodes + * file attribute structure. This must be done here. + * Try and get things initialized, even if the fileserver + * is not known to be up. In the common case this will + * progress things faster. + */ + + /* + * Fill in attribute fields. + */ + if (mf->mf_fsflags & FS_DIRECTORY) + mk_fattr(&mp->am_fattr, NFDIR); + else + mk_fattr(&mp->am_fattr, NFLNK); + + if (mf->mf_flags & MFF_MOUNTED) { + dlog("duplicate mount of \"%s\" ...", mf->mf_info); + /* + * Skip initial processing of the mountpoint if already mounted. + * This could happen if we have multiple sublinks into the same f/s, + * or if we are restarting an already-mounted filesystem. + */ + goto already_mounted; + } + + if (mf->mf_fo && mf->mf_fo->fs_mtab) { + plog(XLOG_MAP, "Trying mount of %s on %s fstype %s mount_type %s", + mf->mf_fo->fs_mtab, mf->mf_mount, p->fs_type, + mp->am_flags & AMF_AUTOFS ? "autofs" : "non-autofs"); + } + + if (p->fs_init && !(mf->mf_flags & MFF_RESTART)) + this_error = p->fs_init(mf); + + if (this_error > 0) + goto failed; + if (this_error < 0) + goto retry; + + if (mf->mf_fo && mf->mf_fo->opt_delay) { + /* + * If there is a delay timer on the mount + * then don't try to mount if the timer + * has not expired. + */ + int i = atoi(mf->mf_fo->opt_delay); + time_t now = clocktime(NULL); + if (i > 0 && now < (cp->start + i)) { + dlog("Mount of %s delayed by %lds", mf->mf_mount, (long) (i - now + cp->start)); + goto retry; + } + } + + /* + * If the directory is not yet made and it needs to be made, then make it! + */ + if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_fsflags & FS_MKMNT) { + plog(XLOG_INFO, "creating mountpoint directory '%s'", mf->mf_mount); + this_error = mkdirs(mf->mf_mount, 0555); + if (this_error) { + plog(XLOG_ERROR, "mkdirs failed: %s", strerror(this_error)); + goto failed; + } + mf->mf_flags |= MFF_MKMNT; + } + +#ifdef HAVE_FS_AUTOFS + if (mf->mf_flags & MFF_IS_AUTOFS) + if ((this_error = autofs_get_fh(mp))) + goto failed; +#endif /* HAVE_FS_AUTOFS */ + + already_mounted: + mf->mf_flags |= MFF_MOUNTING; + if (mf->mf_fsflags & FS_MBACKGROUND) { + dlog("backgrounding mount of \"%s\"", mf->mf_mount); + if (cp->callout) { + untimeout(cp->callout); + cp->callout = 0; + } + + /* actually run the task, backgrounding as necessary */ + run_task(mount_node, (opaque_t) mp, amfs_cont, (opaque_t) cp); + return -1; + } else { + dlog("foreground mount of \"%s\" ...", mf->mf_mount); + this_error = mount_node((opaque_t) mp); + } + + mf->mf_flags &= ~MFF_MOUNTING; + if (this_error > 0) + goto failed; + if (this_error == 0) { + am_mounted(mp); + break; /* Success */ + } + + retry: + if (!cp->retry) + continue; + dlog("will retry ...\n"); + + /* + * Arrange that amfs_bgmount is called + * after anything else happens. + */ + dlog("Arranging to retry mount of %s", mp->am_path); + sched_task(amfs_retry, (opaque_t) cp, get_mntfs_wchan(mf)); + if (cp->callout) + untimeout(cp->callout); + cp->callout = timeout(RETRY_INTERVAL, wakeup, + (opaque_t) get_mntfs_wchan(mf)); + + mp->am_ttl = clocktime(NULL) + RETRY_INTERVAL; + + /* + * Not done yet - so don't return anything + */ + return -1; + + failed: + amd_stats.d_merr++; + mf->mf_error = this_error; + mf->mf_flags |= MFF_ERROR; +#ifdef HAVE_FS_AUTOFS + if (mp->am_autofs_fh) + autofs_release_fh(mp); +#endif /* HAVE_FS_AUTOFS */ + if (mf->mf_flags & MFF_MKMNT) { + rmdirs(mf->mf_mount); + mf->mf_flags &= ~MFF_MKMNT; + } + /* + * Wakeup anything waiting for this mount + */ + wakeup(get_mntfs_wchan(mf)); + free_mntfs(mf); + /* continue */ + } + + /* + * If we get here, then either the mount succeeded or + * there is no more mount information available. + */ + if (this_error) { + mp->am_mnt = mf = new_mntfs(); + +#ifdef HAVE_FS_AUTOFS + if (mp->am_flags & AMF_AUTOFS) + autofs_mount_failed(mp); + else +#endif /* HAVE_FS_AUTOFS */ + nfs_quick_reply(mp, this_error); + + if (hard_error <= 0) + hard_error = this_error; + if (hard_error < 0) + hard_error = ETIMEDOUT; + + /* + * Set a small(ish) timeout on an error node if + * the error was not a time out. + */ + switch (hard_error) { + case ETIMEDOUT: + case EWOULDBLOCK: + case EIO: + mp->am_timeo = 17; + break; + default: + mp->am_timeo = 5; + break; + } + new_ttl(mp); + } else { + mf = mp->am_mnt; + /* + * Wakeup anything waiting for this mount + */ + wakeup(get_mntfs_wchan(mf)); + hard_error = 0; + } + + /* + * Make sure that the error value in the mntfs has a + * reasonable value. + */ + if (mf->mf_error < 0) { + mf->mf_error = hard_error; + if (hard_error) + mf->mf_flags |= MFF_ERROR; + } + + /* + * In any case we don't need the continuation any more + */ + free_continuation(cp); + + return hard_error; +} + + +static char * +amfs_parse_defaults(am_node *mp, mntfs *mf, char *def_opts) +{ + char *dflts; + char *dfl; + char **rvec = NULL; + struct mnt_map *mm = (mnt_map *) mf->mf_private; + + dlog("determining /defaults entry value"); + + /* + * Find out if amd.conf overrode any map-specific /defaults. + * + * HACK ALERT: there's no easy way to find out what the map mount point is + * at this point, so I am forced to initialize the mnt_map->cfm field here + * for the first time, upon the very first search for a /defaults entry in + * this map. This initialization is much better done in mapc_create(), + * but it's impossible to do that there with the current code structure. + */ + if (mm->cfm == NULL) { /* then initialize it for first time */ + mm->cfm = find_cf_map(mf->mf_mount); + } + if (mm->cfm && mm->cfm->cfm_defaults) { + dlog("map %s map_defaults override: %s", mf->mf_mount, mm->cfm->cfm_defaults); + dflts = strdup(mm->cfm->cfm_defaults); + } else if (mapc_search(mm, "/defaults", &dflts) == 0) { + dlog("/defaults gave %s", dflts); + } else { + return def_opts; /* if nothing found */ + } + + /* trim leading '-' in case thee's one */ + if (*dflts == '-') + dfl = dflts + 1; + else + dfl = dflts; + + /* + * Chop the defaults up + */ + rvec = strsplit(dfl, ' ', '\"'); + + if (gopt.flags & CFM_SELECTORS_IN_DEFAULTS) { + /* + * Pick whichever first entry matched the list of selectors. + * Strip the selectors from the string, and assign to dfl the + * rest of the string. + */ + if (rvec) { + am_opts ap; + am_ops *pt; + char **sp = rvec; + while (*sp) { /* loop until you find something, if any */ + memset((char *) &ap, 0, sizeof(am_opts)); + /* + * This next routine cause many spurious "expansion of ... is" + * messages, which are ignored, b/c all we need out of this + * routine is to match selectors. These spurious messages may + * be wrong, esp. if they try to expand ${key} b/c it will + * get expanded to "/defaults" + */ + pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults", + mp->am_parent->am_mnt->mf_info); + free_opts(&ap); /* don't leak */ + if (pt == &amfs_error_ops) { + plog(XLOG_MAP, "did not match defaults for \"%s\"", *sp); + } else { + dfl = strip_selectors(*sp, "/defaults"); + plog(XLOG_MAP, "matched default selectors \"%s\"", dfl); + break; + } + ++sp; + } + } + } else { /* not selectors_in_defaults */ + /* + * Extract first value + */ + dfl = rvec[0]; + } + + /* + * If there were any values at all... + */ + if (dfl) { + /* + * Log error if there were other values + */ + if (!(gopt.flags & CFM_SELECTORS_IN_DEFAULTS) && rvec[1]) { + dlog("/defaults chopped into %s", dfl); + plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info); + } + + /* + * Prepend to existing defaults if they exist, + * otherwise just use these defaults. + */ + if (*def_opts && *dfl) { + size_t l = strlen(def_opts) + strlen(dfl) + 2; + char *nopts = (char *) xmalloc(l); + xsnprintf(nopts, l, "%s;%s", dfl, def_opts); + XFREE(def_opts); + def_opts = nopts; + } else if (*dfl) { + def_opts = strealloc(def_opts, dfl); + } + } + + XFREE(dflts); + + /* don't need info vector any more */ + if (rvec) + XFREE(rvec); + + return def_opts; +} + + +am_node * +amfs_generic_mount_child(am_node *new_mp, int *error_return) +{ + int error; + struct continuation *cp; /* Continuation structure if need to mount */ + + dlog("in amfs_generic_mount_child"); + + *error_return = error = 0; /* Error so far */ + + /* we have an errorfs attached to the am_node, free it */ + free_mntfs(new_mp->am_mnt); + new_mp->am_mnt = 0; + + /* + * Construct a continuation + */ + cp = ALLOC(struct continuation); + cp->callout = 0; + cp->mp = new_mp; + cp->retry = TRUE; + cp->start = clocktime(NULL); + cp->mf = new_mp->am_mfarray; + + /* + * Try and mount the file system. If this succeeds immediately (possible + * for a ufs file system) then return the attributes, otherwise just + * return an error. + */ + error = amfs_bgmount(cp); + reschedule_timeout_mp(); + if (!error) + return new_mp; + + /* + * Code for quick reply. If current_transp is set, then it's the + * transp that's been passed down from nfs_program_2() or from + * autofs_program_[123](). + * If new_mp->am_transp is not already set, set it by copying in + * current_transp. Once am_transp is set, nfs_quick_reply() and + * autofs_mount_succeeded() can use it to send a reply to the + * client that requested this mount. + */ + if (current_transp && !new_mp->am_transp) { + dlog("Saving RPC transport for %s", new_mp->am_path); + new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT)); + *(new_mp->am_transp) = *current_transp; + } + if (error && (new_mp->am_mnt->mf_ops == &amfs_error_ops)) + new_mp->am_error = error; + + if (new_mp->am_error > 0) + assign_error_mntfs(new_mp); + + ereturn(error); +} + + +/* + * Automount interface to RPC lookup routine + * Find the corresponding entry and return + * the file handle for it. + */ +am_node * +amfs_generic_lookup_child(am_node *mp, char *fname, int *error_return, int op) +{ + am_node *new_mp; + mntfs **mf_array; + int mp_error; + + dlog("in amfs_generic_lookup_child"); + + *error_return = 0; + new_mp = amfs_lookup_node(mp, fname, error_return); + + /* return if we got an error */ + if (!new_mp || *error_return > 0) + return new_mp; + + /* also return if it's already mounted and known to be up */ + if (*error_return == 0 && FSRV_ISUP(new_mp->am_mnt->mf_server)) + return new_mp; + + switch (op) { + case VLOOK_DELETE: + /* + * If doing a delete then don't create again! + */ + ereturn(ENOENT); + case VLOOK_LOOKUP: + return new_mp; + } + + /* save error_return */ + mp_error = *error_return; + + mf_array = amfs_lookup_mntfs(new_mp, error_return); + if (!mf_array) { + new_mp->am_error = new_mp->am_mnt->mf_error = *error_return; + free_map(new_mp); + return NULL; + } + + /* + * Already mounted but known to be down: + * check if we have any alternatives to mount + */ + if (mp_error == 0) { + mntfs **mfp; + for (mfp = mf_array; *mfp; mfp++) + if (*mfp != new_mp->am_mnt) + break; + if (*mfp != NULL) { + /* + * we found an alternative, so try mounting again. + */ + *error_return = -1; + } else { + for (mfp = mf_array; *mfp; mfp++) + free_mntfs(*mfp); + XFREE(mf_array); + if (new_mp->am_flags & AMF_SOFTLOOKUP) { + ereturn(EIO); + } else { + *error_return = 0; + return new_mp; + } + } + } + + /* store the array inside the am_node */ + new_mp->am_mfarray = mf_array; + + /* + * Note: while it might seem like a good idea to prioritize + * the list of mntfs's we got here, it probably isn't. + * It would ignore the ordering of entries specified by the user, + * which is counterintuitive and confusing. + */ + return new_mp; +} + + +void +amfs_generic_mounted(mntfs *mf) +{ + amfs_mkcacheref(mf); +} + + +/* + * Unmount an automount sub-node + */ +int +amfs_generic_umount(am_node *mp, mntfs *mf) +{ + int error = 0; + +#ifdef HAVE_FS_AUTOFS + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + if (mf->mf_flags & MFF_IS_AUTOFS) + error = UMOUNT_FS(mp->am_path, mnttab_file_name, unmount_flags); +#endif /* HAVE_FS_AUTOFS */ + + return error; +} + + +char * +amfs_generic_match(am_opts *fo) +{ + char *p; + + if (!fo->opt_rfs) { + plog(XLOG_USER, "amfs_generic_match: no mount point named (rfs:=)"); + return 0; + } + if (!fo->opt_fs) { + plog(XLOG_USER, "amfs_generic_match: no map named (fs:=)"); + return 0; + } + + /* + * Swap round fs:= and rfs:= options + * ... historical (jsp) + */ + p = fo->opt_rfs; + fo->opt_rfs = fo->opt_fs; + fo->opt_fs = p; + + /* + * mtab entry turns out to be the name of the mount map + */ + return strdup(fo->opt_rfs ? fo->opt_rfs : "."); +} diff --git a/amd/amfs_host.c b/amd/amfs_host.c new file mode 100644 index 000000000000..55423c46ea33 --- /dev/null +++ b/amd/amfs_host.c @@ -0,0 +1,709 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/amfs_host.c + * + */ + +/* + * NFS host file system. + * Mounts all exported filesystems from a given host. + * This has now degenerated into a mess but will not + * be rewritten. Amd 6 will support the abstractions + * needed to make this work correctly. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +static char *amfs_host_match(am_opts *fo); +static int amfs_host_init(mntfs *mf); +static int amfs_host_mount(am_node *am, mntfs *mf); +static int amfs_host_umount(am_node *am, mntfs *mf); +static void amfs_host_umounted(mntfs *mf); + +/* + * Ops structure + */ +am_ops amfs_host_ops = +{ + "host", + amfs_host_match, + amfs_host_init, + amfs_host_mount, + amfs_host_umount, + amfs_error_lookup_child, + amfs_error_mount_child, + amfs_error_readdir, + 0, /* amfs_host_readlink */ + 0, /* amfs_host_mounted */ + amfs_host_umounted, + find_nfs_srvr, + 0, /* amfs_host_get_wchan */ + FS_MKMNT | FS_BACKGROUND | FS_AMQINFO, +#ifdef HAVE_FS_AUTOFS + AUTOFS_HOST_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ +}; + + +/* + * Determine the mount point: + * + * The next change we put in to better handle PCs. This is a bit + * disgusting, so you'd better sit down. We change the make_mntpt function + * to look for exported file systems without a leading '/'. If they don't + * have a leading '/', we add one. If the export is 'a:' through 'z:' + * (without a leading slash), we change it to 'a%' (or b% or z%). This + * allows the entire PC disk to be mounted. + */ +static void +make_mntpt(char *mntpt, size_t l, const exports ex, const char *mf_mount) +{ + if (ex->ex_dir[0] == '/') { + if (ex->ex_dir[1] == 0) + xstrlcpy(mntpt, mf_mount, l); + else + xsnprintf(mntpt, l, "%s%s", mf_mount, ex->ex_dir); + } else if (ex->ex_dir[0] >= 'a' && + ex->ex_dir[0] <= 'z' && + ex->ex_dir[1] == ':' && + ex->ex_dir[2] == '/' && + ex->ex_dir[3] == 0) + xsnprintf(mntpt, l, "%s/%c%%", mf_mount, ex->ex_dir[0]); + else + xsnprintf(mntpt, l, "%s/%s", mf_mount, ex->ex_dir); +} + + +/* + * Execute needs the same as NFS plus a helper command + */ +static char * +amfs_host_match(am_opts *fo) +{ + extern am_ops nfs_ops; + + /* + * Make sure rfs is specified to keep nfs_match happy... + */ + if (!fo->opt_rfs) + fo->opt_rfs = "/"; + + return (*nfs_ops.fs_match) (fo); +} + + +static int +amfs_host_init(mntfs *mf) +{ + u_short mountd_port; + + if (strchr(mf->mf_info, ':') == 0) + return ENOENT; + + /* + * This is primarily to schedule a wakeup so that as soon + * as our fileserver is ready, we can continue setting up + * the host filesystem. If we don't do this, the standard + * amfs_auto code will set up a fileserver structure, but it will + * have to wait for another nfs request from the client to come + * in before finishing. Our way is faster since we don't have + * to wait for the client to resend its request (which could + * take a second or two). + */ + /* + * First, we find the fileserver for this mntfs and then call + * get_mountd_port with our mntfs passed as the wait channel. + * get_mountd_port will check some things and then schedule + * it so that when the fileserver is ready, a wakeup is done + * on this mntfs. amfs_cont() is already sleeping on this mntfs + * so as soon as that wakeup happens amfs_cont() is called and + * this mount is retried. + */ + if (mf->mf_server) + /* + * We don't really care if there's an error returned. + * Since this is just to help speed things along, the + * error will get handled properly elsewhere. + */ + get_mountd_port(mf->mf_server, &mountd_port, get_mntfs_wchan(mf)); + + return 0; +} + + +static int +do_mount(am_nfs_handle_t *fhp, char *mntdir, char *fs_name, mntfs *mf) +{ + struct stat stb; + + dlog("amfs_host: mounting fs %s on %s\n", fs_name, mntdir); + + (void) mkdirs(mntdir, 0555); + if (stat(mntdir, &stb) < 0 || (stb.st_mode & S_IFMT) != S_IFDIR) { + plog(XLOG_ERROR, "No mount point for %s - skipping", mntdir); + return ENOENT; + } + + return mount_nfs_fh(fhp, mntdir, fs_name, mf); +} + + +static int +sortfun(const voidp x, const voidp y) +{ + exports *a = (exports *) x; + exports *b = (exports *) y; + + return strcmp((*a)->ex_dir, (*b)->ex_dir); +} + + +/* + * Get filehandle + */ +static int +fetch_fhandle(CLIENT *client, char *dir, am_nfs_handle_t *fhp, u_long nfs_version) +{ + struct timeval tv; + enum clnt_stat clnt_stat; + struct fhstatus res; +#ifdef HAVE_FS_NFS3 + struct am_mountres3 res3; +#endif /* HAVE_FS_NFS3 */ + + /* + * Pick a number, any number... + */ + tv.tv_sec = 20; + tv.tv_usec = 0; + + dlog("Fetching fhandle for %s", dir); + + /* + * Call the mount daemon on the remote host to + * get the filehandle. Use NFS version specific call. + */ + + plog(XLOG_INFO, "fetch_fhandle: NFS version %d", (int) nfs_version); +#ifdef HAVE_FS_NFS3 + if (nfs_version == NFS_VERSION3) { + memset((char *) &res3, 0, sizeof(res3)); + clnt_stat = clnt_call(client, + MOUNTPROC_MNT, + (XDRPROC_T_TYPE) xdr_dirpath, + (SVC_IN_ARG_TYPE) &dir, + (XDRPROC_T_TYPE) xdr_am_mountres3, + (SVC_IN_ARG_TYPE) &res3, + tv); + if (clnt_stat != RPC_SUCCESS) { + plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat)); + return EIO; + } + /* Check the status of the filehandle */ + if ((errno = res3.fhs_status)) { + dlog("fhandle fetch for mount version 3 failed: %m"); + return errno; + } + memset((voidp) &fhp->v3, 0, sizeof(am_nfs_fh3)); + fhp->v3.am_fh3_length = res3.mountres3_u.mountinfo.fhandle.fhandle3_len; + memmove(fhp->v3.am_fh3_data, + res3.mountres3_u.mountinfo.fhandle.fhandle3_val, + fhp->v3.am_fh3_length); + } else { /* not NFS_VERSION3 mount */ +#endif /* HAVE_FS_NFS3 */ + clnt_stat = clnt_call(client, + MOUNTPROC_MNT, + (XDRPROC_T_TYPE) xdr_dirpath, + (SVC_IN_ARG_TYPE) &dir, + (XDRPROC_T_TYPE) xdr_fhstatus, + (SVC_IN_ARG_TYPE) &res, + tv); + if (clnt_stat != RPC_SUCCESS) { + plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat)); + return EIO; + } + /* Check status of filehandle */ + if (res.fhs_status) { + errno = res.fhs_status; + dlog("fhandle fetch for mount version 1 failed: %m"); + return errno; + } + memmove(&fhp->v2, &res.fhs_fh, NFS_FHSIZE); +#ifdef HAVE_FS_NFS3 + } /* end of "if (nfs_version == NFS_VERSION3)" statement */ +#endif /* HAVE_FS_NFS3 */ + + /* all is well */ + return 0; +} + + +/* + * Scan mount table to see if something already mounted + */ +static int +already_mounted(mntlist *mlist, char *dir) +{ + mntlist *ml; + + for (ml = mlist; ml; ml = ml->mnext) + if (STREQ(ml->mnt->mnt_dir, dir)) + return 1; + return 0; +} + + +static int +amfs_host_mount(am_node *am, mntfs *mf) +{ + struct timeval tv2; + CLIENT *client; + enum clnt_stat clnt_stat; + int n_export; + int j, k; + exports exlist = 0, ex; + exports *ep = 0; + am_nfs_handle_t *fp = 0; + char *host; + int error = 0; + struct sockaddr_in sin; + int sock = RPC_ANYSOCK; + int ok = FALSE; + mntlist *mlist; + char fs_name[MAXPATHLEN], *rfs_dir; + char mntpt[MAXPATHLEN]; + struct timeval tv; + u_long mnt_version; + + /* + * WebNFS servers don't necessarily run mountd. + */ + if (mf->mf_flags & MFF_WEBNFS) { + plog(XLOG_ERROR, "amfs_host_mount: cannot support WebNFS"); + return EIO; + } + + /* + * Read the mount list + */ + mlist = read_mtab(mf->mf_mount, mnttab_file_name); + +#ifdef MOUNT_TABLE_ON_FILE + /* + * Unlock the mount list + */ + unlock_mntlist(); +#endif /* MOUNT_TABLE_ON_FILE */ + + /* + * Take a copy of the server hostname, address, and nfs version + * to mount version conversion. + */ + host = mf->mf_server->fs_host; + sin = *mf->mf_server->fs_ip; + plog(XLOG_INFO, "amfs_host_mount: NFS version %d", (int) mf->mf_server->fs_version); +#ifdef HAVE_FS_NFS3 + if (mf->mf_server->fs_version == NFS_VERSION3) + mnt_version = AM_MOUNTVERS3; + else +#endif /* HAVE_FS_NFS3 */ + mnt_version = MOUNTVERS; + + /* + * The original 10 second per try timeout is WAY too large, especially + * if we're only waiting 10 or 20 seconds max for the response. + * That would mean we'd try only once in 10 seconds, and we could + * lose the transmit or receive packet, and never try again. + * A 2-second per try timeout here is much more reasonable. + * 09/28/92 Mike Mitchell, mcm@unx.sas.com + */ + tv.tv_sec = 2; + tv.tv_usec = 0; + + /* + * Create a client attached to mountd + */ + client = get_mount_client(host, &sin, &tv, &sock, mnt_version); + if (client == NULL) { +#ifdef HAVE_CLNT_SPCREATEERROR + plog(XLOG_ERROR, "get_mount_client failed for %s: %s", + host, clnt_spcreateerror("")); +#else /* not HAVE_CLNT_SPCREATEERROR */ + plog(XLOG_ERROR, "get_mount_client failed for %s", host); +#endif /* not HAVE_CLNT_SPCREATEERROR */ + error = EIO; + goto out; + } + if (!nfs_auth) { + error = make_nfs_auth(); + if (error) + goto out; + } + client->cl_auth = nfs_auth; + + dlog("Fetching export list from %s", host); + + /* + * Fetch the export list + */ + tv2.tv_sec = 10; + tv2.tv_usec = 0; + clnt_stat = clnt_call(client, + MOUNTPROC_EXPORT, + (XDRPROC_T_TYPE) xdr_void, + 0, + (XDRPROC_T_TYPE) xdr_exports, + (SVC_IN_ARG_TYPE) & exlist, + tv2); + if (clnt_stat != RPC_SUCCESS) { + const char *msg = clnt_sperrno(clnt_stat); + plog(XLOG_ERROR, "host_mount rpc failed: %s", msg); + /* clnt_perror(client, "rpc"); */ + error = EIO; + goto out; + } + + /* + * Figure out how many exports were returned + */ + for (n_export = 0, ex = exlist; ex; ex = ex->ex_next) { + n_export++; + } + + /* + * Allocate an array of pointers into the list + * so that they can be sorted. If the filesystem + * is already mounted then ignore it. + */ + ep = (exports *) xmalloc(n_export * sizeof(exports)); + for (j = 0, ex = exlist; ex; ex = ex->ex_next) { + make_mntpt(mntpt, sizeof(mntpt), ex, mf->mf_mount); + if (already_mounted(mlist, mntpt)) + /* we have at least one mounted f/s, so don't fail the mount */ + ok = TRUE; + else + ep[j++] = ex; + } + n_export = j; + + /* + * Sort into order. + * This way the mounts are done in order down the tree, + * instead of any random order returned by the mount + * daemon (the protocol doesn't specify...). + */ + qsort(ep, n_export, sizeof(exports), sortfun); + + /* + * Allocate an array of filehandles + */ + fp = (am_nfs_handle_t *) xmalloc(n_export * sizeof(am_nfs_handle_t)); + + /* + * Try to obtain filehandles for each directory. + * If a fetch fails then just zero out the array + * reference but discard the error. + */ + for (j = k = 0; j < n_export; j++) { + /* Check and avoid a duplicated export entry */ + if (j > k && ep[k] && STREQ(ep[j]->ex_dir, ep[k]->ex_dir)) { + dlog("avoiding dup fhandle requested for %s", ep[j]->ex_dir); + ep[j] = 0; + } else { + k = j; + error = fetch_fhandle(client, ep[j]->ex_dir, &fp[j], + mf->mf_server->fs_version); + if (error) + ep[j] = 0; + } + } + + /* + * Mount each filesystem for which we have a filehandle. + * If any of the mounts succeed then mark "ok" and return + * error code 0 at the end. If they all fail then return + * the last error code. + */ + xstrlcpy(fs_name, mf->mf_info, MAXPATHLEN); + if ((rfs_dir = strchr(fs_name, ':')) == (char *) 0) { + plog(XLOG_FATAL, "amfs_host_mount: mf_info has no colon"); + error = EINVAL; + goto out; + } + ++rfs_dir; + for (j = 0; j < n_export; j++) { + ex = ep[j]; + if (ex) { + /* + * Note: the sizeof space left in rfs_dir is what's left in fs_name + * after strchr() above returned a pointer _inside_ fs_name. The + * calculation below also takes into account that rfs_dir was + * incremented by the ++ above. + */ + xstrlcpy(rfs_dir, ex->ex_dir, sizeof(fs_name) - (rfs_dir - fs_name)); + make_mntpt(mntpt, sizeof(mntpt), ex, mf->mf_mount); + if (do_mount(&fp[j], mntpt, fs_name, mf) == 0) + ok = TRUE; + } + } + + /* + * Clean up and exit + */ +out: + discard_mntlist(mlist); + if (ep) + XFREE(ep); + if (fp) + XFREE(fp); + if (sock != RPC_ANYSOCK) + (void) amu_close(sock); + if (client) + clnt_destroy(client); + if (exlist) + xdr_pri_free((XDRPROC_T_TYPE) xdr_exports, (caddr_t) &exlist); + if (ok) + return 0; + return error; +} + + +/* + * Return true if pref is a directory prefix of dir. + * + * XXX TODO: + * Does not work if pref is "/". + */ +static int +directory_prefix(char *pref, char *dir) +{ + int len = strlen(pref); + + if (!NSTREQ(pref, dir, len)) + return FALSE; + if (dir[len] == '/' || dir[len] == '\0') + return TRUE; + return FALSE; +} + + +/* + * Unmount a mount tree + */ +static int +amfs_host_umount(am_node *am, mntfs *mf) +{ + mntlist *ml, *mprev; + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + int xerror = 0; + + /* + * Read the mount list + */ + mntlist *mlist = read_mtab(mf->mf_mount, mnttab_file_name); + +#ifdef MOUNT_TABLE_ON_FILE + /* + * Unlock the mount list + */ + unlock_mntlist(); +#endif /* MOUNT_TABLE_ON_FILE */ + + /* + * Reverse list... + */ + ml = mlist; + mprev = 0; + while (ml) { + mntlist *ml2 = ml->mnext; + ml->mnext = mprev; + mprev = ml; + ml = ml2; + } + mlist = mprev; + + /* + * Unmount all filesystems... + */ + for (ml = mlist; ml && !xerror; ml = ml->mnext) { + char *dir = ml->mnt->mnt_dir; + if (directory_prefix(mf->mf_mount, dir)) { + int error; + dlog("amfs_host: unmounts %s", dir); + /* + * Unmount "dir" + */ + error = UMOUNT_FS(dir, mnttab_file_name, unmount_flags); + /* + * Keep track of errors + */ + if (error) { + /* + * If we have not already set xerror and error is not ENOENT, + * then set xerror equal to error and log it. + * 'xerror' is the return value for this function. + * + * We do not want to pass ENOENT as an error because if the + * directory does not exists our work is done anyway. + */ + if (!xerror && error != ENOENT) + xerror = error; + if (error != EBUSY) { + errno = error; + plog(XLOG_ERROR, "Tree unmount of %s failed: %m", ml->mnt->mnt_dir); + } + } else { + (void) rmdirs(dir); + } + } + } + + /* + * Throw away mount list + */ + discard_mntlist(mlist); + + /* + * Try to remount, except when we are shutting down. + */ + if (xerror && amd_state != Finishing) { + xerror = amfs_host_mount(am, mf); + if (!xerror) { + /* + * Don't log this - it's usually too verbose + plog(XLOG_INFO, "Remounted host %s", mf->mf_info); + */ + xerror = EBUSY; + } + } + return xerror; +} + + +/* + * Tell mountd we're done. + * This is not quite right, because we may still + * have other filesystems mounted, but the existing + * mountd protocol is badly broken anyway. + */ +static void +amfs_host_umounted(mntfs *mf) +{ + char *host; + CLIENT *client; + enum clnt_stat clnt_stat; + struct sockaddr_in sin; + int sock = RPC_ANYSOCK; + struct timeval tv; + u_long mnt_version; + + if (mf->mf_error || mf->mf_refc > 1 || !mf->mf_server) + return; + + /* + * WebNFS servers shouldn't ever get here. + */ + if (mf->mf_flags & MFF_WEBNFS) { + plog(XLOG_ERROR, "amfs_host_umounted: cannot support WebNFS"); + return; + } + + /* + * Take a copy of the server hostname, address, and NFS version + * to mount version conversion. + */ + host = mf->mf_server->fs_host; + sin = *mf->mf_server->fs_ip; + plog(XLOG_INFO, "amfs_host_umounted: NFS version %d", (int) mf->mf_server->fs_version); +#ifdef HAVE_FS_NFS3 + if (mf->mf_server->fs_version == NFS_VERSION3) + mnt_version = AM_MOUNTVERS3; + else +#endif /* HAVE_FS_NFS3 */ + mnt_version = MOUNTVERS; + + /* + * Create a client attached to mountd + */ + tv.tv_sec = 10; + tv.tv_usec = 0; + client = get_mount_client(host, &sin, &tv, &sock, mnt_version); + if (client == NULL) { +#ifdef HAVE_CLNT_SPCREATEERROR + plog(XLOG_ERROR, "get_mount_client failed for %s: %s", + host, clnt_spcreateerror("")); +#else /* not HAVE_CLNT_SPCREATEERROR */ + plog(XLOG_ERROR, "get_mount_client failed for %s", host); +#endif /* not HAVE_CLNT_SPCREATEERROR */ + goto out; + } + + if (!nfs_auth) { + if (make_nfs_auth()) + goto out; + } + client->cl_auth = nfs_auth; + + dlog("Unmounting all from %s", host); + + clnt_stat = clnt_call(client, + MOUNTPROC_UMNTALL, + (XDRPROC_T_TYPE) xdr_void, + 0, + (XDRPROC_T_TYPE) xdr_void, + 0, + tv); + if (clnt_stat != RPC_SUCCESS && clnt_stat != RPC_SYSTEMERROR) { + /* RPC_SYSTEMERROR seems to be returned for no good reason ... */ + const char *msg = clnt_sperrno(clnt_stat); + plog(XLOG_ERROR, "unmount all from %s rpc failed: %s", host, msg); + goto out; + } + +out: + if (sock != RPC_ANYSOCK) + (void) amu_close(sock); + if (client) + clnt_destroy(client); +} diff --git a/amd/amfs_link.c b/amd/amfs_link.c new file mode 100644 index 000000000000..e75ab862f8bc --- /dev/null +++ b/amd/amfs_link.c @@ -0,0 +1,133 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/amfs_link.c + * + */ + +/* + * Symbol-link file system + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* forward declarations */ +static int amfs_link_mount(am_node *mp, mntfs *mf); +static int amfs_link_umount(am_node *mp, mntfs *mf); + +/* + * Ops structures + */ +am_ops amfs_link_ops = +{ + "link", + amfs_link_match, + 0, /* amfs_link_init */ + amfs_link_mount, + amfs_link_umount, + amfs_error_lookup_child, + amfs_error_mount_child, + amfs_error_readdir, + 0, /* amfs_link_readlink */ + 0, /* amfs_link_mounted */ + 0, /* amfs_link_umounted */ + amfs_generic_find_srvr, + 0, /* nfs_fs_flags */ + 0, /* amfs_link_get_wchan */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_LINK_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ +}; + + +/* + * SFS needs a link. + */ +char * +amfs_link_match(am_opts *fo) +{ + + if (!fo->opt_fs) { + plog(XLOG_USER, "link: no fs specified"); + return 0; + } + + /* + * If the link target points to another mount point, then we could + * end up with an unpleasant situation, where the link f/s simply + * "assumes" the mntfs of that mount point. + * + * For example, if the link points to /usr, and /usr is a real ufs + * filesystem, then the link f/s will use the inherited ufs mntfs, + * and the end result will be that it will become unmountable. + * + * To prevent this, we use a hack: we prepend a dot ('.') to opt_fs if + * its original value was an absolute path, so that it will never match + * any other mntfs. + * + * XXX: a less hacky solution should be used... + */ + if (fo->opt_fs[0] == '/') { + char *link_hack = str3cat(NULL, ".", fo->opt_fs, ""); + if (!fo->opt_sublink) + fo->opt_sublink = strdup(fo->opt_fs); + XFREE(fo->opt_fs); + fo->opt_fs = link_hack; + } + + return strdup(fo->opt_fs); +} + + +static int +amfs_link_mount(am_node *mp, mntfs *mf) +{ + return 0; +} + + +static int +amfs_link_umount(am_node *mp, mntfs *mf) +{ + return 0; +} diff --git a/amd/amfs_linkx.c b/amd/amfs_linkx.c new file mode 100644 index 000000000000..e306eda91d3b --- /dev/null +++ b/amd/amfs_linkx.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/amfs_linkx.c + * + */ + +/* + * Symbol-link file system, with test that the target of the link exists. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* forward declarations */ +static int amfs_linkx_mount(am_node *mp, mntfs *mf); +static int amfs_linkx_umount(am_node *mp, mntfs *mf); + +/* + * linkx operations + */ +struct am_ops amfs_linkx_ops = +{ + "linkx", + amfs_link_match, + 0, /* amfs_linkx_init */ + amfs_linkx_mount, + amfs_linkx_umount, + amfs_error_lookup_child, + amfs_error_mount_child, + amfs_error_readdir, + 0, /* amfs_linkx_readlink */ + 0, /* amfs_linkx_mounted */ + 0, /* amfs_linkx_umounted */ + amfs_generic_find_srvr, + 0, /* amfs_linkx_get_wchan */ + FS_MBACKGROUND, +#ifdef HAVE_FS_AUTOFS + AUTOFS_LINKX_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ +}; + + +static int +amfs_linkx_mount(am_node *mp, mntfs *mf) +{ + /* + * Check for existence of target. + */ + struct stat stb; + char *ln; + + if (mp->am_link) + ln = mp->am_link; + else /* should never occur */ + ln = mf->mf_mount; + + /* + * Use lstat, not stat, since we don't + * want to know if the ultimate target of + * a symlink chain exists, just the first. + */ + if (lstat(ln, &stb) < 0) + return errno; + + return 0; +} + + +static int +amfs_linkx_umount(am_node *mp, mntfs *mf) +{ + return 0; +} diff --git a/amd/amfs_nfsl.c b/amd/amfs_nfsl.c new file mode 100644 index 000000000000..bb48f0efd255 --- /dev/null +++ b/amd/amfs_nfsl.c @@ -0,0 +1,238 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/amfs_nfsl.c + * + */ + +/* + * NFSL: Network file system with local existence check. If the local + * path denoted by $rfs exists, it behaves as type:=link. + * + * Example: + * pkg type:=nfsl;rhost:=jonny;rfs:=/n/johnny/src/pkg;fs:=${rfs} + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + + +/* forward declarations */ +static char *amfs_nfsl_match(am_opts *fo); +static int amfs_nfsl_init(mntfs *mf); +static int amfs_nfsl_mount(am_node *mp, mntfs *mf); +static int amfs_nfsl_umount(am_node *mp, mntfs *mf); +static void amfs_nfsl_umounted(mntfs *mf); +static fserver *amfs_nfsl_ffserver(mntfs *mf); + +/* + * NFS-Link operations + */ +am_ops amfs_nfsl_ops = +{ + "nfsl", + amfs_nfsl_match, + amfs_nfsl_init, + amfs_nfsl_mount, + amfs_nfsl_umount, + amfs_error_lookup_child, + amfs_error_mount_child, + amfs_error_readdir, + 0, /* amfs_nfsl_readlink */ + 0, /* amfs_nfsl_mounted */ + amfs_nfsl_umounted, + amfs_nfsl_ffserver, + 0, /* amfs_nfsl_get_wchan */ + FS_MKMNT | FS_BACKGROUND | FS_AMQINFO, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_NFSL_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ +}; + + +/* + * Check that f/s has all needed fields. + * Returns: matched string if found, NULL otherwise. + */ +static char * +amfs_nfsl_match(am_opts *fo) +{ + char *cp; + char *ho = fo->opt_rhost; + char *retval; + struct stat stb; + + if (fo->opt_sublink) + cp = fo->opt_sublink; + else + cp = fo->opt_fs; + + if (!cp || !ho) { + plog(XLOG_USER, "amfs_nfsl: host $fs and $rhost must be specified"); + return NULL; + } + + /* + * If this host is not the same as $rhost, or if link does not exist, + * call nfs_ops.fs_match(). + * If link value exists (or same host), call amfs_link_ops.fs_match(). + */ + if (!STRCEQ(ho, am_get_hostname())) { + plog(XLOG_INFO, "amfs_nfsl: \"%s\" is not local host, using type:=nfs", ho); + retval = nfs_ops.fs_match(fo); + } else if (lstat(cp, &stb) < 0) { + plog(XLOG_INFO, "amfs_nfsl: \"%s\" does not exist, using type:=nfs", cp); + retval = nfs_ops.fs_match(fo); + } else { + plog(XLOG_INFO, "amfs_nfsl: \"%s\" exists, using type:=link", cp); + retval = amfs_link_ops.fs_match(fo); + } + return retval; +} + + +/* + * Initialize. + * Returns: 0 if OK, non-zero (errno) if failed. + */ +static int +amfs_nfsl_init(mntfs *mf) +{ + int ret = 0; + if (mf->mf_flags & MFF_NFSLINK) { + if (amfs_link_ops.fs_init) + ret = amfs_link_ops.fs_init(mf); + } else { + if (nfs_ops.fs_init) + ret = nfs_ops.fs_init(mf); + } + return ret; +} + + +/* + * Mount vfs. + * Returns: 0 if OK, non-zero (errno) if failed. + */ +static int +amfs_nfsl_mount(am_node *mp, mntfs *mf) +{ + int ret = 0; + if (mf->mf_flags & MFF_NFSLINK) { + if (amfs_link_ops.mount_fs) + ret = amfs_link_ops.mount_fs(mp, mf); + } else { + if (nfs_ops.mount_fs) + ret = nfs_ops.mount_fs(mp, mf); + } + return ret; +} + + +/* + * Unmount VFS. + * Returns: 0 if OK, non-zero (errno) if failed. + */ +static int +amfs_nfsl_umount(am_node *mp, mntfs *mf) +{ + int ret = 0; + if (mf->mf_flags & MFF_NFSLINK) { + if (amfs_link_ops.umount_fs) + ret = amfs_link_ops.umount_fs(mp, mf); + } else { + if (nfs_ops.umount_fs) + ret = nfs_ops.umount_fs(mp, mf); + } + return ret; +} + + +/* + * Async unmount callback function. + * After the base umount() succeeds, we may want to take extra actions, + * such as informing remote mount daemons that we've unmounted them. + * See amfs_auto_umounted(), host_umounted(), nfs_umounted(). + */ +static void +amfs_nfsl_umounted(mntfs *mf) +{ + if (mf->mf_flags & MFF_NFSLINK) { + if (amfs_link_ops.umounted) + amfs_link_ops.umounted(mf); + } else { + if (nfs_ops.umounted) + nfs_ops.umounted(mf); + } +} + + +/* + * Find a file server. + * Returns: fserver of found server, or NULL if not found. + */ +static fserver * +amfs_nfsl_ffserver(mntfs *mf) +{ + char *cp; + char *ho = mf->mf_fo->opt_rhost; + struct stat stb; + + if (mf->mf_fo->opt_sublink) + cp = mf->mf_fo->opt_sublink; + else + cp = mf->mf_fo->opt_fs; + + /* + * If this host is not the same as $rhost, or if link does not exist, + * call amfs_link_ops.ffserver(). + * If link value exists (or same host), then call ops_nfs.ffserver(). + */ + if (!STRCEQ(ho, am_get_hostname()) || lstat(cp, &stb) < 0) { + return nfs_ops.ffserver(mf); + } else { + mf->mf_flags |= MFF_NFSLINK; + /* remove the FS_MKMNT flag, we don't want amd touching the mountpoint */ + mf->mf_fsflags &= ~FS_MKMNT; + return amfs_link_ops.ffserver(mf); + } +} diff --git a/amd/amfs_nfsx.c b/amd/amfs_nfsx.c new file mode 100644 index 000000000000..91be8af47e11 --- /dev/null +++ b/amd/amfs_nfsx.c @@ -0,0 +1,512 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/amfs_nfsx.c + * + */ + +/* + * NFS hierarchical mounts + * + * TODO: Re-implement. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* + * The rfs field contains a list of mounts to be done from + * the remote host. + */ +typedef struct amfs_nfsx_mnt { + mntfs *n_mnt; + int n_error; +} amfs_nfsx_mnt; + +struct amfs_nfsx { + int nx_c; /* Number of elements in nx_v */ + amfs_nfsx_mnt *nx_v; /* Underlying mounts */ + amfs_nfsx_mnt *nx_try; + am_node *nx_mp; +}; + +/* forward definitions */ +static char *amfs_nfsx_match(am_opts *fo); +static int amfs_nfsx_mount(am_node *am, mntfs *mf); +static int amfs_nfsx_umount(am_node *am, mntfs *mf); +static int amfs_nfsx_init(mntfs *mf); + +/* + * Ops structure + */ +am_ops amfs_nfsx_ops = +{ + "nfsx", + amfs_nfsx_match, + amfs_nfsx_init, + amfs_nfsx_mount, + amfs_nfsx_umount, + amfs_error_lookup_child, + amfs_error_mount_child, + amfs_error_readdir, + 0, /* amfs_nfsx_readlink */ + 0, /* amfs_nfsx_mounted */ + 0, /* amfs_nfsx_umounted */ + find_nfs_srvr, /* XXX */ + 0, /* amfs_nfsx_get_wchan */ + /* FS_UBACKGROUND| */ FS_AMQINFO, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_NFSX_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ +}; + + +static char * +amfs_nfsx_match(am_opts *fo) +{ + char *xmtab; + char *ptr; + int len; + + if (!fo->opt_rfs) { + plog(XLOG_USER, "amfs_nfsx: no remote filesystem specified"); + return FALSE; + } + + if (!fo->opt_rhost) { + plog(XLOG_USER, "amfs_nfsx: no remote host specified"); + return FALSE; + } + + /* set default sublink */ + if (fo->opt_sublink == 0) { + ptr = strchr(fo->opt_rfs, ','); + if (ptr && ptr > (fo->opt_rfs + 1)) + fo->opt_sublink = strnsave(fo->opt_rfs + 1, ptr - fo->opt_rfs - 1); + } + + /* + * Remove trailing ",..." from ${fs} + * After deslashifying, overwrite the end of ${fs} with "/" + * to make sure it is unique. + */ + if ((ptr = strchr(fo->opt_fs, ','))) + *ptr = '\0'; + deslashify(fo->opt_fs); + + /* + * Bump string length to allow trailing / + */ + len = strlen(fo->opt_fs); + fo->opt_fs = xrealloc(fo->opt_fs, len + 1 + 1); + ptr = fo->opt_fs + len; + + /* + * Make unique... + */ + *ptr++ = '/'; + *ptr = '\0'; + + /* + * Determine magic cookie to put in mtab + */ + xmtab = str3cat((char *) 0, fo->opt_rhost, ":", fo->opt_rfs); + dlog("NFSX: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"", + fo->opt_rhost, fo->opt_rfs, fo->opt_fs); + + return xmtab; +} + + +static void +amfs_nfsx_prfree(opaque_t vp) +{ + struct amfs_nfsx *nx = (struct amfs_nfsx *) vp; + int i; + + for (i = 0; i < nx->nx_c; i++) { + mntfs *m = nx->nx_v[i].n_mnt; + if (m) + free_mntfs(m); + } + + XFREE(nx->nx_v); + XFREE(nx); +} + + +static int +amfs_nfsx_init(mntfs *mf) +{ + /* + * mf_info has the form: + * host:/prefix/path,sub,sub,sub + */ + int i; + int glob_error; + struct amfs_nfsx *nx; + int asked_for_wakeup = 0; + + nx = (struct amfs_nfsx *) mf->mf_private; + + if (nx == 0) { + char **ivec; + char *info = 0; + char *host; + char *pref; + int error = 0; + + info = strdup(mf->mf_info); + host = strchr(info, ':'); + if (!host) { + error = EINVAL; + goto errexit; + } + pref = host + 1; + host = info; + + /* + * Split the prefix off from the suffices + */ + ivec = strsplit(pref, ',', '\''); + + /* + * Count array size + */ + for (i = 0; ivec[i]; i++) + /* nothing */; + + nx = ALLOC(struct amfs_nfsx); + mf->mf_private = (opaque_t) nx; + mf->mf_prfree = amfs_nfsx_prfree; + + nx->nx_c = i - 1; /* i-1 because we don't want the prefix */ + nx->nx_v = (amfs_nfsx_mnt *) xmalloc(nx->nx_c * sizeof(amfs_nfsx_mnt)); + nx->nx_mp = 0; + { + char *mp = 0; + char *xinfo = 0; + char *fs = mf->mf_fo->opt_fs; + char *rfs = 0; + for (i = 0; i < nx->nx_c; i++) { + char *path = ivec[i + 1]; + rfs = str3cat(rfs, pref, "/", path); + /* + * Determine the mount point. + * If this is the root, then don't remove + * the trailing slash to avoid mntfs name clashes. + */ + mp = str3cat(mp, fs, "/", rfs); + normalize_slash(mp); + deslashify(mp); + /* + * Determine the mount info + */ + xinfo = str3cat(xinfo, host, *path == '/' ? "" : "/", path); + normalize_slash(xinfo); + if (pref[1] != '\0') + deslashify(xinfo); + dlog("amfs_nfsx: init mount for %s on %s", xinfo, mp); + nx->nx_v[i].n_error = -1; + nx->nx_v[i].n_mnt = find_mntfs(&nfs_ops, mf->mf_fo, mp, xinfo, "", mf->mf_mopts, mf->mf_remopts); + /* propagate the on_autofs flag */ + nx->nx_v[i].n_mnt->mf_flags |= mf->mf_flags & MFF_ON_AUTOFS; + } + if (rfs) + XFREE(rfs); + if (mp) + XFREE(mp); + if (xinfo) + XFREE(xinfo); + } + + XFREE(ivec); + errexit: + if (info) + XFREE(info); + if (error) + return error; + } + + /* + * Iterate through the mntfs's and call + * the underlying init routine on each + */ + glob_error = 0; + + for (i = 0; i < nx->nx_c; i++) { + amfs_nfsx_mnt *n = &nx->nx_v[i]; + mntfs *m = n->n_mnt; + int error = 0; + if (m->mf_ops->fs_init && !(mf->mf_flags & MFF_RESTART)) + error = m->mf_ops->fs_init(m); + /* + * if you just "return error" here, you will have made a failure + * in any submounts to fail the whole group. There was old unused code + * here before. + */ + if (error > 0) + n->n_error = error; + + else if (error < 0) { + glob_error = -1; + if (!asked_for_wakeup) { + asked_for_wakeup = 1; + sched_task(wakeup_task, (opaque_t) mf, get_mntfs_wchan(m)); + } + } + } + + return glob_error; +} + + +static void +amfs_nfsx_cont(int rc, int term, opaque_t arg) +{ + mntfs *mf = (mntfs *) arg; + struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private; + am_node *mp = nx->nx_mp; + amfs_nfsx_mnt *n = nx->nx_try; + + n->n_mnt->mf_flags &= ~(MFF_ERROR | MFF_MOUNTING); + mf->mf_flags &= ~MFF_ERROR; + + /* + * Wakeup anything waiting for this mount + */ + wakeup(get_mntfs_wchan(n->n_mnt)); + + if (rc || term) { + if (term) { + /* + * Not sure what to do for an error code. + */ + plog(XLOG_ERROR, "mount for %s got signal %d", n->n_mnt->mf_mount, term); + n->n_error = EIO; + } else { + /* + * Check for exit status + */ + errno = rc; /* XXX */ + plog(XLOG_ERROR, "%s: mount (amfs_nfsx_cont): %m", n->n_mnt->mf_mount); + n->n_error = rc; + } + free_mntfs(n->n_mnt); + n->n_mnt = new_mntfs(); + n->n_mnt->mf_error = n->n_error; + n->n_mnt->mf_flags |= MFF_ERROR; + } else { + /* + * The mount worked. + */ + mf_mounted(n->n_mnt, FALSE); /* FALSE => don't free the n_mnt->am_opts */ + n->n_error = 0; + } + + /* + * Do the remaining bits + */ + if (amfs_nfsx_mount(mp, mf) >= 0) + wakeup(get_mntfs_wchan(mf)); +} + + +static int +try_amfs_nfsx_mount(opaque_t mv) +{ + mntfs *mf = (mntfs *) mv; + struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private; + am_node *mp = nx->nx_mp; + int error; + + error = mf->mf_ops->mount_fs(mp, mf); + + return error; +} + + +static int +amfs_nfsx_remount(am_node *am, mntfs *mf, int fg) +{ + struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private; + amfs_nfsx_mnt *n; + int glob_error = -1; + + /* Save the am_node pointer for later use */ + nx->nx_mp = am; + + /* + * Iterate through the mntfs's and mount each filesystem + * which is not yet mounted. + */ + for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) { + mntfs *m = n->n_mnt; + + if (m->mf_flags & MFF_MOUNTING) + break; + + if (m->mf_flags & MFF_MOUNTED) { + mf_mounted(m, FALSE); /* FALSE => don't free the m->am_opts */ + n->n_error = glob_error = 0; + continue; + } + + if (n->n_error < 0) { + /* Create the mountpoint, if and as required */ + if (!(m->mf_flags & MFF_MKMNT) && m->mf_fsflags & FS_MKMNT) { + if (!mkdirs(m->mf_mount, 0555)) + m->mf_flags |= MFF_MKMNT; + } + + dlog("calling underlying mount on %s", m->mf_mount); + if (!fg && foreground && (m->mf_fsflags & FS_MBACKGROUND)) { + m->mf_flags |= MFF_MOUNTING; + dlog("backgrounding mount of \"%s\"", m->mf_info); + nx->nx_try = n; + run_task(try_amfs_nfsx_mount, (opaque_t) m, amfs_nfsx_cont, (opaque_t) mf); + n->n_error = -1; + return -1; + } else { + dlog("foreground mount of \"%s\" ...", mf->mf_info); + n->n_error = m->mf_ops->mount_fs(am, m); + } + + if (n->n_error > 0) + dlog("underlying fmount of %s failed: %s", m->mf_mount, strerror(n->n_error)); + + if (n->n_error == 0) { + glob_error = 0; + } else if (glob_error < 0) { + glob_error = n->n_error; + } + } + } + + return glob_error < 0 ? 0 : glob_error; +} + + +static int +amfs_nfsx_mount(am_node *am, mntfs *mf) +{ + return amfs_nfsx_remount(am, mf, FALSE); +} + + +/* + * Unmount an NFS hierarchy. + * Note that this is called in the foreground + * and so may hang under extremely rare conditions. + */ +static int +amfs_nfsx_umount(am_node *am, mntfs *mf) +{ + struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private; + amfs_nfsx_mnt *n; + int glob_error = 0; + + /* + * Iterate in reverse through the mntfs's and unmount each filesystem + * which is mounted. + */ + for (n = nx->nx_v + nx->nx_c - 1; n >= nx->nx_v; --n) { + mntfs *m = n->n_mnt; + /* + * If this node has not been messed with + * and there has been no error so far + * then try and unmount. + * If an error had occurred then zero + * the error code so that the remount + * only tries to unmount those nodes + * which had been successfully unmounted. + */ + if (n->n_error == 0) { + dlog("calling underlying fumount on %s", m->mf_mount); + n->n_error = m->mf_ops->umount_fs(am, m); + if (n->n_error) { + glob_error = n->n_error; + n->n_error = 0; + } else { + /* + * Make sure remount gets this node + */ + n->n_error = -1; + } + } + } + + /* + * If any unmounts failed then remount the + * whole lot... + */ + if (glob_error) { + glob_error = amfs_nfsx_remount(am, mf, TRUE); + if (glob_error) { + errno = glob_error; /* XXX */ + plog(XLOG_USER, "amfs_nfsx: remount of %s failed: %m", mf->mf_mount); + } + glob_error = EBUSY; + } else { + /* + * Remove all the mount points + */ + for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) { + mntfs *m = n->n_mnt; + dlog("calling underlying umounted on %s", m->mf_mount); + if (m->mf_ops->umounted) + m->mf_ops->umounted(m); + + if (n->n_error < 0) { + if (m->mf_fsflags & FS_MKMNT) { + (void) rmdirs(m->mf_mount); + m->mf_flags &= ~MFF_MKMNT; + } + } + free_mntfs(m); + n->n_mnt = 0; + n->n_error = -1; + } + } + + return glob_error; +} diff --git a/amd/amfs_program.c b/amd/amfs_program.c new file mode 100644 index 000000000000..843e8050c11b --- /dev/null +++ b/amd/amfs_program.c @@ -0,0 +1,205 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * 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. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/amfs_program.c + * + */ + +/* + * Program file system + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* forward definitions */ +static char *amfs_program_match(am_opts *fo); +static int amfs_program_mount(am_node *am, mntfs *mf); +static int amfs_program_umount(am_node *am, mntfs *mf); +static int amfs_program_init(mntfs *mf); + +/* + * Ops structure + */ +am_ops amfs_program_ops = +{ + "program", + amfs_program_match, + amfs_program_init, + amfs_program_mount, + amfs_program_umount, + amfs_error_lookup_child, + amfs_error_mount_child, + amfs_error_readdir, + 0, /* amfs_program_readlink */ + 0, /* amfs_program_mounted */ + 0, /* amfs_program_umounted */ + amfs_generic_find_srvr, + 0, /* amfs_program_get_wchan */ + FS_MKMNT | FS_BACKGROUND | FS_AMQINFO, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_PROGRAM_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ +}; + + +/* + * Execute needs a mount and unmount command. + */ +static char * +amfs_program_match(am_opts *fo) +{ + char *prog; + + if (fo->opt_unmount && fo->opt_umount) { + plog(XLOG_ERROR, "program: cannot specify both unmount and umount options"); + return 0; + } + if (!fo->opt_mount) { + plog(XLOG_ERROR, "program: must specify mount command"); + return 0; + } + if (!fo->opt_unmount && !fo->opt_umount) { + fo->opt_unmount = str3cat(NULL, UNMOUNT_PROGRAM, " umount ", fo->opt_fs); + plog(XLOG_INFO, "program: un/umount not specified; using default \"%s\"", + fo->opt_unmount); + } + prog = strchr(fo->opt_mount, ' '); + + return strdup(prog ? prog + 1 : fo->opt_mount); +} + + +static int +amfs_program_init(mntfs *mf) +{ + /* check if already saved value */ + if (mf->mf_private != NULL) + return 0; + + /* save unmount (or umount) command */ + if (mf->mf_fo->opt_unmount != NULL) + mf->mf_private = (opaque_t) strdup(mf->mf_fo->opt_unmount); + else + mf->mf_private = (opaque_t) strdup(mf->mf_fo->opt_umount); + mf->mf_prfree = (void (*)(opaque_t)) free; + + return 0; +} + + +static int +amfs_program_exec(char *info) +{ + char **xivec; + int error; + + /* + * Split copy of command info string + */ + info = strdup(info); + if (info == 0) + return ENOBUFS; + xivec = strsplit(info, ' ', '\''); + + /* + * Put stdout to stderr + */ + (void) fclose(stdout); + if (!logfp) + logfp = stderr; /* initialize before possible first use */ + (void) dup(fileno(logfp)); + if (fileno(logfp) != fileno(stderr)) { + (void) fclose(stderr); + (void) dup(fileno(logfp)); + } + + /* + * Try the exec + */ + if (amuDebug(D_FULL)) { + char **cp = xivec; + plog(XLOG_DEBUG, "executing (un)mount command..."); + while (*cp) { + plog(XLOG_DEBUG, "arg[%ld] = '%s'", (long) (cp - xivec), *cp); + cp++; + } + } + + if (xivec[0] == 0 || xivec[1] == 0) { + errno = EINVAL; + plog(XLOG_USER, "1st/2nd args missing to (un)mount program"); + } else { + (void) execv(xivec[0], xivec + 1); + } + + /* + * Save error number + */ + error = errno; + plog(XLOG_ERROR, "exec failed: %m"); + + /* + * Free allocate memory + */ + XFREE(info); + XFREE(xivec); + + /* + * Return error + */ + return error; +} + + +static int +amfs_program_mount(am_node *am, mntfs *mf) +{ + return amfs_program_exec(mf->mf_fo->opt_mount); +} + + +static int +amfs_program_umount(am_node *am, mntfs *mf) +{ + return amfs_program_exec((char *) mf->mf_private); +} diff --git a/amd/amfs_root.c b/amd/amfs_root.c new file mode 100644 index 000000000000..243267e71e86 --- /dev/null +++ b/amd/amfs_root.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/amfs_root.c + * + */ + +/* + * Root file system + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/**************************************************************************** + *** FORWARD DEFINITIONS *** + ****************************************************************************/ +static int amfs_root_mount(am_node *mp, mntfs *mf); + +/**************************************************************************** + *** OPS STRUCTURES *** + ****************************************************************************/ +am_ops amfs_root_ops = +{ + "root", + 0, /* amfs_root_match */ + 0, /* amfs_root_init */ + amfs_root_mount, + amfs_generic_umount, + amfs_generic_lookup_child, + amfs_generic_mount_child, + amfs_generic_readdir, + 0, /* amfs_root_readlink */ + 0, /* amfs_root_mounted */ + 0, /* amfs_root_umounted */ + amfs_generic_find_srvr, + 0, /* amfs_root_get_wchan */ + FS_NOTIMEOUT | FS_AMQINFO | FS_DIRECTORY, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_ROOT_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ +}; + + +/**************************************************************************** + *** FUNCTIONS *** + ****************************************************************************/ + +/* + * Mount the root... + */ +static int +amfs_root_mount(am_node *mp, mntfs *mf) +{ + mf->mf_mount = strealloc(mf->mf_mount, pid_fsname); + mf->mf_private = (opaque_t) mapc_find(mf->mf_info, "", NULL); + mf->mf_prfree = mapc_free; + + return 0; +} diff --git a/amd/amfs_toplvl.c b/amd/amfs_toplvl.c new file mode 100644 index 000000000000..1b968f197f55 --- /dev/null +++ b/amd/amfs_toplvl.c @@ -0,0 +1,318 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/amfs_toplvl.c + * + */ + +/* + * Top-level file system + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/**************************************************************************** + *** FORWARD DEFINITIONS *** + ****************************************************************************/ +static int amfs_toplvl_init(mntfs *mf); + +/**************************************************************************** + *** OPS STRUCTURES *** + ****************************************************************************/ +am_ops amfs_toplvl_ops = +{ + "toplvl", + amfs_generic_match, + amfs_toplvl_init, /* amfs_toplvl_init */ + amfs_toplvl_mount, + amfs_toplvl_umount, + amfs_generic_lookup_child, + amfs_generic_mount_child, + amfs_generic_readdir, + 0, /* amfs_toplvl_readlink */ + amfs_generic_mounted, + 0, /* amfs_toplvl_umounted */ + amfs_generic_find_srvr, + 0, /* amfs_toplvl_get_wchan */ + FS_MKMNT | FS_NOTIMEOUT | FS_BACKGROUND | + FS_AMQINFO | FS_DIRECTORY, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_TOPLVL_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ +}; + + +/**************************************************************************** + *** FUNCTIONS *** + ****************************************************************************/ + +static void +set_auto_attrcache_timeout(char *preopts, char *opts, size_t l) +{ + +#ifdef MNTTAB_OPT_NOAC + /* + * Don't cache attributes - they are changing under the kernel's feet. + * For example, IRIX5.2 will dispense with nfs lookup calls and hand stale + * filehandles to getattr unless we disable attribute caching on the + * automount points. + */ + if (gopt.auto_attrcache == 0) { + xsnprintf(preopts, l, ",%s", MNTTAB_OPT_NOAC); + xstrlcat(opts, preopts, l); + } +#endif /* MNTTAB_OPT_NOAC */ + + /* + * XXX: note that setting these to 0 in the past resulted in an error on + * some systems, which is why it's better to use "noac" if possible. For + * now, we're setting everything possible, but if this will cause trouble, + * then we'll have to condition the remainder of this on OPT_NOAC. + */ +#ifdef MNTTAB_OPT_ACTIMEO + xsnprintf(preopts, l, ",%s=%d", MNTTAB_OPT_ACTIMEO, gopt.auto_attrcache); + xstrlcat(opts, preopts, l); +#else /* MNTTAB_OPT_ACTIMEO */ +# ifdef MNTTAB_OPT_ACDIRMIN + xsnprintf(preopts, l, ",%s=%d", MNTTAB_OPT_ACTDIRMIN, gopt.auto_attrcache); + xstrlcat(opts, preopts, l); +# endif /* MNTTAB_OPT_ACDIRMIN */ +# ifdef MNTTAB_OPT_ACDIRMAX + xsnprintf(preopts, l, ",%s=%d", MNTTAB_OPT_ACTDIRMAX, gopt.auto_attrcache); + xstrlcat(opts, preopts, l); +# endif /* MNTTAB_OPT_ACDIRMAX */ +# ifdef MNTTAB_OPT_ACREGMIN + xsnprintf(preopts, l, ",%s=%d", MNTTAB_OPT_ACTREGMIN, gopt.auto_attrcache); + xstrlcat(opts, preopts, l); +# endif /* MNTTAB_OPT_ACREGMIN */ +# ifdef MNTTAB_OPT_ACREGMAX + xsnprintf(preopts, l, ",%s=%d", MNTTAB_OPT_ACTREGMAX, gopt.auto_attrcache); + xstrlcat(opts, preopts, l); +# endif /* MNTTAB_OPT_ACREGMAX */ +#endif /* MNTTAB_OPT_ACTIMEO */ +} + + +/* + * Initialize a top-level mount. In our case, if the user asked for + * forced_unmounts, and the OS supports it, then we try forced/lazy unmounts + * on any previous toplvl mounts. This is useful if a previous Amd died and + * left behind toplvl mount points (this Amd will clean them up). + * + * WARNING: Don't use forced/lazy unmounts if you have another valid Amd + * running, because this code WILL force those valid toplvl mount points to + * be detached as well! + */ +static int +amfs_toplvl_init(mntfs *mf) +{ + int error = 0; + +#if defined(MNT2_GEN_OPT_FORCE) || defined(MNT2_GEN_OPT_DETACH) + if (gopt.flags & CFM_FORCED_UNMOUNTS) { + plog(XLOG_INFO, "amfs_toplvl_init: trying forced/lazy unmount of %s", + mf->mf_mount); + error = umount2_fs(mf->mf_mount, AMU_UMOUNT_FORCE | AMU_UMOUNT_DETACH); + if (error) + plog(XLOG_INFO, "amfs_toplvl_init: forced/lazy unmount failed: %m"); + else + dlog("amfs_toplvl_init: forced/lazy unmount succeeded"); + } +#endif /* MNT2_GEN_OPT_FORCE || MNT2_GEN_OPT_DETACH */ + return error; +} + + +/* + * Mount the top-level + */ +int +amfs_toplvl_mount(am_node *mp, mntfs *mf) +{ + struct stat stb; + char opts[SIZEOF_OPTS], preopts[SIZEOF_OPTS], toplvl_opts[40]; + int error; + + /* + * Mounting the automounter. + * Make sure the mount directory exists, construct + * the mount options and call the mount_amfs_toplvl routine. + */ + + if (stat(mp->am_path, &stb) < 0) { + return errno; + } else if ((stb.st_mode & S_IFMT) != S_IFDIR) { + plog(XLOG_WARNING, "%s is not a directory", mp->am_path); + return ENOTDIR; + } + + /* + * Construct some mount options: + * + * Tack on magic map=<mapname> option in mtab to emulate + * SunOS automounter behavior. + */ + +#ifdef HAVE_FS_AUTOFS + if (mf->mf_flags & MFF_IS_AUTOFS) { + autofs_get_opts(opts, sizeof(opts), mp->am_autofs_fh); + } else +#endif /* HAVE_FS_AUTOFS */ + { + preopts[0] = '\0'; +#ifdef MNTTAB_OPT_INTR + xstrlcat(preopts, MNTTAB_OPT_INTR, sizeof(preopts)); + xstrlcat(preopts, ",", sizeof(preopts)); +#endif /* MNTTAB_OPT_INTR */ +#ifdef MNTTAB_OPT_IGNORE + xstrlcat(preopts, MNTTAB_OPT_IGNORE, sizeof(preopts)); + xstrlcat(preopts, ",", sizeof(preopts)); +#endif /* MNTTAB_OPT_IGNORE */ + /* write most of the initial options + preopts */ + xsnprintf(opts, sizeof(opts), "%s%s,%s=%d,%s,map=%s", + preopts, + MNTTAB_OPT_RW, + MNTTAB_OPT_PORT, nfs_port, + mf->mf_ops->fs_type, mf->mf_info); + + /* process toplvl timeo/retrans options, if any */ + if (gopt.amfs_auto_timeo[AMU_TYPE_TOPLVL] > 0) { + xsnprintf(toplvl_opts, sizeof(toplvl_opts), ",%s=%d", + MNTTAB_OPT_TIMEO, gopt.amfs_auto_timeo[AMU_TYPE_TOPLVL]); + xstrlcat(opts, toplvl_opts, sizeof(opts)); + } + if (gopt.amfs_auto_retrans[AMU_TYPE_TOPLVL] > 0) { + xsnprintf(toplvl_opts, sizeof(toplvl_opts), ",%s=%d", + MNTTAB_OPT_RETRANS, gopt.amfs_auto_retrans[AMU_TYPE_TOPLVL]); + xstrlcat(opts, toplvl_opts, sizeof(opts)); + } + +#ifdef MNTTAB_OPT_NOAC + if (gopt.auto_attrcache == 0) { + xstrlcat(opts, ",", sizeof(opts)); + xstrlcat(opts, MNTTAB_OPT_NOAC, sizeof(opts)); + } else +#endif /* MNTTAB_OPT_NOAC */ + set_auto_attrcache_timeout(preopts, opts, sizeof(preopts)); + } + + /* now do the mount */ + error = amfs_mount(mp, mf, opts); + if (error) { + errno = error; + plog(XLOG_FATAL, "amfs_toplvl_mount: amfs_mount failed: %m"); + return error; + } + return 0; +} + + +/* + * Unmount a top-level automount node + */ +int +amfs_toplvl_umount(am_node *mp, mntfs *mf) +{ + struct stat stb; + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + int error; + int count = 0; /* how many times did we try to unmount? */ + +again: + /* + * The lstat is needed if this mount is type=direct. + * When that happens, the kernel cache gets confused + * between the underlying type (dir) and the mounted + * type (link) and so needs to be re-synced before + * the unmount. This is all because the unmount system + * call follows links and so can't actually unmount + * a link (stupid!). It was noted that doing an ls -ld + * of the mount point to see why things were not working + * actually fixed the problem - so simulate an ls -ld here. + */ + if (lstat(mp->am_path, &stb) < 0) { + error = errno; + dlog("lstat(%s): %m", mp->am_path); + goto out; + } + if ((stb.st_mode & S_IFMT) != S_IFDIR) { + plog(XLOG_ERROR, "amfs_toplvl_umount: %s is not a directory, aborting.", mp->am_path); + error = ENOTDIR; + goto out; + } + + error = UMOUNT_FS(mp->am_path, mnttab_file_name, unmount_flags); + if (error == EBUSY) { +#ifdef HAVE_FS_AUTOFS + /* + * autofs mounts are "in place", so it is possible + * that we can't just unmount our mount points and go away. + * If that's the case, just give up. + */ + if (mf->mf_flags & MFF_IS_AUTOFS) + return error; +#endif /* HAVE_FS_AUTOFS */ + plog(XLOG_WARNING, "amfs_toplvl_unmount retrying %s in 1s", mp->am_path); + count++; + sleep(1); + /* + * If user wants forced/lazy unmount semantics, then set those flags, + * but only after we've tried normal lstat/umount a few times -- + * otherwise forced unmounts may hang this very same Amd (by preventing + * it from achieving a clean unmount). + */ + if (gopt.flags & CFM_FORCED_UNMOUNTS) { + if (count == 5) { /* after 5 seconds, try MNT_FORCE */ + dlog("enabling forced unmounts for toplvl node %s", mp->am_path); + unmount_flags |= AMU_UMOUNT_FORCE; + } + if (count == 10) { /* after 10 seconds, try MNT_DETACH */ + dlog("enabling detached unmounts for toplvl node %s", mp->am_path); + unmount_flags |= AMU_UMOUNT_DETACH; + } + } + goto again; + } +out: + return error; +} diff --git a/amd/amfs_union.c b/amd/amfs_union.c new file mode 100644 index 000000000000..6adb5b0bd9cb --- /dev/null +++ b/amd/amfs_union.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/amfs_union.c + * + */ + +/* + * Union automounter file system + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/**************************************************************************** + *** FORWARD DEFINITIONS *** + ****************************************************************************/ +static int create_amfs_union_node(char *dir, opaque_t arg); +static void amfs_union_mounted(mntfs *mf); + + +/**************************************************************************** + *** OPS STRUCTURES *** + ****************************************************************************/ +am_ops amfs_union_ops = +{ + "union", + amfs_generic_match, + 0, /* amfs_union_init */ + amfs_toplvl_mount, + amfs_toplvl_umount, + amfs_generic_lookup_child, + amfs_generic_mount_child, + amfs_generic_readdir, + 0, /* amfs_union_readlink */ + amfs_union_mounted, + 0, /* amfs_union_umounted */ + amfs_generic_find_srvr, + 0, /* amfs_union_get_wchan */ + FS_MKMNT | FS_NOTIMEOUT | FS_BACKGROUND | FS_AMQINFO | FS_DIRECTORY, +#ifdef HAVE_FS_AUTOFS + AUTOFS_UNION_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ +}; + + +/* + * Create a reference to a union'ed entry + * XXX: this function may not be used anywhere... + */ +static int +create_amfs_union_node(char *dir, opaque_t arg) +{ + if (!STREQ(dir, "/defaults")) { + int error = 0; + am_node *am; + am = amfs_generic_lookup_child(arg, dir, &error, VLOOK_CREATE); + if (am && error < 0) + am = amfs_generic_mount_child(am, &error); + if (error > 0) { + errno = error; /* XXX */ + plog(XLOG_ERROR, "unionfs: could not mount %s: %m", dir); + } + return error; + } + return 0; +} + + +static void +amfs_union_mounted(mntfs *mf) +{ + int index; + am_node *mp; + + amfs_mkcacheref(mf); + + /* + * Having made the union mount point, + * populate all the entries... + */ + for (mp = get_first_exported_ap(&index); + mp; + mp = get_next_exported_ap(&index)) { + if (mp->am_mnt == mf) { + /* return value from create_amfs_union_node is ignored by mapc_keyiter */ + (void) mapc_keyiter((mnt_map *) mp->am_mnt->mf_private, + create_amfs_union_node, + mp); + break; + } + } +} diff --git a/amd/amq_subr.c b/amd/amq_subr.c new file mode 100644 index 000000000000..79ecafc3d9b3 --- /dev/null +++ b/amd/amq_subr.c @@ -0,0 +1,479 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/amq_subr.c + * + */ +/* + * Auxiliary routines for amq tool + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* forward definitions */ +bool_t xdr_amq_mount_tree_node(XDR *xdrs, amq_mount_tree *objp); +bool_t xdr_amq_mount_subtree(XDR *xdrs, amq_mount_tree *objp); + + +voidp +amqproc_null_1_svc(voidp argp, struct svc_req *rqstp) +{ + static char res; + + return (voidp) &res; +} + + +/* + * Return a sub-tree of mounts + */ +amq_mount_tree_p * +amqproc_mnttree_1_svc(voidp argp, struct svc_req *rqstp) +{ + static am_node *mp; + + mp = find_ap(*(char **) argp); + return (amq_mount_tree_p *) ((void *)&mp); +} + + +/* + * Unmount a single node + */ +voidp +amqproc_umnt_1_svc(voidp argp, struct svc_req *rqstp) +{ + static char res; + am_node *mp = find_ap(*(char **) argp); + + if (mp) + forcibly_timeout_mp(mp); + + return (voidp) &res; +} + + +/* + * Return global statistics + */ +amq_mount_stats * +amqproc_stats_1_svc(voidp argp, struct svc_req *rqstp) +{ + return (amq_mount_stats *) ((void *)&amd_stats); +} + + +/* + * Return the entire tree of mount nodes + */ +amq_mount_tree_list * +amqproc_export_1_svc(voidp argp, struct svc_req *rqstp) +{ + static amq_mount_tree_list aml; + static am_node *mp; + + mp = get_exported_ap(0); + aml.amq_mount_tree_list_val = (amq_mount_tree_p *) ((void *) &mp); + aml.amq_mount_tree_list_len = 1; /* XXX */ + + return &aml; +} + + +int * +amqproc_setopt_1_svc(voidp argp, struct svc_req *rqstp) +{ + static int rc; + amq_setopt *opt = (amq_setopt *) argp; + + rc = 0; + + switch (opt->as_opt) { + + case AMOPT_DEBUG: + if (debug_option(opt->as_str)) + rc = EINVAL; + break; + + case AMOPT_LOGFILE: + if (gopt.logfile && opt->as_str + && STREQ(gopt.logfile, opt->as_str)) { + if (switch_to_logfile(opt->as_str, orig_umask, 0)) + rc = EINVAL; + } else { + rc = EACCES; + } + break; + + case AMOPT_XLOG: + if (switch_option(opt->as_str)) + rc = EINVAL; + break; + + case AMOPT_FLUSHMAPC: + if (amd_state == Run) { + plog(XLOG_INFO, "amq says flush cache"); + do_mapc_reload = 0; + flush_nfs_fhandle_cache((fserver *) NULL); + flush_srvr_nfs_cache((fserver *) NULL); + } + break; + } + + return &rc; +} + + +amq_mount_info_list * +amqproc_getmntfs_1_svc(voidp argp, struct svc_req *rqstp) +{ + return (amq_mount_info_list *) ((void *)&mfhead); /* XXX */ +} + + +amq_string * +amqproc_getvers_1_svc(voidp argp, struct svc_req *rqstp) +{ + static amq_string res; + + res = get_version_string(); + return &res; +} + + +/* get PID of remote amd */ +int * +amqproc_getpid_1_svc(voidp argp, struct svc_req *rqstp) +{ + static int res; + + res = getpid(); + return &res; +} + + +/* + * Process PAWD string of remote pawd tool. + * + * We repeat the resolution of the string until the resolved string resolves + * to itself. This ensures that we follow path resolutions through all + * possible Amd mount points until we reach some sort of convergence. To + * prevent possible infinite loops, we break out of this loop if the strings + * do not converge after MAX_PAWD_TRIES times. + */ +amq_string * +amqproc_pawd_1_svc(voidp argp, struct svc_req *rqstp) +{ + static amq_string res; +#define MAX_PAWD_TRIES 10 + int index, len, maxagain = MAX_PAWD_TRIES; + am_node *mp; + char *mountpoint; + char *dir = *(char **) argp; + static char tmp_buf[MAXPATHLEN]; + char prev_buf[MAXPATHLEN]; + + tmp_buf[0] = prev_buf[0] = '\0'; /* default is empty string: no match */ + do { + for (mp = get_first_exported_ap(&index); + mp; + mp = get_next_exported_ap(&index)) { + if (STREQ(mp->am_mnt->mf_ops->fs_type, "toplvl")) + continue; + if (STREQ(mp->am_mnt->mf_ops->fs_type, "auto")) + continue; + mountpoint = (mp->am_link ? mp->am_link : mp->am_mnt->mf_mount); + len = strlen(mountpoint); + if (len == 0) + continue; + if (!NSTREQ(mountpoint, dir, len)) + continue; + if (dir[len] != '\0' && dir[len] != '/') + continue; + xstrlcpy(tmp_buf, mp->am_path, sizeof(tmp_buf)); + xstrlcat(tmp_buf, &dir[len], sizeof(tmp_buf)); + break; + } /* end of "for" loop */ + /* once tmp_buf and prev_buf are equal, break out of "do" loop */ + if (STREQ(tmp_buf, prev_buf)) + break; + else + xstrlcpy(prev_buf, tmp_buf, sizeof(prev_buf)); + } while (--maxagain); + /* check if we couldn't resolve the string after MAX_PAWD_TRIES times */ + if (maxagain <= 0) + plog(XLOG_WARNING, "path \"%s\" did not resolve after %d tries", + tmp_buf, MAX_PAWD_TRIES); + + res = tmp_buf; + return &res; +} + + +/* + * XDR routines. + */ + + +bool_t +xdr_amq_setopt(XDR *xdrs, amq_setopt *objp) +{ + if (!xdr_enum(xdrs, (enum_t *) ((voidp) &objp->as_opt))) { + return (FALSE); + } + if (!xdr_string(xdrs, &objp->as_str, AMQ_STRLEN)) { + return (FALSE); + } + return (TRUE); +} + + +/* + * More XDR routines - Should be used for OUTPUT ONLY. + */ +bool_t +xdr_amq_mount_tree_node(XDR *xdrs, amq_mount_tree *objp) +{ + am_node *mp = (am_node *) objp; + long mtime; + + if (!xdr_amq_string(xdrs, &mp->am_mnt->mf_info)) { + return (FALSE); + } + if (!xdr_amq_string(xdrs, &mp->am_path)) { + return (FALSE); + } + if (!xdr_amq_string(xdrs, mp->am_link ? &mp->am_link : &mp->am_mnt->mf_mount)) { + return (FALSE); + } + if (!xdr_amq_string(xdrs, &mp->am_mnt->mf_ops->fs_type)) { + return (FALSE); + } + mtime = mp->am_stats.s_mtime; + if (!xdr_long(xdrs, &mtime)) { + return (FALSE); + } + if (!xdr_u_short(xdrs, &mp->am_stats.s_uid)) { + return (FALSE); + } + if (!xdr_int(xdrs, &mp->am_stats.s_getattr)) { + return (FALSE); + } + if (!xdr_int(xdrs, &mp->am_stats.s_lookup)) { + return (FALSE); + } + if (!xdr_int(xdrs, &mp->am_stats.s_readdir)) { + return (FALSE); + } + if (!xdr_int(xdrs, &mp->am_stats.s_readlink)) { + return (FALSE); + } + if (!xdr_int(xdrs, &mp->am_stats.s_statfs)) { + return (FALSE); + } + return (TRUE); +} + + +bool_t +xdr_amq_mount_subtree(XDR *xdrs, amq_mount_tree *objp) +{ + am_node *mp = (am_node *) objp; + + if (!xdr_amq_mount_tree_node(xdrs, objp)) { + return (FALSE); + } + if (!xdr_pointer(xdrs, + (char **) ((voidp) &mp->am_osib), + sizeof(amq_mount_tree), + (XDRPROC_T_TYPE) xdr_amq_mount_subtree)) { + return (FALSE); + } + if (!xdr_pointer(xdrs, + (char **) ((voidp) &mp->am_child), + sizeof(amq_mount_tree), + (XDRPROC_T_TYPE) xdr_amq_mount_subtree)) { + return (FALSE); + } + return (TRUE); +} + + +bool_t +xdr_amq_mount_tree(XDR *xdrs, amq_mount_tree *objp) +{ + am_node *mp = (am_node *) objp; + am_node *mnil = NULL; + + if (!xdr_amq_mount_tree_node(xdrs, objp)) { + return (FALSE); + } + if (!xdr_pointer(xdrs, + (char **) ((voidp) &mnil), + sizeof(amq_mount_tree), + (XDRPROC_T_TYPE) xdr_amq_mount_subtree)) { + return (FALSE); + } + if (!xdr_pointer(xdrs, + (char **) ((voidp) &mp->am_child), + sizeof(amq_mount_tree), + (XDRPROC_T_TYPE) xdr_amq_mount_subtree)) { + return (FALSE); + } + return (TRUE); +} + + +bool_t +xdr_amq_mount_tree_p(XDR *xdrs, amq_mount_tree_p *objp) +{ + if (!xdr_pointer(xdrs, (char **) objp, sizeof(amq_mount_tree), (XDRPROC_T_TYPE) xdr_amq_mount_tree)) { + return (FALSE); + } + return (TRUE); +} + + +bool_t +xdr_amq_mount_stats(XDR *xdrs, amq_mount_stats *objp) +{ + if (!xdr_int(xdrs, &objp->as_drops)) { + return (FALSE); + } + if (!xdr_int(xdrs, &objp->as_stale)) { + return (FALSE); + } + if (!xdr_int(xdrs, &objp->as_mok)) { + return (FALSE); + } + if (!xdr_int(xdrs, &objp->as_merr)) { + return (FALSE); + } + if (!xdr_int(xdrs, &objp->as_uerr)) { + return (FALSE); + } + return (TRUE); +} + + + +bool_t +xdr_amq_mount_tree_list(XDR *xdrs, amq_mount_tree_list *objp) +{ + if (!xdr_array(xdrs, + (char **) ((voidp) &objp->amq_mount_tree_list_val), + (u_int *) &objp->amq_mount_tree_list_len, + ~0, + sizeof(amq_mount_tree_p), + (XDRPROC_T_TYPE) xdr_amq_mount_tree_p)) { + return (FALSE); + } + return (TRUE); +} + + + +/* + * Compute length of list + */ +bool_t +xdr_amq_mount_info_qelem(XDR *xdrs, qelem *qhead) +{ + mntfs *mf; + u_int len = 0; + + for (mf = AM_LAST(mntfs, qhead); mf != HEAD(mntfs, qhead); mf = PREV(mntfs, mf)) { + if (!(mf->mf_fsflags & FS_AMQINFO)) + continue; + len++; + } + xdr_u_int(xdrs, &len); + + /* + * Send individual data items + */ + for (mf = AM_LAST(mntfs, qhead); mf != HEAD(mntfs, qhead); mf = PREV(mntfs, mf)) { + int up; + if (!(mf->mf_fsflags & FS_AMQINFO)) + continue; + + if (!xdr_amq_string(xdrs, &mf->mf_ops->fs_type)) { + return (FALSE); + } + if (!xdr_amq_string(xdrs, &mf->mf_mount)) { + return (FALSE); + } + if (!xdr_amq_string(xdrs, &mf->mf_info)) { + return (FALSE); + } + if (!xdr_amq_string(xdrs, &mf->mf_server->fs_host)) { + return (FALSE); + } + if (!xdr_int(xdrs, &mf->mf_error)) { + return (FALSE); + } + if (!xdr_int(xdrs, &mf->mf_refc)) { + return (FALSE); + } + if (FSRV_ERROR(mf->mf_server) || FSRV_ISDOWN(mf->mf_server)) + up = 0; + else if (FSRV_ISUP(mf->mf_server)) + up = 1; + else + up = -1; + if (!xdr_int(xdrs, &up)) { + return (FALSE); + } + } + return (TRUE); +} + + +bool_t +xdr_pri_free(XDRPROC_T_TYPE xdr_args, caddr_t args_ptr) +{ + XDR xdr; + + xdr.x_op = XDR_FREE; + return ((*xdr_args) (&xdr, (caddr_t *) args_ptr)); +} diff --git a/amd/amq_svc.c b/amd/amq_svc.c new file mode 100644 index 000000000000..6fadb160c121 --- /dev/null +++ b/amd/amq_svc.c @@ -0,0 +1,229 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/amq_svc.c + * + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* typedefs */ +typedef char *(*amqsvcproc_t)(voidp, struct svc_req *); + +#if defined(HAVE_TCPD_H) && defined(HAVE_LIBWRAP) +# ifdef NEED_LIBWRAP_SEVERITY_VARIABLES +/* + * Some systems that define libwrap already define these two variables + * in libwrap, while others don't: so I need to know precisely iff + * to define these two severity variables. + */ +int allow_severity=0, deny_severity=0; +# endif /* NEED_LIBWRAP_SEVERITY_VARIABLES */ + +/* + * check if remote amq is authorized to access this amd. + * Returns: 1=allowed, 0=denied. + */ +static int +amqsvc_is_client_allowed(const struct sockaddr_in *addr, char *remote) +{ + struct hostent *h; + char *name = NULL, **ad; + int ret = 0; /* default is 0==denied */ + + /* Check IP address */ + if (hosts_ctl(AMD_SERVICE_NAME, "", remote, "")) { + ret = 1; + goto out; + } + /* Get address */ + if (!(h = gethostbyaddr((const char *)&(addr->sin_addr), + sizeof(addr->sin_addr), + AF_INET))) + goto out; + if (!(name = strdup(h->h_name))) + goto out; + /* Paranoia check */ + if (!(h = gethostbyname(name))) + goto out; + for (ad = h->h_addr_list; *ad; ad++) + if (!memcmp(*ad, &(addr->sin_addr), h->h_length)) + break; + if (!*ad) + goto out; + if (hosts_ctl(AMD_SERVICE_NAME, "", h->h_name, "")) { + return 1; + goto out; + } + /* Check aliases */ + for (ad = h->h_aliases; *ad; ad++) + if (hosts_ctl(AMD_SERVICE_NAME, "", *ad, "")) { + return 1; + goto out; + } + + out: + if (name) + XFREE(name); + return ret; +} +#endif /* defined(HAVE_TCPD_H) && defined(HAVE_LIBWRAP) */ + + +void +amq_program_1(struct svc_req *rqstp, SVCXPRT *transp) +{ + union { + amq_string amqproc_mnttree_1_arg; + amq_string amqproc_umnt_1_arg; + amq_setopt amqproc_setopt_1_arg; + } argument; + char *result; + xdrproc_t xdr_argument, xdr_result; + amqsvcproc_t local; + +#if defined(HAVE_TCPD_H) && defined(HAVE_LIBWRAP) + if (gopt.flags & CFM_USE_TCPWRAPPERS) { + struct sockaddr_in *remote_addr = svc_getcaller(rqstp->rq_xprt); + char *remote_hostname = inet_ntoa(remote_addr->sin_addr); + + if (!amqsvc_is_client_allowed(remote_addr, remote_hostname)) { + plog(XLOG_WARNING, "Amd denied remote amq service to %s", remote_hostname); + svcerr_auth(transp, AUTH_FAILED); + return; + } else { + dlog("Amd allowed remote amq service to %s", remote_hostname); + } + } +#endif /* defined(HAVE_TCPD_H) && defined(HAVE_LIBWRAP) */ + + switch (rqstp->rq_proc) { + + case AMQPROC_NULL: + xdr_argument = (xdrproc_t) xdr_void; + xdr_result = (xdrproc_t) xdr_void; + local = (amqsvcproc_t) amqproc_null_1_svc; + break; + + case AMQPROC_MNTTREE: + xdr_argument = (xdrproc_t) xdr_amq_string; + xdr_result = (xdrproc_t) xdr_amq_mount_tree_p; + local = (amqsvcproc_t) amqproc_mnttree_1_svc; + break; + + case AMQPROC_UMNT: + xdr_argument = (xdrproc_t) xdr_amq_string; + xdr_result = (xdrproc_t) xdr_void; + local = (amqsvcproc_t) amqproc_umnt_1_svc; + break; + + case AMQPROC_STATS: + xdr_argument = (xdrproc_t) xdr_void; + xdr_result = (xdrproc_t) xdr_amq_mount_stats; + local = (amqsvcproc_t) amqproc_stats_1_svc; + break; + + case AMQPROC_EXPORT: + xdr_argument = (xdrproc_t) xdr_void; + xdr_result = (xdrproc_t) xdr_amq_mount_tree_list; + local = (amqsvcproc_t) amqproc_export_1_svc; + break; + + case AMQPROC_SETOPT: + xdr_argument = (xdrproc_t) xdr_amq_setopt; + xdr_result = (xdrproc_t) xdr_int; + local = (amqsvcproc_t) amqproc_setopt_1_svc; + break; + + case AMQPROC_GETMNTFS: + xdr_argument = (xdrproc_t) xdr_void; + xdr_result = (xdrproc_t) xdr_amq_mount_info_qelem; + local = (amqsvcproc_t) amqproc_getmntfs_1_svc; + break; + + case AMQPROC_GETVERS: + xdr_argument = (xdrproc_t) xdr_void; + xdr_result = (xdrproc_t) xdr_amq_string; + local = (amqsvcproc_t) amqproc_getvers_1_svc; + break; + + case AMQPROC_GETPID: + xdr_argument = (xdrproc_t) xdr_void; + xdr_result = (xdrproc_t) xdr_int; + local = (amqsvcproc_t) amqproc_getpid_1_svc; + break; + + case AMQPROC_PAWD: + xdr_argument = (xdrproc_t) xdr_amq_string; + xdr_result = (xdrproc_t) xdr_amq_string; + local = (amqsvcproc_t) amqproc_pawd_1_svc; + break; + + default: + svcerr_noproc(transp); + return; + } + + memset((char *) &argument, 0, sizeof(argument)); + if (!svc_getargs(transp, + (XDRPROC_T_TYPE) xdr_argument, + (SVC_IN_ARG_TYPE) & argument)) { + svcerr_decode(transp); + return; + } + + result = (*local) (&argument, rqstp); + + if (result != NULL && !svc_sendreply(transp, + (XDRPROC_T_TYPE) xdr_result, + result)) { + svcerr_systemerr(transp); + } + + if (!svc_freeargs(transp, + (XDRPROC_T_TYPE) xdr_argument, + (SVC_IN_ARG_TYPE) & argument)) { + plog(XLOG_FATAL, "unable to free rpc arguments in amqprog_1"); + going_down(1); + } +} diff --git a/amd/autil.c b/amd/autil.c new file mode 100644 index 000000000000..a5ecd6d0d1f3 --- /dev/null +++ b/amd/autil.c @@ -0,0 +1,783 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/autil.c + * + */ + +/* + * utilities specified to amd, taken out of the older amd/util.c. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +int NumChildren = 0; /* number of children of primary amd */ +static char invalid_keys[] = "\"'!;@ \t\n"; + +/**************************************************************************** + *** MACROS *** + ****************************************************************************/ + +#ifdef HAVE_TRANSPORT_TYPE_TLI +# define PARENT_USLEEP_TIME 100000 /* 0.1 seconds */ +#endif /* HAVE_TRANSPORT_TYPE_TLI */ + + +/**************************************************************************** + *** FORWARD DEFINITIONS *** + ****************************************************************************/ +static void domain_strip(char *otherdom, char *localdom); +static int dofork(void); + + +/**************************************************************************** + *** FUNCTIONS *** + ****************************************************************************/ + +/* + * Copy s into p, reallocating p if necessary + */ +char * +strealloc(char *p, char *s) +{ + size_t len = strlen(s) + 1; + + p = (char *) xrealloc((voidp) p, len); + + xstrlcpy(p, s, len); +#ifdef DEBUG_MEM +# if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) + malloc_verify(); +# endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */ +#endif /* DEBUG_MEM */ + return p; +} + + +/* + * Strip off the trailing part of a domain + * to produce a short-form domain relative + * to the local host domain. + * Note that this has no effect if the domain + * names do not have the same number of + * components. If that restriction proves + * to be a problem then the loop needs recoding + * to skip from right to left and do partial + * matches along the way -- ie more expensive. + */ +static void +domain_strip(char *otherdom, char *localdom) +{ + char *p1, *p2; + + if ((p1 = strchr(otherdom, '.')) && + (p2 = strchr(localdom, '.')) && + STREQ(p1 + 1, p2 + 1)) + *p1 = '\0'; +} + + +/* + * Normalize a host name: replace cnames with real names, and decide if to + * strip domain name or not. + */ +void +host_normalize(char **chp) +{ + /* + * Normalize hosts is used to resolve host name aliases + * and replace them with the standard-form name. + * Invoked with "-n" command line option. + */ + if (gopt.flags & CFM_NORMALIZE_HOSTNAMES) { + struct hostent *hp; + hp = gethostbyname(*chp); + if (hp && hp->h_addrtype == AF_INET) { + dlog("Hostname %s normalized to %s", *chp, hp->h_name); + *chp = strealloc(*chp, (char *) hp->h_name); + } + } + if (gopt.flags & CFM_DOMAIN_STRIP) { + domain_strip(*chp, hostd); + } +} + + +/* + * Keys are not allowed to contain " ' ! or ; to avoid + * problems with macro expansions. + */ +int +valid_key(char *key) +{ + while (*key) + if (strchr(invalid_keys, *key++)) + return FALSE; + return TRUE; +} + + +void +forcibly_timeout_mp(am_node *mp) +{ + mntfs *mf = mp->am_mnt; + /* + * Arrange to timeout this node + */ + if (mf && ((mp->am_flags & AMF_ROOT) || + (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)))) { + if (mf->mf_flags & MFF_UNMOUNTING) + plog(XLOG_WARNING, "node %s is currently being unmounted, ignoring timeout request", mp->am_path); + else + plog(XLOG_WARNING, "ignoring timeout request for active node %s", mp->am_path); + } else { + plog(XLOG_INFO, "\"%s\" forcibly timed out", mp->am_path); + mp->am_flags &= ~AMF_NOTIMEOUT; + mp->am_ttl = clocktime(NULL); + /* + * Force mtime update of parent dir, to prevent DNLC/dcache from caching + * the old entry, which could result in ESTALE errors, bad symlinks, and + * more. + */ + clocktime(&mp->am_parent->am_fattr.na_mtime); + reschedule_timeout_mp(); + } +} + + +void +mf_mounted(mntfs *mf, bool_t call_free_opts) +{ + int quoted; + int wasmounted = mf->mf_flags & MFF_MOUNTED; + + if (!wasmounted) { + /* + * If this is a freshly mounted + * filesystem then update the + * mntfs structure... + */ + mf->mf_flags |= MFF_MOUNTED; + mf->mf_error = 0; + + /* + * Do mounted callback + */ + if (mf->mf_ops->mounted) + mf->mf_ops->mounted(mf); + + /* + * Be careful when calling free_ops and XFREE here. Some pseudo file + * systems like nfsx call this function (mf_mounted), even though it + * would be called by the lower-level amd file system functions. nfsx + * needs to call this function because of the other actions it takes. + * So we pass a boolean from the caller (yes, not so clean workaround) + * to determine if we should free or not. If we're not freeing (often + * because we're called from a callback function), then just to be sure, + * we'll zero out the am_opts structure and set the pointer to NULL. + * The parent mntfs node owns this memory and is going to free it with a + * call to mf_mounted(mntfs,TRUE) (see comment in the am_mounted code). + */ + if (call_free_opts) { + free_opts(mf->mf_fo); /* this free is needed to prevent leaks */ + XFREE(mf->mf_fo); /* (also this one) */ + } else { + memset(mf->mf_fo, 0, sizeof(am_opts)); + mf->mf_fo = NULL; + } + } + + if (mf->mf_flags & MFF_RESTART) { + mf->mf_flags &= ~MFF_RESTART; + dlog("Restarted filesystem %s, flags 0x%x", mf->mf_mount, mf->mf_flags); + } + + /* + * Log message + */ + quoted = strchr(mf->mf_info, ' ') != 0; + plog(XLOG_INFO, "%s%s%s %s fstype %s on %s", + quoted ? "\"" : "", + mf->mf_info, + quoted ? "\"" : "", + wasmounted ? "referenced" : "mounted", + mf->mf_ops->fs_type, mf->mf_mount); +} + + +void +am_mounted(am_node *mp) +{ + int notimeout = 0; /* assume normal timeouts initially */ + mntfs *mf = mp->am_mnt; + + /* + * This is the parent mntfs which does the mf->mf_fo (am_opts type), and + * we're passing TRUE here to tell mf_mounted to actually free the + * am_opts. See a related comment in mf_mounted(). + */ + mf_mounted(mf, TRUE); + +#ifdef HAVE_FS_AUTOFS + if (mf->mf_flags & MFF_IS_AUTOFS) + autofs_mounted(mp); +#endif /* HAVE_FS_AUTOFS */ + + /* + * Patch up path for direct mounts + */ + if (mp->am_parent && mp->am_parent->am_mnt->mf_fsflags & FS_DIRECT) + mp->am_path = str3cat(mp->am_path, mp->am_parent->am_path, "/", "."); + + /* + * Check whether this mount should be cached permanently or not, + * and handle user-requested timeouts. + */ + /* first check if file system was set to never timeout */ + if (mf->mf_fsflags & FS_NOTIMEOUT) + notimeout = 1; + /* next, alter that decision by map flags */ + if (mf->mf_mopts) { + mntent_t mnt; + mnt.mnt_opts = mf->mf_mopts; + + /* umount option: user wants to unmount this entry */ + if (amu_hasmntopt(&mnt, "unmount") || amu_hasmntopt(&mnt, "umount")) + notimeout = 0; + /* noumount option: user does NOT want to unmount this entry */ + if (amu_hasmntopt(&mnt, "nounmount") || amu_hasmntopt(&mnt, "noumount")) + notimeout = 1; + /* utimeout=N option: user wants to unmount this option AND set timeout */ + if ((mp->am_timeo = hasmntval(&mnt, "utimeout")) == 0) + mp->am_timeo = gopt.am_timeo; /* otherwise use default timeout */ + else + notimeout = 0; + /* special case: don't try to unmount "/" (it can never succeed) */ + if (mf->mf_mount[0] == '/' && mf->mf_mount[1] == '\0') + notimeout = 1; + } + /* finally set actual flags */ + if (notimeout) { + mp->am_flags |= AMF_NOTIMEOUT; + plog(XLOG_INFO, "%s set to never timeout", mp->am_path); + } else { + mp->am_flags &= ~AMF_NOTIMEOUT; + plog(XLOG_INFO, "%s set to timeout in %d seconds", mp->am_path, mp->am_timeo); + } + + /* + * If this node is a symlink then + * compute the length of the returned string. + */ + if (mp->am_fattr.na_type == NFLNK) + mp->am_fattr.na_size = strlen(mp->am_link ? mp->am_link : mf->mf_mount); + + /* + * Record mount time, and update am_stats at the same time. + */ + mp->am_stats.s_mtime = clocktime(&mp->am_fattr.na_mtime); + new_ttl(mp); + + /* + * Update mtime of parent node (copying "struct nfstime" in '=' below) + */ + if (mp->am_parent && mp->am_parent->am_mnt) + mp->am_parent->am_fattr.na_mtime = mp->am_fattr.na_mtime; + + /* + * This is ugly, but essentially unavoidable + * Sublinks must be treated separately as type==link + * when the base type is different. + */ + if (mp->am_link && mf->mf_ops != &amfs_link_ops) + amfs_link_ops.mount_fs(mp, mf); + + /* + * Now, if we can, do a reply to our client here + * to speed things up. + */ +#ifdef HAVE_FS_AUTOFS + if (mp->am_flags & AMF_AUTOFS) + autofs_mount_succeeded(mp); + else +#endif /* HAVE_FS_AUTOFS */ + nfs_quick_reply(mp, 0); + + /* + * Update stats + */ + amd_stats.d_mok++; +} + + +/* + * Replace mount point with a reference to an error filesystem. + * The mount point (struct mntfs) is NOT discarded, + * the caller must do it if it wants to _before_ calling this function. + */ +void +assign_error_mntfs(am_node *mp) +{ + int error; + dlog("assign_error_mntfs"); + /* + * Save the old error code + */ + error = mp->am_error; + if (error <= 0) + error = mp->am_mnt->mf_error; + /* + * Allocate a new error reference + */ + mp->am_mnt = new_mntfs(); + /* + * Put back the error code + */ + mp->am_mnt->mf_error = error; + mp->am_mnt->mf_flags |= MFF_ERROR; + /* + * Zero the error in the mount point + */ + mp->am_error = 0; +} + + +/* + * Build a new map cache for this node, or re-use + * an existing cache for the same map. + */ +void +amfs_mkcacheref(mntfs *mf) +{ + char *cache; + + if (mf->mf_fo && mf->mf_fo->opt_cache) + cache = mf->mf_fo->opt_cache; + else + cache = "none"; + mf->mf_private = (opaque_t) mapc_find(mf->mf_info, + cache, + (mf->mf_fo ? mf->mf_fo->opt_maptype : NULL)); + mf->mf_prfree = mapc_free; +} + + +/* + * Locate next node in sibling list which is mounted + * and is not an error node. + */ +am_node * +next_nonerror_node(am_node *xp) +{ + mntfs *mf; + + /* + * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no> + * Fixes a race condition when mounting direct automounts. + * Also fixes a problem when doing a readdir on a directory + * containing hung automounts. + */ + while (xp && + (!(mf = xp->am_mnt) || /* No mounted filesystem */ + mf->mf_error != 0 || /* There was a mntfs error */ + xp->am_error != 0 || /* There was a mount error */ + !(mf->mf_flags & MFF_MOUNTED) || /* The fs is not mounted */ + (mf->mf_server->fs_flags & FSF_DOWN)) /* The fs may be down */ + ) + xp = xp->am_osib; + + return xp; +} + + +/* + * Mount an automounter directory. + * The automounter is connected into the system + * as a user-level NFS server. amfs_mount constructs + * the necessary NFS parameters to be given to the + * kernel so that it will talk back to us. + * + * NOTE: automounter mounts in themselves are using NFS Version 2 (UDP). + * + * NEW: on certain systems, mounting can be done using the + * kernel-level automount (autofs) support. In that case, + * we don't need NFS at all here. + */ +int +amfs_mount(am_node *mp, mntfs *mf, char *opts) +{ + char fs_hostname[MAXHOSTNAMELEN + MAXPATHLEN + 1]; + int retry, error = 0, genflags; + int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; + char *dir = mf->mf_mount; + mntent_t mnt; + MTYPE_TYPE type; + int forced_unmount = 0; /* are we using forced unmounts? */ + + memset((voidp) &mnt, 0, sizeof(mnt)); + mnt.mnt_dir = dir; + mnt.mnt_fsname = pid_fsname; + mnt.mnt_opts = opts; + +#ifdef HAVE_FS_AUTOFS + if (mf->mf_flags & MFF_IS_AUTOFS) { + type = MOUNT_TYPE_AUTOFS; + /* + * Make sure that amd's top-level autofs mounts are hidden by default + * from df. + * XXX: It works ok on Linux, might not work on other systems. + */ + mnt.mnt_type = "autofs"; + } else +#endif /* HAVE_FS_AUTOFS */ + { + type = MOUNT_TYPE_NFS; + /* + * Make sure that amd's top-level NFS mounts are hidden by default + * from df. + * If they don't appear to support the either the "ignore" mnttab + * option entry, or the "auto" one, set the mount type to "nfs". + */ + mnt.mnt_type = HIDE_MOUNT_TYPE; + } + + retry = hasmntval(&mnt, MNTTAB_OPT_RETRY); + if (retry <= 0) + retry = 2; /* XXX: default to 2 retries */ + + /* + * SET MOUNT ARGS + */ + + /* + * Make a ``hostname'' string for the kernel + */ + xsnprintf(fs_hostname, sizeof(fs_hostname), "pid%ld@%s:%s", + get_server_pid(), am_get_hostname(), dir); + /* + * Most kernels have a name length restriction (64 bytes)... + */ + if (strlen(fs_hostname) >= MAXHOSTNAMELEN) + xstrlcpy(fs_hostname + MAXHOSTNAMELEN - 3, "..", + sizeof(fs_hostname) - MAXHOSTNAMELEN + 3); +#ifdef HOSTNAMESZ + /* + * ... and some of these restrictions are 32 bytes (HOSTNAMESZ) + * If you need to get the definition for HOSTNAMESZ found, you may + * add the proper header file to the conf/nfs_prot/nfs_prot_*.h file. + */ + if (strlen(fs_hostname) >= HOSTNAMESZ) + xstrlcpy(fs_hostname + HOSTNAMESZ - 3, "..", + sizeof(fs_hostname) - HOSTNAMESZ + 3); +#endif /* HOSTNAMESZ */ + + /* + * Finally we can compute the mount genflags set above, + * and add any automounter specific flags. + */ + genflags = compute_mount_flags(&mnt); +#ifdef HAVE_FS_AUTOFS + if (on_autofs) + genflags |= autofs_compute_mount_flags(&mnt); +#endif /* HAVE_FS_AUTOFS */ + genflags |= compute_automounter_mount_flags(&mnt); + +again: + if (!(mf->mf_flags & MFF_IS_AUTOFS)) { + nfs_args_t nfs_args; + am_nfs_fh *fhp; + am_nfs_handle_t anh; +#ifndef HAVE_TRANSPORT_TYPE_TLI + u_short port; + struct sockaddr_in sin; +#endif /* not HAVE_TRANSPORT_TYPE_TLI */ + + /* + * get fhandle of remote path for automount point + */ + fhp = get_root_nfs_fh(dir); + if (!fhp) { + plog(XLOG_FATAL, "Can't find root file handle for %s", dir); + return EINVAL; + } + +#ifndef HAVE_TRANSPORT_TYPE_TLI + /* + * Create sockaddr to point to the local machine. + */ + memset((voidp) &sin, 0, sizeof(sin)); + /* as per POSIX, sin_len need not be set (used internally by kernel) */ + sin.sin_family = AF_INET; + sin.sin_addr = myipaddr; + port = hasmntval(&mnt, MNTTAB_OPT_PORT); + if (port) { + sin.sin_port = htons(port); + } else { + plog(XLOG_ERROR, "no port number specified for %s", dir); + return EINVAL; + } +#endif /* not HAVE_TRANSPORT_TYPE_TLI */ + + /* setup the many fields and flags within nfs_args */ + memmove(&anh.v2, fhp, sizeof(*fhp)); +#ifdef HAVE_TRANSPORT_TYPE_TLI + compute_nfs_args(&nfs_args, + &mnt, + genflags, + nfsncp, + NULL, /* remote host IP addr is set below */ + NFS_VERSION, /* version 2 */ + "udp", + &anh, + fs_hostname, + pid_fsname); + /* + * IMPORTANT: set the correct IP address AFTERWARDS. It cannot + * be done using the normal mechanism of compute_nfs_args(), because + * that one will allocate a new address and use NFS_SA_DREF() to copy + * parts to it, while assuming that the ip_addr passed is always + * a "struct sockaddr_in". That assumption is incorrect on TLI systems, + * because they define a special macro HOST_SELF which is DIFFERENT + * than localhost (127.0.0.1)! + */ + nfs_args.addr = &nfsxprt->xp_ltaddr; +#else /* not HAVE_TRANSPORT_TYPE_TLI */ + compute_nfs_args(&nfs_args, + &mnt, + genflags, + NULL, + &sin, + NFS_VERSION, /* version 2 */ + "udp", + &anh, + fs_hostname, + pid_fsname); +#endif /* not HAVE_TRANSPORT_TYPE_TLI */ + + /************************************************************************* + * NOTE: while compute_nfs_args() works ok for regular NFS mounts * + * the toplvl one is not quite regular, and so some options must be * + * corrected by hand more carefully, *after* compute_nfs_args() runs. * + *************************************************************************/ + compute_automounter_nfs_args(&nfs_args, &mnt); + + if (amuDebug(D_TRACE)) { + print_nfs_args(&nfs_args, 0); + plog(XLOG_DEBUG, "Generic mount flags 0x%x", genflags); + } + + /* This is it! Here we try to mount amd on its mount points */ + error = mount_fs(&mnt, genflags, (caddr_t) &nfs_args, + retry, type, 0, NULL, mnttab_file_name, on_autofs); + +#ifdef HAVE_TRANSPORT_TYPE_TLI + free_knetconfig(nfs_args.knconf); + /* + * local automounter mounts do not allocate a special address, so + * no need to XFREE(nfs_args.addr) under TLI. + */ +#endif /* HAVE_TRANSPORT_TYPE_TLI */ + +#ifdef HAVE_FS_AUTOFS + } else { + /* This is it! Here we try to mount amd on its mount points */ + error = mount_fs(&mnt, genflags, (caddr_t) mp->am_autofs_fh, + retry, type, 0, NULL, mnttab_file_name, on_autofs); +#endif /* HAVE_FS_AUTOFS */ + } + if (error == 0 || forced_unmount) + return error; + + /* + * If user wants forced/lazy unmount semantics, then try it iff the + * current mount failed with EIO or ESTALE. + */ + if (gopt.flags & CFM_FORCED_UNMOUNTS) { + switch (errno) { + case ESTALE: + case EIO: + forced_unmount = errno; + plog(XLOG_WARNING, "Mount %s failed (%m); force unmount.", mp->am_path); + if ((error = UMOUNT_FS(mp->am_path, mnttab_file_name, + AMU_UMOUNT_FORCE | AMU_UMOUNT_DETACH)) < 0) { + plog(XLOG_WARNING, "Forced umount %s failed: %m.", mp->am_path); + errno = forced_unmount; + } else + goto again; + default: + break; + } + } + + return error; +} + + +void +am_unmounted(am_node *mp) +{ + mntfs *mf = mp->am_mnt; + + if (!foreground) /* firewall - should never happen */ + return; + + /* + * Do unmounted callback + */ + if (mf->mf_ops->umounted) + mf->mf_ops->umounted(mf); + + /* + * This is ugly, but essentially unavoidable. + * Sublinks must be treated separately as type==link + * when the base type is different. + */ + if (mp->am_link && mf->mf_ops != &amfs_link_ops) + amfs_link_ops.umount_fs(mp, mf); + +#ifdef HAVE_FS_AUTOFS + if (mf->mf_flags & MFF_IS_AUTOFS) + autofs_release_fh(mp); + if (mp->am_flags & AMF_AUTOFS) + autofs_umount_succeeded(mp); +#endif /* HAVE_FS_AUTOFS */ + + /* + * Clean up any directories that were made + * + * If we remove the mount point of a pending mount, any queued access + * to it will fail. So don't do it in that case. + * Also don't do it if the refcount is > 1. + */ + if (mf->mf_flags & MFF_MKMNT && + mf->mf_refc == 1 && + !(mp->am_flags & AMF_REMOUNT)) { + plog(XLOG_INFO, "removing mountpoint directory '%s'", mf->mf_mount); + rmdirs(mf->mf_mount); + mf->mf_flags &= ~MFF_MKMNT; + } + + /* + * If this is a pseudo-directory then adjust the link count + * in the parent + */ + if (mp->am_parent && mp->am_fattr.na_type == NFDIR) + --mp->am_parent->am_fattr.na_nlink; + + /* + * Update mtime of parent node + */ + if (mp->am_parent && mp->am_parent->am_mnt) + clocktime(&mp->am_parent->am_fattr.na_mtime); + + if (mp->am_parent && (mp->am_flags & AMF_REMOUNT)) { + char *fname = strdup(mp->am_name); + am_node *mp_parent = mp->am_parent; + mntfs *mf_parent = mp_parent->am_mnt; + int error = 0; + + free_map(mp); + plog(XLOG_INFO, "am_unmounted: remounting %s", fname); + mp = mf_parent->mf_ops->lookup_child(mp_parent, fname, &error, VLOOK_CREATE); + if (mp && error < 0) + mp = mf_parent->mf_ops->mount_child(mp, &error); + if (error > 0) { + errno = error; + plog(XLOG_ERROR, "am_unmounted: could not remount %s: %m", fname); + } + XFREE(fname); + } else + /* + * We have a race here. + * If this node has a pending mount and amd is going down (unmounting + * everything in the process), then we could potentially free it here + * while a struct continuation still has a reference to it. So when + * amfs_cont is called, it blows up. + * We avoid the race by refusing to free any nodes that have + * pending mounts (defined as having a non-NULL am_mfarray). + */ + if (!mp->am_mfarray) + free_map(mp); +} + + +/* + * Fork the automounter + * + * TODO: Need a better strategy for handling errors + */ +static int +dofork(void) +{ + int pid; + +top: + pid = fork(); + + if (pid < 0) { /* fork error, retry in 1 second */ + sleep(1); + goto top; + } + if (pid == 0) { /* child process (foreground==false) */ + am_set_mypid(); + foreground = 0; + } else { /* parent process, has one more child */ + NumChildren++; + } + + return pid; +} + + +int +background(void) +{ + int pid = dofork(); + + if (pid == 0) { + dlog("backgrounded"); + foreground = 0; + } else + dlog("forked process %d", pid); + return pid; +} diff --git a/amd/clock.c b/amd/clock.c new file mode 100644 index 000000000000..0caa7dff2c73 --- /dev/null +++ b/amd/clock.c @@ -0,0 +1,244 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * 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. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/clock.c + * + */ + +/* + * Callouts. + * + * Modeled on kernel object of the same name. + * See usual references. + * + * Use of a heap-based mechanism was rejected: + * 1. more complex implementation needed. + * 2. not obvious that a list is too slow for Amd. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +void reschedule_timeouts(time_t now, time_t then); + +typedef struct callout callout; +struct callout { + callout *c_next; /* List of callouts */ + callout_fun *c_fn; /* Function to call */ + opaque_t c_arg; /* Argument to pass to call */ + time_t c_time; /* Time of call */ + int c_id; /* Unique identifier */ +}; + +static callout callouts; /* List of pending callouts */ +static callout *free_callouts; /* Cache of free callouts */ +static int nfree_callouts; /* Number on free list */ +static int callout_id; /* Next free callout identifier */ + +time_t next_softclock; /* Time of next call to softclock() */ + + +/* + * Number of callout slots we keep on the free list + */ +#define CALLOUT_FREE_SLOP 10 + +/* + * Global assumption: valid id's are non-zero. + */ +#define CID_ALLOC() (++callout_id) +#define CID_UNDEF (0) + + +static callout * +alloc_callout(void) +{ + callout *cp = free_callouts; + + if (cp) { + --nfree_callouts; + free_callouts = free_callouts->c_next; + return cp; + } + return ALLOC(struct callout); +} + + +static void +free_callout(callout *cp) +{ + if (nfree_callouts > CALLOUT_FREE_SLOP) { + XFREE(cp); + } else { + cp->c_next = free_callouts; + free_callouts = cp; + nfree_callouts++; + } +} + + +/* + * Schedule a callout. + * + * (*fn)(fn_arg) will be called at clocktime(NULL) + secs + */ +int +timeout(u_int secs, callout_fun *fn, opaque_t fn_arg) +{ + callout *cp, *cp2; + time_t t = clocktime(NULL) + secs; + + /* + * Allocate and fill in a new callout structure + */ + callout *cpnew = alloc_callout(); + cpnew->c_arg = fn_arg; + cpnew->c_fn = fn; + cpnew->c_time = t; + cpnew->c_id = CID_ALLOC(); + + if (t < next_softclock) + next_softclock = t; + + /* + * Find the correct place in the list + */ + for (cp = &callouts; (cp2 = cp->c_next); cp = cp2) + if (cp2->c_time >= t) + break; + + /* + * And link it in + */ + cp->c_next = cpnew; + cpnew->c_next = cp2; + + /* + * Return callout identifier + */ + return cpnew->c_id; +} + + +/* + * De-schedule a callout + */ +void +untimeout(int id) +{ + callout *cp, *cp2; + for (cp = &callouts; (cp2 = cp->c_next); cp = cp2) { + if (cp2->c_id == id) { + cp->c_next = cp2->c_next; + free_callout(cp2); + break; + } + } +} + + +/* + * Reschedule after clock changed + */ +void +reschedule_timeouts(time_t now, time_t then) +{ + callout *cp; + + for (cp = callouts.c_next; cp; cp = cp->c_next) { + if (cp->c_time >= now && cp->c_time <= then) { + plog(XLOG_WARNING, "job %d rescheduled to run immediately", cp->c_id); + dlog("rescheduling job %d back %ld seconds", + cp->c_id, (long) (cp->c_time - now)); + next_softclock = cp->c_time = now; + } + } +} + + +/* + * Clock handler + */ +int +softclock(void) +{ + time_t now; + callout *cp; + + do { + if (task_notify_todo) + do_task_notify(); + + now = clocktime(NULL); + + /* + * While there are more callouts waiting... + */ + while ((cp = callouts.c_next) && cp->c_time <= now) { + /* + * Extract first from list, save fn & fn_arg and + * unlink callout from list and free. + * Finally call function. + * + * The free is done first because + * it is quite common that the + * function will call timeout() + * and try to allocate a callout + */ + callout_fun *fn = cp->c_fn; + opaque_t fn_arg = cp->c_arg; + + callouts.c_next = cp->c_next; + free_callout(cp); + (*fn) (fn_arg); + } + + } while (task_notify_todo); + + /* + * Return number of seconds to next event, + * or 0 if there is no event. + */ + if ((cp = callouts.c_next)) + return cp->c_time - now; + return 0; +} diff --git a/amd/conf.c b/amd/conf.c new file mode 100644 index 000000000000..2fc587b0cadb --- /dev/null +++ b/amd/conf.c @@ -0,0 +1,1406 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/conf.c + * + */ + +/* + * Functions to handle the configuration file. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + + +/* + * MACROS: + */ +/* Turn on to show some info about maps being configured */ +/* #define DEBUG_CONF */ + +/* + * TYPEDEFS: + */ +typedef int (*OptFuncPtr)(const char *); + +/* + * STRUCTURES: + */ +struct _func_map { + char *name; + OptFuncPtr func; +}; + +/* + * FORWARD DECLARATIONS: + */ +static int gopt_arch(const char *val); +static int gopt_auto_attrcache(const char *val); +static int gopt_auto_dir(const char *val); +static int gopt_autofs_use_lofs(const char *val); +static int gopt_browsable_dirs(const char *val); +static int gopt_cache_duration(const char *val); +static int gopt_cluster(const char *val); +static int gopt_debug_mtab_file(const char *val); +static int gopt_debug_options(const char *val); +static int gopt_dismount_interval(const char *val); +static int gopt_domain_strip(const char *val); +static int gopt_exec_map_timeout(const char *val); +static int gopt_forced_unmounts(const char *val); +static int gopt_full_os(const char *val); +static int gopt_fully_qualified_hosts(const char *val); +static int gopt_hesiod_base(const char *val); +static int gopt_karch(const char *val); +static int gopt_ldap_base(const char *val); +static int gopt_ldap_cache_maxmem(const char *val); +static int gopt_ldap_cache_seconds(const char *val); +static int gopt_ldap_hostports(const char *val); +static int gopt_ldap_proto_version(const char *val); +static int gopt_local_domain(const char *val); +static int gopt_localhost_address(const char *val); +static int gopt_log_file(const char *val); +static int gopt_log_options(const char *val); +static int gopt_map_defaults(const char *val); +static int gopt_map_options(const char *val); +static int gopt_map_reload_interval(const char *val); +static int gopt_map_type(const char *val); +static int gopt_mount_type(const char *val); +static int gopt_pid_file(const char *val); +static int gopt_portmap_program(const char *val); +static int gopt_preferred_amq_port(const char *val); +static int gopt_nfs_allow_any_interface(const char *val); +static int gopt_nfs_allow_insecure_port(const char *val); +static int gopt_nfs_proto(const char *val); +static int gopt_nfs_retransmit_counter(const char *val); +static int gopt_nfs_retransmit_counter_udp(const char *val); +static int gopt_nfs_retransmit_counter_tcp(const char *val); +static int gopt_nfs_retransmit_counter_toplvl(const char *val); +static int gopt_nfs_retry_interval(const char *val); +static int gopt_nfs_retry_interval_udp(const char *val); +static int gopt_nfs_retry_interval_tcp(const char *val); +static int gopt_nfs_retry_interval_toplvl(const char *val); +static int gopt_nfs_vers(const char *val); +static int gopt_nis_domain(const char *val); +static int gopt_normalize_hostnames(const char *val); +static int gopt_normalize_slashes(const char *val); +static int gopt_os(const char *val); +static int gopt_osver(const char *val); +static int gopt_plock(const char *val); +static int gopt_print_pid(const char *val); +static int gopt_print_version(const char *val); +static int gopt_restart_mounts(const char *val); +static int gopt_search_path(const char *val); +static int gopt_selectors_in_defaults(const char *val); +static int gopt_show_statfs_entries(const char *val); +static int gopt_truncate_log(const char *val); +static int gopt_unmount_on_exit(const char *val); +static int gopt_use_tcpwrappers(const char *val); +static int gopt_vendor(const char *val); +static int process_global_option(const char *key, const char *val); +static int process_one_regular_map(const cf_map_t *cfm); +static int process_regular_option(const char *section, const char *key, const char *val, cf_map_t *cfm); +static int ropt_browsable_dirs(const char *val, cf_map_t *cfm); +static int ropt_map_name(const char *val, cf_map_t *cfm); +static int ropt_map_defaults(const char *val, cf_map_t *cfm); +static int ropt_map_options(const char *val, cf_map_t *cfm); +static int ropt_map_type(const char *val, cf_map_t *cfm); +static int ropt_mount_type(const char *val, cf_map_t *cfm); +static int ropt_search_path(const char *val, cf_map_t *cfm); +static int ropt_tag(const char *val, cf_map_t *cfm); +static void init_cf_map(cf_map_t *cfm); + + +/* + * STATIC VARIABLES: + */ +static cf_map_t *head_map, *cur_map; + +static struct _func_map glob_functable[] = { + {"arch", gopt_arch}, + {"auto_attrcache", gopt_auto_attrcache}, + {"auto_dir", gopt_auto_dir}, + {"autofs_use_lofs", gopt_autofs_use_lofs}, + {"browsable_dirs", gopt_browsable_dirs}, + {"cache_duration", gopt_cache_duration}, + {"cluster", gopt_cluster}, + {"debug_mtab_file", gopt_debug_mtab_file}, + {"debug_options", gopt_debug_options}, + {"dismount_interval", gopt_dismount_interval}, + {"domain_strip", gopt_domain_strip}, + {"exec_map_timeout", gopt_exec_map_timeout}, + {"forced_unmounts", gopt_forced_unmounts}, + {"fully_qualified_hosts", gopt_fully_qualified_hosts}, + {"full_os", gopt_full_os}, + {"hesiod_base", gopt_hesiod_base}, + {"karch", gopt_karch}, + {"ldap_base", gopt_ldap_base}, + {"ldap_cache_maxmem", gopt_ldap_cache_maxmem}, + {"ldap_cache_seconds", gopt_ldap_cache_seconds}, + {"ldap_hostports", gopt_ldap_hostports}, + {"ldap_proto_version", gopt_ldap_proto_version}, + {"local_domain", gopt_local_domain}, + {"localhost_address", gopt_localhost_address}, + {"log_file", gopt_log_file}, + {"log_options", gopt_log_options}, + {"map_defaults", gopt_map_defaults}, + {"map_options", gopt_map_options}, + {"map_reload_interval", gopt_map_reload_interval}, + {"map_type", gopt_map_type}, + {"mount_type", gopt_mount_type}, + {"pid_file", gopt_pid_file}, + {"portmap_program", gopt_portmap_program}, + {"preferred_amq_port", gopt_preferred_amq_port}, + {"nfs_allow_any_interface", gopt_nfs_allow_any_interface}, + {"nfs_allow_insecure_port", gopt_nfs_allow_insecure_port}, + {"nfs_proto", gopt_nfs_proto}, + {"nfs_retransmit_counter", gopt_nfs_retransmit_counter}, + {"nfs_retransmit_counter_udp", gopt_nfs_retransmit_counter_udp}, + {"nfs_retransmit_counter_tcp", gopt_nfs_retransmit_counter_tcp}, + {"nfs_retransmit_counter_toplvl", gopt_nfs_retransmit_counter_toplvl}, + {"nfs_retry_interval", gopt_nfs_retry_interval}, + {"nfs_retry_interval_udp", gopt_nfs_retry_interval_udp}, + {"nfs_retry_interval_tcp", gopt_nfs_retry_interval_tcp}, + {"nfs_retry_interval_toplvl", gopt_nfs_retry_interval_toplvl}, + {"nfs_vers", gopt_nfs_vers}, + {"nis_domain", gopt_nis_domain}, + {"normalize_hostnames", gopt_normalize_hostnames}, + {"normalize_slashes", gopt_normalize_slashes}, + {"os", gopt_os}, + {"osver", gopt_osver}, + {"plock", gopt_plock}, + {"print_pid", gopt_print_pid}, + {"print_version", gopt_print_version}, + {"restart_mounts", gopt_restart_mounts}, + {"search_path", gopt_search_path}, + {"selectors_on_default", gopt_selectors_in_defaults}, + {"selectors_in_defaults", gopt_selectors_in_defaults}, + {"show_statfs_entries", gopt_show_statfs_entries}, + {"truncate_log", gopt_truncate_log}, + {"unmount_on_exit", gopt_unmount_on_exit}, + {"use_tcpwrappers", gopt_use_tcpwrappers}, + {"vendor", gopt_vendor}, + {NULL, NULL} +}; + + +/* + * Initialize a map from [global] defaults. + */ +static void +init_cf_map(cf_map_t *cfm) +{ + if (!cfm) + return; + + /* + * Initialize a regular map's flags and other variables from the + * global ones, so that they are applied to all maps. Of course, each map + * can then override the flags individually. + * + * NOTES: + * (1): Will only work for maps that appear after [global]. + * (2): I'm assigning pointers directly from the global map. + */ + + /* initialize map_type from [global] */ + cfm->cfm_type = gopt.map_type; + + /* initialize map_defaults from [global] */ + cfm->cfm_defaults = gopt.map_defaults; + + /* initialize map_opts from [global] */ + cfm->cfm_opts = gopt.map_options; + + /* initialize search_path from [global] */ + cfm->cfm_search_path = gopt.search_path; + + /* + * Initialize flags that are common both to [global] and a local map. + */ + cfm->cfm_flags = gopt.flags & (CFM_BROWSABLE_DIRS | + CFM_BROWSABLE_DIRS_FULL | + CFM_MOUNT_TYPE_AUTOFS | + CFM_SELECTORS_IN_DEFAULTS); +} + + +/* + * Process configuration file options (called from YACC parser). + * Return 0 if OK, 1 otherwise. + */ +int +set_conf_kv(const char *section, const char *key, const char *val) +{ + int ret; + +#ifdef DEBUG_CONF + fprintf(stderr, "set_conf_kv: section=%s, key=%s, val=%s\n", + section, key, val); +#endif /* DEBUG_CONF */ + + /* + * If global section, process kv pairs one at a time. + */ + if (STREQ(section, "global")) { + /* + * Check if a regular map was configured before "global", + * and warn about it. + */ + if (cur_map && cur_map->cfm_dir) { + static short printed_this_error; + if (!printed_this_error) { + fprintf(stderr, "found regular map \"%s\" before global one.\n", + cur_map->cfm_dir); + printed_this_error = 1; + } + } + + /* process the global option first */ + ret = process_global_option(key, val); + + /* return status from the processing of the global option */ + return ret; + } + + /* + * Otherwise we found a non-global option: store it after some testing. + */ + + /* initialize (static) global list head and current map pointer */ + if (!head_map && !cur_map) { + cur_map = CALLOC(cf_map_t); + if (!cur_map) { + perror("calloc"); + exit(1); + } + /* initialize first head map from global defaults */ + init_cf_map(cur_map); + head_map = cur_map; + } + + /* check if we found a new map, then allocate and initialize it */ + if (cur_map->cfm_dir && !STREQ(cur_map->cfm_dir, section)) { + /* allocate new map struct */ + cf_map_t *tmp_map = CALLOC(cf_map_t); + if (!tmp_map) { + perror("calloc"); + exit(1); + } + /* initialize it from global defaults */ + init_cf_map(tmp_map); + /* append it to end of linked list */ + cur_map->cfm_next = tmp_map; + cur_map = tmp_map; + } + + /* now process a single entry of a regular map */ + return process_regular_option(section, key, val, cur_map); +} + + +/* + * 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 int +gopt_arch(const char *val) +{ + gopt.arch = strdup((char *)val); + return 0; +} + + +static int +gopt_auto_attrcache(const char *val) +{ + gopt.auto_attrcache = atoi(val); + if (gopt.auto_attrcache < 0) { + fprintf(stderr, "conf: bad attrcache value: \"%s\"\n", val); + return 1; + } + return 0; +} + + +static int +gopt_auto_dir(const char *val) +{ + gopt.auto_dir = strdup((char *)val); + return 0; +} + + +static int +gopt_autofs_use_lofs(const char *val) +{ + if (STREQ(val, "yes")) { + gopt.flags |= CFM_AUTOFS_USE_LOFS; + return 0; + } else if (STREQ(val, "no")) { + gopt.flags &= ~CFM_AUTOFS_USE_LOFS; + return 0; + } + + fprintf(stderr, "conf: unknown value to autofs_use_lofs \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int +gopt_browsable_dirs(const char *val) +{ + if (STREQ(val, "full")) { + gopt.flags |= CFM_BROWSABLE_DIRS_FULL; + return 0; + } else if (STREQ(val, "yes")) { + gopt.flags |= CFM_BROWSABLE_DIRS; + return 0; + } else if (STREQ(val, "no")) { + gopt.flags &= ~CFM_BROWSABLE_DIRS; + return 0; + } + + fprintf(stderr, "conf: unknown value to browsable_dirs \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int +gopt_cache_duration(const char *val) +{ + gopt.am_timeo = atoi(val); + if (gopt.am_timeo <= 0) + gopt.am_timeo = AM_TTL; + return 0; +} + + +static int +gopt_cluster(const char *val) +{ + gopt.cluster = strdup((char *)val); + return 0; +} + + +static int +gopt_debug_mtab_file(const char *val) +{ + gopt.debug_mtab_file = strdup((char*)val); + return 0; +} + + +static int +gopt_debug_options(const char *val) +{ +#ifdef DEBUG + usage += debug_option((char *)val); + return 0; +#else /* not DEBUG */ + fprintf(stderr, "%s: not compiled with DEBUG option -- sorry.\n", + am_get_progname()); + return 1; +#endif /* not DEBUG */ +} + + +static int +gopt_dismount_interval(const char *val) +{ + gopt.am_timeo_w = atoi(val); + if (gopt.am_timeo_w <= 0) + gopt.am_timeo_w = AM_TTL_W; + return 0; +} + + +static int +gopt_domain_strip(const char *val) +{ + if (STREQ(val, "yes")) { + gopt.flags |= CFM_DOMAIN_STRIP; + return 0; + } else if (STREQ(val, "no")) { + gopt.flags &= ~CFM_DOMAIN_STRIP; + return 0; + } + + fprintf(stderr, "conf: unknown value to domain_strip \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int +gopt_exec_map_timeout(const char *val) +{ + gopt.exec_map_timeout = atoi(val); + if (gopt.exec_map_timeout <= 0) + gopt.exec_map_timeout = AMFS_EXEC_MAP_TIMEOUT; /* default exec map timeout */ + return 0; +} + + +static int +gopt_forced_unmounts(const char *val) +{ + if (STREQ(val, "yes")) { +#if !defined(MNT2_GEN_OPT_DETACH) && !defined(MNT2_GEN_OPT_FORCE) + fprintf(stderr, "conf: forced_unmounts unsupported on this system.\n"); + return 1; +#else /* defined(MNT2_GEN_OPT_DETACH) || defined(MNT2_GEN_OPT_FORCE) */ +# ifdef __linux__ + /* + * HACK ALERT: Linux has had MNT_FORCE since 2.2, but it hasn't gotten + * stable until 2.4. And it had MNT_DETACH since 2.4, but it hasn't + * gotten stable since 2.6. So alert users if they're trying to use a + * feature that may not work well on their older kernel. + */ + { + struct utsname un; + if (uname(&un) >= 0) { +# ifdef MNT2_GEN_OPT_FORCE + if (strcmp(un.release, "2.4.0") < 0) + fprintf(stderr, "warning: forced-unmounts (MNT_FORCE) may not work well before 2.4.0\n"); +# endif /* MNT2_GEN_OPT_FORCE */ +# ifdef MNT2_GEN_OPT_DETACH + if (strcmp(un.release, "2.6.0") < 0) + fprintf(stderr, "warning: lazy-unmounts (MNT_DETACH) may not work well before 2.6.0\n"); +# endif /* MNT2_GEN_OPT_DETACH */ + } + } +# endif /* __linux__ */ + gopt.flags |= CFM_FORCED_UNMOUNTS; + return 0; +#endif /* defined(MNT2_GEN_OPT_DETACH) || defined(MNT2_GEN_OPT_FORCE) */ + } else if (STREQ(val, "no")) { + gopt.flags &= ~CFM_FORCED_UNMOUNTS; + return 0; + } + + fprintf(stderr, "conf: unknown value to unmount_on_exit \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int +gopt_full_os(const char *val) +{ + gopt.op_sys_full = strdup((char *)val); + return 0; +} + + +static int +gopt_fully_qualified_hosts(const char *val) +{ + if (STREQ(val, "yes")) { + gopt.flags |= CFM_FULLY_QUALIFIED_HOSTS; + return 0; + } else if (STREQ(val, "no")) { + gopt.flags &= ~CFM_FULLY_QUALIFIED_HOSTS; + return 0; + } + + fprintf(stderr, "conf: unknown value to fully_qualified_hosts \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int +gopt_hesiod_base(const char *val) +{ +#ifdef HAVE_MAP_HESIOD + gopt.hesiod_base = strdup((char *)val); + return 0; +#else /* not HAVE_MAP_HESIOD */ + fprintf(stderr, "conf: hesiod_base option ignored. No Hesiod support available.\n"); + return 1; +#endif /* not HAVE_MAP_HESIOD */ +} + + +static int +gopt_karch(const char *val) +{ + gopt.karch = strdup((char *)val); + return 0; +} + + +static int +gopt_pid_file(const char *val) +{ + gopt.pid_file = strdup((char *)val); + return 0; +} + + +static int +gopt_local_domain(const char *val) +{ + gopt.sub_domain = strdup((char *)val); + return 0; +} + + +static int +gopt_localhost_address(const char *val) +{ + gopt.localhost_address = strdup((char *)val); + return 0; +} + + +static int +gopt_ldap_base(const char *val) +{ +#ifdef HAVE_MAP_LDAP + gopt.ldap_base = strdup((char *)val); + return 0; +#else /* not HAVE_MAP_LDAP */ + fprintf(stderr, "conf: ldap_base option ignored. No LDAP support available.\n"); + return 1; +#endif /* not HAVE_MAP_LDAP */ +} + + +static int +gopt_ldap_cache_seconds(const char *val) +{ +#ifdef HAVE_MAP_LDAP + char *end; + + gopt.ldap_cache_seconds = strtol((char *)val, &end, 10); + if (end == val) { + fprintf(stderr, "conf: bad LDAP cache (seconds) option: %s\n",val); + return 1; + } + return 0; +#else /* not HAVE_MAP_LDAP */ + fprintf(stderr, "conf: ldap_cache_seconds option ignored. No LDAP support available.\n"); + return 1; +#endif /* not HAVE_MAP_LDAP */ +} + + +static int +gopt_ldap_cache_maxmem(const char *val) +{ +#ifdef HAVE_MAP_LDAP + char *end; + + gopt.ldap_cache_maxmem = strtol((char *)val, &end, 10); + if (end == val) { + fprintf(stderr, "conf: bad LDAP cache (maxmem) option: %s\n",val); + return 1; + } + return 0; +#else /* not HAVE_MAP_LDAP */ + fprintf(stderr, "conf: ldap_cache_maxmem option ignored. No LDAP support available.\n"); + return 1; +#endif /* not HAVE_MAP_LDAP */ +} + + +static int +gopt_ldap_hostports(const char *val) +{ +#ifdef HAVE_MAP_LDAP + gopt.ldap_hostports = strdup((char *)val); + return 0; +#else /* not HAVE_MAP_LDAP */ + fprintf(stderr, "conf: ldap_hostports option ignored. No LDAP support available.\n"); + return 1; +#endif /* not HAVE_MAP_LDAP */ + +} + + +static int +gopt_ldap_proto_version(const char *val) +{ +#ifdef HAVE_MAP_LDAP + char *end; + + gopt.ldap_proto_version = strtol((char *)val, &end, 10); + if (end == val) { + fprintf(stderr, "conf: bad ldap_proto_version option: %s\n",val); + return 1; + } + + if (gopt.ldap_proto_version < 0 || gopt.ldap_proto_version > LDAP_VERSION_MAX) { + fprintf(stderr, "conf: bad ldap_proto_version option value: %s\n",val); + return 1; + } + switch (gopt.ldap_proto_version) { + /* XXX: what about LDAP_VERSION1? */ + case LDAP_VERSION2: +#ifdef LDAP_VERSION3 + case LDAP_VERSION3: +#endif /* LDAP_VERSION3 */ +#ifdef LDAP_VERSION4 + case LDAP_VERSION4: +#endif /* LDAP_VERSION4 */ + break; + default: + fprintf(stderr, "conf: unsupported ldap_proto_version option value: %s\n",val); + return 1; + } + return 0; +#else /* not HAVE_MAP_LDAP */ + fprintf(stderr, "conf: ldap_proto_version option ignored. No LDAP support available.\n"); + return 1; +#endif /* not HAVE_MAP_LDAP */ +} + + +static int +gopt_log_file(const char *val) +{ + gopt.logfile = strdup((char *)val); + return 0; +} + + +static int +gopt_log_options(const char *val) +{ + usage += switch_option((char *)val); + return 0; +} + + +static int +gopt_map_defaults(const char *val) +{ + gopt.map_defaults = strdup((char *)val); + return 0; +} + + +static int +gopt_map_options(const char *val) +{ + gopt.map_options = strdup((char *)val); + return 0; +} + + +static int +gopt_map_reload_interval(const char *val) +{ + gopt.map_reload_interval = atoi(val); + if (gopt.map_reload_interval <= 0) + gopt.map_reload_interval = ONE_HOUR; + return 0; +} + + +static int +gopt_map_type(const char *val) +{ + /* check if map type exist */ + if (!mapc_type_exists(val)) { + fprintf(stderr, "conf: no such map type \"%s\"\n", val); + return 1; + } + gopt.map_type = strdup((char *)val); + return 0; +} + + +static int +gopt_mount_type(const char *val) +{ + if (STREQ(val, "autofs")) { +#ifdef HAVE_FS_AUTOFS + gopt.flags |= CFM_MOUNT_TYPE_AUTOFS; + amd_use_autofs++; + return 0; +#else /* not HAVE_FS_AUTOFS */ + fprintf(stderr, "conf: no autofs support available\n"); + return 1; +#endif /* not HAVE_FS_AUTOFS */ + } else if (STREQ(val, "nfs")) { + gopt.flags &= ~CFM_MOUNT_TYPE_AUTOFS; + return 0; + } + + fprintf(stderr, "conf: unknown value to mount_type \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int +gopt_portmap_program(const char *val) +{ + gopt.portmap_program = atol(val); + /* + * allow alternate program numbers to be no more than 10 offset from + * official amd program number (300019). + */ + if (gopt.portmap_program < AMQ_PROGRAM || + gopt.portmap_program > AMQ_PROGRAM + 10) { + gopt.portmap_program = AMQ_PROGRAM; + set_amd_program_number(gopt.portmap_program); + fprintf(stderr, "conf: illegal amd program number \"%s\"\n", val); + return 1; + } + + set_amd_program_number(gopt.portmap_program); + return 0; /* all is OK */ +} + + +static int +gopt_preferred_amq_port(const char *val) +{ + gopt.preferred_amq_port = atoi(val); + + /* + * No need to check value: preferred_amq_port is an unsigned short and 0 + * is a valid number, meaning "any port". + */ + return 0; /* all is OK */ +} + + +static int +gopt_nfs_allow_any_interface(const char *val) +{ + if (STREQ(val, "yes")) { + gopt.flags |= CFM_NFS_ANY_INTERFACE; + return 0; + } else if (STREQ(val, "no")) { + gopt.flags &= ~CFM_NFS_ANY_INTERFACE; + return 0; + } + + fprintf(stderr, "conf: unknown value to nfs_allow_insecure_port \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int +gopt_nfs_allow_insecure_port(const char *val) +{ + if (STREQ(val, "yes")) { + gopt.flags |= CFM_NFS_INSECURE_PORT; + return 0; + } else if (STREQ(val, "no")) { + gopt.flags &= ~CFM_NFS_INSECURE_PORT; + return 0; + } + + fprintf(stderr, "conf: unknown value to nfs_allow_insecure_port \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int +gopt_nfs_proto(const char *val) +{ + if (STREQ(val, "udp") || STREQ(val, "tcp")) { + gopt.nfs_proto = strdup((char *)val); + return 0; + } + fprintf(stderr, "conf: illegal nfs_proto \"%s\"\n", val); + return 1; +} + + +static int +gopt_nfs_retransmit_counter(const char *val) +{ + int i; + + for (i=0; i<AMU_TYPE_MAX; ++i) + gopt.amfs_auto_retrans[i] = atoi(val); + return 0; +} + + +static int +gopt_nfs_retransmit_counter_udp(const char *val) +{ + gopt.amfs_auto_retrans[AMU_TYPE_UDP] = atoi(val); + return 0; +} + + +static int +gopt_nfs_retransmit_counter_tcp(const char *val) +{ + gopt.amfs_auto_retrans[AMU_TYPE_TCP] = atoi(val); + return 0; +} + + +static int +gopt_nfs_retransmit_counter_toplvl(const char *val) +{ + gopt.amfs_auto_retrans[AMU_TYPE_TOPLVL] = atoi(val); + return 0; +} + + +static int +gopt_nfs_retry_interval(const char *val) +{ + int i; + + for (i=0; i<AMU_TYPE_MAX; ++i) + gopt.amfs_auto_timeo[i] = atoi(val); + return 0; +} + + +static int +gopt_nfs_retry_interval_udp(const char *val) +{ + gopt.amfs_auto_timeo[AMU_TYPE_UDP] = atoi(val); + return 0; +} + + +static int +gopt_nfs_retry_interval_tcp(const char *val) +{ + gopt.amfs_auto_timeo[AMU_TYPE_TCP] = atoi(val); + return 0; +} + + +static int +gopt_nfs_retry_interval_toplvl(const char *val) +{ + gopt.amfs_auto_timeo[AMU_TYPE_TOPLVL] = atoi(val); + return 0; +} + + +static int +gopt_nfs_vers(const char *val) +{ + int i = atoi(val); + + if (i == 2 || i == 3) { + gopt.nfs_vers = i; + return 0; + } + fprintf(stderr, "conf: illegal nfs_vers \"%s\"\n", val); + return 1; +} + + +static int +gopt_nis_domain(const char *val) +{ +#ifdef HAVE_MAP_NIS + gopt.nis_domain = strdup((char *)val); + return 0; +#else /* not HAVE_MAP_NIS */ + fprintf(stderr, "conf: nis_domain option ignored. No NIS support available.\n"); + return 1; +#endif /* not HAVE_MAP_NIS */ +} + + +static int +gopt_normalize_hostnames(const char *val) +{ + if (STREQ(val, "yes")) { + gopt.flags |= CFM_NORMALIZE_HOSTNAMES; + return 0; + } else if (STREQ(val, "no")) { + gopt.flags &= ~CFM_NORMALIZE_HOSTNAMES; + return 0; + } + + fprintf(stderr, "conf: unknown value to normalize_hostnames \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int +gopt_normalize_slashes(const char *val) +{ + if (STREQ(val, "yes")) { + gopt.flags |= CFM_NORMALIZE_SLASHES; + return 0; + } else if (STREQ(val, "no")) { + gopt.flags &= ~CFM_NORMALIZE_SLASHES; + return 0; + } + + fprintf(stderr, "conf: unknown value to normalize_slashes \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int +gopt_os(const char *val) +{ + gopt.op_sys = strdup((char *)val); + return 0; +} + + +static int +gopt_osver(const char *val) +{ + gopt.op_sys_ver = strdup((char *)val); + return 0; +} + + +static int +gopt_plock(const char *val) +{ + if (STREQ(val, "yes")) { + gopt.flags |= CFM_PROCESS_LOCK; + return 0; + } else if (STREQ(val, "no")) { + gopt.flags &= ~CFM_PROCESS_LOCK; + return 0; + } + + fprintf(stderr, "conf: unknown value to plock \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int +gopt_print_pid(const char *val) +{ + if (STREQ(val, "yes")) { + gopt.flags |= CFM_PRINT_PID; + return 0; + } else if (STREQ(val, "no")) { + gopt.flags &= ~CFM_PRINT_PID; + return 0; + } + + fprintf(stderr, "conf: unknown value to print_pid \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int +gopt_print_version(const char *val) +{ + if (STREQ(val, "yes")) { + char *vers = get_version_string(); + fputs(vers, stderr); + XFREE(vers); + return 0; + } else if (STREQ(val, "no")) { + return 0; + } + + fprintf(stderr, "conf: unknown value to print_version \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int +gopt_restart_mounts(const char *val) +{ + if (STREQ(val, "yes")) { + gopt.flags |= CFM_RESTART_EXISTING_MOUNTS; + return 0; + } else if (STREQ(val, "no")) { + gopt.flags &= ~CFM_RESTART_EXISTING_MOUNTS; + return 0; + } + + fprintf(stderr, "conf: unknown value to restart_mounts \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int +gopt_search_path(const char *val) +{ + gopt.search_path = strdup((char *)val); + return 0; +} + + +static int +gopt_selectors_in_defaults(const char *val) +{ + if (STREQ(val, "yes")) { + gopt.flags |= CFM_SELECTORS_IN_DEFAULTS; + return 0; + } else if (STREQ(val, "no")) { + gopt.flags &= ~CFM_SELECTORS_IN_DEFAULTS; + return 0; + } + + fprintf(stderr, "conf: unknown value to enable_default_selectors \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int +gopt_show_statfs_entries(const char *val) +{ + if (STREQ(val, "yes")) { + gopt.flags |= CFM_SHOW_STATFS_ENTRIES; + return 0; + } else if (STREQ(val, "no")) { + gopt.flags &= ~CFM_SHOW_STATFS_ENTRIES; + return 0; + } + + fprintf(stderr, "conf: unknown value to show_statfs_entries \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int +gopt_truncate_log(const char *val) +{ + if (STREQ(val, "yes")) { + gopt.flags |= CFM_TRUNCATE_LOG; + return 0; + } else if (STREQ(val, "no")) { + gopt.flags &= ~CFM_TRUNCATE_LOG; + return 0; + } + + fprintf(stderr, "conf: unknown value to truncate_log \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int +gopt_unmount_on_exit(const char *val) +{ + if (STREQ(val, "yes")) { + gopt.flags |= CFM_UNMOUNT_ON_EXIT; + return 0; + } else if (STREQ(val, "no")) { + gopt.flags &= ~CFM_UNMOUNT_ON_EXIT; + return 0; + } + + fprintf(stderr, "conf: unknown value to unmount_on_exit \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int +gopt_use_tcpwrappers(const char *val) +{ +#if defined(HAVE_TCPD_H) && defined(HAVE_LIBWRAP) + if (STREQ(val, "yes")) { + gopt.flags |= CFM_USE_TCPWRAPPERS; + return 0; + } else if (STREQ(val, "no")) { + gopt.flags &= ~CFM_USE_TCPWRAPPERS; + return 0; + } +#else /* not defined(HAVE_TCPD_H) && defined(HAVE_LIBWRAP) */ + fprintf(stderr, "conf: no tcpd/libwrap support available\n"); + return 1; +#endif /* not defined(HAVE_TCPD_H) && defined(HAVE_LIBWRAP) */ + + fprintf(stderr, "conf: unknown value to use_tcpwrappers \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int +gopt_vendor(const char *val) +{ + gopt.op_sys_vendor = strdup((char *)val); + return 0; +} + + +/* + * Collect one entry for a regular map + */ +static int +process_regular_option(const char *section, const char *key, const char *val, cf_map_t *cfm) +{ + /* ensure that val is valid */ + if (!section || section[0] == '\0' || + !key || key[0] == '\0' || + !val || val[0] == '\0' || + !cfm) { + fprintf(stderr, "conf: process_regular_option: null entries\n"); + return 1; + } + + /* check if initializing a new map */ + if (!cfm->cfm_dir) + cfm->cfm_dir = strdup((char *)section); + + /* check for each possible field */ + if (STREQ(key, "browsable_dirs")) + return ropt_browsable_dirs(val, cfm); + + if (STREQ(key, "map_name")) + return ropt_map_name(val, cfm); + + if (STREQ(key, "map_defaults")) + return ropt_map_defaults(val, cfm); + + if (STREQ(key, "map_options")) + return ropt_map_options(val, cfm); + + if (STREQ(key, "map_type")) + return ropt_map_type(val, cfm); + + if (STREQ(key, "mount_type")) + return ropt_mount_type(val, cfm); + + if (STREQ(key, "search_path")) + return ropt_search_path(val, cfm); + + if (STREQ(key, "tag")) + return ropt_tag(val, cfm); + + fprintf(stderr, "conf: unknown regular key \"%s\" for section \"%s\"\n", + key, section); + return 1; /* failed to match any command */ +} + + +static int +ropt_browsable_dirs(const char *val, cf_map_t *cfm) +{ + if (STREQ(val, "full")) { + cfm->cfm_flags |= CFM_BROWSABLE_DIRS_FULL; + return 0; + } else if (STREQ(val, "yes")) { + cfm->cfm_flags |= CFM_BROWSABLE_DIRS; + return 0; + } else if (STREQ(val, "no")) { + cfm->cfm_flags &= ~CFM_BROWSABLE_DIRS; + return 0; + } + + fprintf(stderr, "conf: unknown value to browsable_dirs \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int +ropt_map_name(const char *val, cf_map_t *cfm) +{ + cfm->cfm_name = strdup((char *)val); + return 0; +} + + +static int +ropt_map_defaults(const char *val, cf_map_t *cfm) +{ + cfm->cfm_defaults = strdup((char *)val); + return 0; +} + + +static int +ropt_map_options(const char *val, cf_map_t *cfm) +{ + cfm->cfm_opts = strdup((char *)val); + return 0; +} + + +static int +ropt_map_type(const char *val, cf_map_t *cfm) +{ + /* check if map type exist */ + if (!mapc_type_exists(val)) { + fprintf(stderr, "conf: no such map type \"%s\"\n", val); + return 1; + } + cfm->cfm_type = strdup((char *)val); + return 0; +} + + +static int +ropt_mount_type(const char *val, cf_map_t *cfm) +{ + if (STREQ(val, "autofs")) { +#ifdef HAVE_FS_AUTOFS + cfm->cfm_flags |= CFM_MOUNT_TYPE_AUTOFS; + amd_use_autofs++; + return 0; +#else /* not HAVE_FS_AUTOFS */ + fprintf(stderr, "conf: no autofs support available\n"); + return 1; +#endif /* not HAVE_FS_AUTOFS */ + } else if (STREQ(val, "nfs")) { + cfm->cfm_flags &= ~CFM_MOUNT_TYPE_AUTOFS; + return 0; + } + + fprintf(stderr, "conf: unknown value to mount_type \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int +ropt_search_path(const char *val, cf_map_t *cfm) +{ + cfm->cfm_search_path = strdup((char *)val); + return 0; +} + + +static int +ropt_tag(const char *val, cf_map_t *cfm) +{ + cfm->cfm_tag = strdup((char *)val); + return 0; +} + + +/* + * Process one collected map. + */ +static int +process_one_regular_map(const cf_map_t *cfm) +{ + if (!cfm->cfm_name) { + fprintf(stderr, "conf: map_name must be defined for map \"%s\"\n", cfm->cfm_dir); + return 1; + } + /* + * If map has no tag defined, process the map. + * If no conf_tag was set in amd -T, process all untagged entries. + * If a tag is defined, then process it only if it matches the map tag. + */ + if (!cfm->cfm_tag || + (conf_tag && STREQ(cfm->cfm_tag, conf_tag))) { +#ifdef DEBUG_CONF + fprintf(stderr, "processing map %s (flags=0x%x)...\n", + cfm->cfm_dir, cfm->cfm_flags); +#endif /* DEBUG_CONF */ + root_newmap(cfm->cfm_dir, + cfm->cfm_opts ? cfm->cfm_opts : "", + cfm->cfm_name, + cfm); + } else { + fprintf(stderr, "skipping map %s...\n", cfm->cfm_dir); + } + + return 0; +} + + +/* + * Process all regular maps in conf file (if any) + */ +int +process_all_regular_maps(void) +{ + cf_map_t *tmp_map = head_map; + + /* + * If the amd.conf file only has a [global] section (pretty useless + * IMHO), there's nothing to process + */ + if (!tmp_map) + return 0; + + while (tmp_map) { + if (process_one_regular_map(tmp_map) != 0) + return 1; + tmp_map = tmp_map->cfm_next; + } + return 0; +} + + +/* + * Find a cf_map_t for a given map name. + * Return NULL if not found. + */ +cf_map_t * +find_cf_map(const char *name) +{ + + cf_map_t *tmp_map = head_map; + + if (!tmp_map || !name) + return NULL; + + while (tmp_map) { + if (STREQ(tmp_map->cfm_dir,name)) { + return tmp_map; + } + tmp_map = tmp_map->cfm_next; + } + return NULL; +} diff --git a/amd/conf_parse.y b/amd/conf_parse.y new file mode 100644 index 000000000000..b397b0573242 --- /dev/null +++ b/amd/conf_parse.y @@ -0,0 +1,158 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * 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. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/conf_parse.y + * + */ + +%{ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +extern char *yytext; +extern int ayylineno; +extern int yylex(void); + +static int yyerror(const char *s); +static int retval; +static char *header_section = NULL; /* start with no header section */ + +#define YYDEBUG 1 + +#define PARSE_DEBUG 0 + +#if PARSE_DEBUG +# define dprintf(f,s) fprintf(stderr, (f), ayylineno, (s)) +# define amu_return(v) +#else /* not PARSE_DEBUG */ +# define dprintf(f,s) +# define amu_return(v) return((v)) +#endif /* not PARSE_DEBUG */ + +%} + +%union { +char *strtype; +} + +%token LEFT_BRACKET RIGHT_BRACKET EQUAL +%token NEWLINE +%token <strtype> NONWS_STRING +%token <strtype> NONWSEQ_STRING +%token <strtype> QUOTED_NONWSEQ_STRING + +%start file +%% + +/****************************************************************************/ +file : { yydebug = PARSE_DEBUG; } newlines map_sections + | { yydebug = PARSE_DEBUG; } map_sections + ; + +newlines : NEWLINE + | NEWLINE newlines + ; + +map_sections : map_section + | map_section map_sections + ; + +map_section : sec_header kv_pairs + ; + +sec_header : LEFT_BRACKET NONWS_STRING RIGHT_BRACKET NEWLINE + { + if (yydebug) + fprintf(stderr, "sec_header1 = \"%s\"\n", $2); + header_section = $2; + } + ; + +kv_pairs : kv_pair + | kv_pair kv_pairs + ; + +kv_pair : NONWS_STRING EQUAL NONWS_STRING NEWLINE + { + if (yydebug) + fprintf(stderr,"parse1: key=\"%s\", val=\"%s\"\n", $1, $3); + retval = set_conf_kv(header_section, $1, $3); + if (retval != 0) { + yyerror("syntax error"); + YYABORT; + } + } + | NONWS_STRING EQUAL NONWSEQ_STRING NEWLINE + { + if (yydebug) + fprintf(stderr,"parse2: key=\"%s\", val=\"%s\"\n", $1, $3); + retval = set_conf_kv(header_section, $1, $3); + if (retval != 0) { + yyerror("syntax error"); + YYABORT; + } + } + | NONWS_STRING EQUAL QUOTED_NONWSEQ_STRING NEWLINE + { + if (yydebug) + fprintf(stderr,"parse3: key=\"%s\", val=\"%s\"\n", $1, $3); + retval = set_conf_kv(header_section, $1, $3); + if (retval != 0) { + yyerror("syntax error"); + YYABORT; + } + } + | NEWLINE + ; + +/****************************************************************************/ +%% + +static int +yyerror(const char *s) +{ + fprintf(stderr, "AMDCONF: %s on line %d (section %s)\n", + s, ayylineno, + (header_section ? header_section : "null")); + exit(1); + return 1; /* to full compilers that insist on a return statement */ +} diff --git a/amd/conf_tok.l b/amd/conf_tok.l new file mode 100644 index 000000000000..b572e4d7d5a3 --- /dev/null +++ b/amd/conf_tok.l @@ -0,0 +1,192 @@ +%{ +/* + * Copyright (c) 1997-2006 Erez Zadok + * 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. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/conf_tok.l + * + */ + +/* + * Lexical analyzer for AMD configuration parser. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +/* + * Some systems include a definition for the macro ECHO in <sys/ioctl.h>, + * and their (bad) version of lex defines it too at the very beginning of + * the generated lex.yy.c file (before it can be easily undefined), + * resulting in a conflict. So undefine it here before needed. + * Luckily, it does not appear that this macro is actually used in the rest + * of the generated lex.yy.c file. + */ +#ifdef ECHO +# undef ECHO +#endif /* ECHO */ +#include <am_defs.h> +#include <amd.h> +#include <conf_parse.h> +/* and once again undefine this, just in case */ +#ifdef ECHO +# undef ECHO +#endif /* ECHO */ + +/* + * There are some things that need to be defined only if using GNU flex. + * These must not be defined if using standard lex + */ +#ifdef FLEX_SCANNER +# ifndef ECHO +# define ECHO (void) fwrite( yytext, yyleng, 1, yyout ) +# endif /* not ECHO */ +#endif /* FLEX_SCANNER */ + +int ayylineno = 0; + +int yylex(void); +/* + * some systems such as DU-4.x have a different GNU flex in /usr/bin + * which automatically generates yywrap macros and symbols. So I must + * distinguish between them and when yywrap is actually needed. + */ +#ifndef yywrap +int yywrap(void); +#endif /* not yywrap */ + +#define TOK_DEBUG 0 + +#if TOK_DEBUG +# define dprintf(f,s) fprintf(stderr, (f), ayylineno, (s)) +# define amu_return(v) +#else /* not TOK_DEBUG */ +# define dprintf(f,s) +# define amu_return(v) return((v)) +#endif /* not TOK_DEBUG */ + +/* no need to use yywrap() */ +#define YY_SKIP_YYWRAP + +%} + +/* This option causes Solaris lex to fail. Use flex. See BUGS file */ +/* no need to use yyunput() */ +%option nounput + +/* allocate more output slots so lex scanners don't run out of mem */ +%o 1024 + +DIGIT [0-9] +ALPHA [A-Za-z] +ALPHANUM [A-Za-z0-9] +SYMBOL [A-Za-z0-9_-] +PATH [A-Za-z0-9_-/] +NONWSCHAR [^ \t\n\[\]=] +NONWSEQCHAR [^ \t\n\[\]] +NONNL [^\n] +NONQUOTE [^\"] + +%% + +\n { + ayylineno++; + amu_return(NEWLINE); + } + +\[ { + dprintf("%8d: Left bracket \"%s\"\n", yytext); + yylval.strtype = strdup((char *)yytext); + amu_return(LEFT_BRACKET); + } + +\] { + dprintf("%8d: Right bracket \"%s\"\n", yytext); + yylval.strtype = strdup((char *)yytext); + amu_return(RIGHT_BRACKET); + } + += { + dprintf("%8d: Equal \"%s\"\n", yytext); + yylval.strtype = strdup((char *)yytext); + amu_return(EQUAL); + } + +[ \t]* { + dprintf("%8d: Whitespace \"%s\"\n", yytext); + } +"#"[^\n]*\n { + /* a comment line includes the terminating \n */ + ayylineno++; + yytext[strlen((char *)yytext)-1] = '\0'; + dprintf("%8d: Comment \"%s\"\n", yytext); + } + +{NONWSCHAR}{NONWSCHAR}* { + dprintf("%8d: Non-WS string \"%s\"\n", yytext); + yylval.strtype = strdup((char *)yytext); + amu_return(NONWS_STRING); + } + +\"{NONQUOTE}{NONQUOTE}*\" { + dprintf("%8d: QUOTED-Non-WS-EQ string \"%s\"\n", yytext); + /* must strip quotes */ + yytext[strlen((char *)yytext)-1] = '\0'; + yylval.strtype = strdup((char *)&yytext[1]); + amu_return(QUOTED_NONWSEQ_STRING); + } + +{NONWSEQCHAR}{NONWSEQCHAR}* { + dprintf("%8d: Non-WS-EQ string \"%s\"\n", yytext); + yylval.strtype = strdup((char *)yytext); + amu_return(NONWSEQ_STRING); + } + +%% + +/* + * some systems such as DU-4.x have a different GNU flex in /usr/bin + * which automatically generates yywrap macros and symbols. So I must + * distinguish between them and when yywrap is actually needed. + */ +#ifndef yywrap +int yywrap(void) +{ + return 1; +} +#endif /* not yywrap */ diff --git a/amd/get_args.c b/amd/get_args.c new file mode 100644 index 000000000000..12a7d07efd59 --- /dev/null +++ b/amd/get_args.c @@ -0,0 +1,437 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/get_args.c + * + */ + +/* + * Argument decode + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* include auto-generated version file */ +#include <build_version.h> + +char *amu_conf_file = "/etc/amd.conf"; /* default amd configuration file */ +char *conf_tag = NULL; /* default conf file tags to use */ +int usage = 0; +int use_conf_file = 0; /* default don't use amd.conf file */ +char *mnttab_file_name = NULL; /* symbol must be available always */ + + +/* + * Return the version string (dynamic buffer) + */ +char * +get_version_string(void) +{ + char *vers = NULL; + char tmpbuf[1024]; + char *wire_buf; + int wire_buf_len = 0; + size_t len; /* max allocated length (to avoid buf overflow) */ + + /* + * First get dynamic string listing all known networks. + * This could be a long list, if host has lots of interfaces. + */ + wire_buf = print_wires(); + if (wire_buf) + wire_buf_len = strlen(wire_buf); + + len = 2048 + wire_buf_len; + vers = xmalloc(len); + xsnprintf(vers, len, "%s\n%s\n%s\n%s\n", + "Copyright (c) 1997-2006 Erez Zadok", + "Copyright (c) 1990 Jan-Simon Pendry", + "Copyright (c) 1990 Imperial College of Science, Technology & Medicine", + "Copyright (c) 1990 The Regents of the University of California."); + xsnprintf(tmpbuf, sizeof(tmpbuf), "%s version %s (build %d).\n", + PACKAGE_NAME, PACKAGE_VERSION, AMU_BUILD_VERSION); + strlcat(vers, tmpbuf, len); + xsnprintf(tmpbuf, sizeof(tmpbuf), "Report bugs to %s.\n", PACKAGE_BUGREPORT); + strlcat(vers, tmpbuf, len); + xsnprintf(tmpbuf, sizeof(tmpbuf), "Configured by %s@%s on date %s.\n", + USER_NAME, HOST_NAME, CONFIG_DATE); + strlcat(vers, tmpbuf, len); + xsnprintf(tmpbuf, sizeof(tmpbuf), "Built by %s@%s on date %s.\n", + BUILD_USER, BUILD_HOST, BUILD_DATE); + strlcat(vers, tmpbuf, len); + xsnprintf(tmpbuf, sizeof(tmpbuf), "cpu=%s (%s-endian), arch=%s, karch=%s.\n", + cpu, endian, gopt.arch, gopt.karch); + strlcat(vers, tmpbuf, len); + xsnprintf(tmpbuf, sizeof(tmpbuf), "full_os=%s, os=%s, osver=%s, vendor=%s, distro=%s.\n", + gopt.op_sys_full, gopt.op_sys, gopt.op_sys_ver, gopt.op_sys_vendor, DISTRO_NAME); + strlcat(vers, tmpbuf, len); + xsnprintf(tmpbuf, sizeof(tmpbuf), "domain=%s, host=%s, hostd=%s.\n", + hostdomain, am_get_hostname(), hostd); + strlcat(vers, tmpbuf, len); + + strlcat(vers, "Map support for: ", len); + mapc_showtypes(tmpbuf, sizeof(tmpbuf)); + strlcat(vers, tmpbuf, len); + strlcat(vers, ".\nAMFS: ", len); + ops_showamfstypes(tmpbuf, sizeof(tmpbuf)); + strlcat(vers, tmpbuf, len); + strlcat(vers, ", inherit.\nFS: ", len); /* hack: "show" that we support type:=inherit */ + ops_showfstypes(tmpbuf, sizeof(tmpbuf)); + strlcat(vers, tmpbuf, len); + + /* append list of networks if available */ + if (wire_buf) { + strlcat(vers, wire_buf, len); + XFREE(wire_buf); + } + + return vers; +} + + +static void +show_usage(void) +{ + fprintf(stderr, + "Usage: %s [-nprvHS] [-a mount_point] [-c cache_time] [-d domain]\n\ +\t[-k kernel_arch] [-l logfile%s\n\ +\t[-t timeout.retrans] [-w wait_timeout] [-A arch] [-C cluster_name]\n\ +\t[-o op_sys_ver] [-O op_sys_name]\n\ +\t[-F conf_file] [-T conf_tag]", am_get_progname(), +#ifdef HAVE_SYSLOG +# ifdef LOG_DAEMON + "|\"syslog[:facility]\"]" +# else /* not LOG_DAEMON */ + "|\"syslog\"]" +# endif /* not LOG_DAEMON */ +#else /* not HAVE_SYSLOG */ + "]" +#endif /* not HAVE_SYSLOG */ + ); + +#ifdef HAVE_MAP_NIS + fputs(" [-y nis-domain]\n", stderr); +#else /* not HAVE_MAP_NIS */ + fputc('\n', stderr); +#endif /* HAVE_MAP_NIS */ + + show_opts('x', xlog_opt); +#ifdef DEBUG + show_opts('D', dbg_opt); +#endif /* DEBUG */ + fprintf(stderr, "\t[directory mapname [-map_options]] ...\n"); +} + + +void +get_args(int argc, char *argv[]) +{ + int opt_ch, i; + FILE *fp = stdin; + char getopt_arguments[] = "+nprvSa:c:d:k:l:o:t:w:x:y:C:D:F:T:O:HA:"; + char *getopt_args; + int print_version = 0; /* 1 means we should print version info */ + +#ifdef HAVE_GNU_GETOPT + getopt_args = getopt_arguments; +#else /* ! HAVE_GNU_GETOPT */ + getopt_args = &getopt_arguments[1]; +#endif /* HAVE_GNU_GETOPT */ + + /* if no arguments were passed, try to use /etc/amd.conf file */ + if (argc <= 1) + use_conf_file = 1; + + while ((opt_ch = getopt(argc, argv, getopt_args)) != -1) + switch (opt_ch) { + + case 'a': + if (*optarg != '/') { + fprintf(stderr, "%s: -a option must begin with a '/'\n", + am_get_progname()); + exit(1); + } + gopt.auto_dir = optarg; + break; + + case 'c': + gopt.am_timeo = atoi(optarg); + if (gopt.am_timeo <= 0) + gopt.am_timeo = AM_TTL; + break; + + case 'd': + gopt.sub_domain = optarg; + break; + + case 'k': + gopt.karch = optarg; + break; + + case 'l': + gopt.logfile = optarg; + break; + + case 'n': + gopt.flags |= CFM_NORMALIZE_HOSTNAMES; + break; + + case 'o': + gopt.op_sys_ver = optarg; + break; + + case 'p': + gopt.flags |= CFM_PRINT_PID; + break; + + case 'r': + gopt.flags |= CFM_RESTART_EXISTING_MOUNTS; + break; + + case 't': + /* timeo.retrans (also affects toplvl mounts) */ + { + char *dot = strchr(optarg, '.'); + int i; + if (dot) + *dot = '\0'; + if (*optarg) { + for (i=0; i<AMU_TYPE_MAX; ++i) + gopt.amfs_auto_timeo[i] = atoi(optarg); + } + if (dot) { + for (i=0; i<AMU_TYPE_MAX; ++i) + gopt.amfs_auto_retrans[i] = atoi(dot + 1); + *dot = '.'; + } + } + break; + + case 'v': + /* + * defer to print version info after every variable had been + * initialized. + */ + print_version++; + break; + + case 'w': + gopt.am_timeo_w = atoi(optarg); + if (gopt.am_timeo_w <= 0) + gopt.am_timeo_w = AM_TTL_W; + break; + + case 'x': + usage += switch_option(optarg); + break; + + case 'y': +#ifdef HAVE_MAP_NIS + gopt.nis_domain = optarg; +#else /* not HAVE_MAP_NIS */ + plog(XLOG_USER, "-y: option ignored. No NIS support available."); +#endif /* not HAVE_MAP_NIS */ + break; + + case 'A': + gopt.arch = optarg; + break; + + case 'C': + gopt.cluster = optarg; + break; + + case 'D': +#ifdef DEBUG + usage += debug_option(optarg); +#else /* not DEBUG */ + fprintf(stderr, "%s: not compiled with DEBUG option -- sorry.\n", + am_get_progname()); +#endif /* not DEBUG */ + break; + + case 'F': + amu_conf_file = optarg; + use_conf_file = 1; + break; + + case 'H': + show_usage(); + exit(1); + break; + + case 'O': + gopt.op_sys = optarg; + break; + + case 'S': + gopt.flags &= ~CFM_PROCESS_LOCK; /* turn process locking off */ + break; + + case 'T': + conf_tag = optarg; + break; + + default: + usage = 1; + break; + } + + /* + * amd.conf file: if not command-line arguments were used, or if -F was + * specified, then use that amd.conf file. If the file cannot be opened, + * abort amd. If it can be found, open it, parse it, and then close it. + */ + if (use_conf_file && amu_conf_file) { + fp = fopen(amu_conf_file, "r"); + if (!fp) { + char buf[128]; + xsnprintf(buf, sizeof(buf), "Amd configuration file (%s)", + amu_conf_file); + perror(buf); + exit(1); + } + yyin = fp; + yyparse(); + fclose(fp); + if (process_all_regular_maps() != 0) + exit(1); + } + + /* make sure there are some default options defined */ + if (xlog_level_init == ~0) { + switch_option(""); + } +#ifdef DEBUG + usage += switch_option("debug"); +#endif /* DEBUG */ + + /* log information regarding amd.conf file */ + if (use_conf_file && amu_conf_file) + plog(XLOG_INFO, "using configuration file %s", amu_conf_file); + +#ifdef HAVE_MAP_LDAP + /* ensure that if ldap_base is specified, that also ldap_hostports is */ + if (gopt.ldap_hostports && !gopt.ldap_base) { + fprintf(stderr, "must specify both ldap_hostports and ldap_base\n"); + exit(1); + } +#endif /* HAVE_MAP_LDAP */ + + if (usage) { + show_usage(); + exit(1); + } + + while (optind <= argc - 2) { + char *dir = argv[optind++]; + char *map = argv[optind++]; + char *opts = ""; + if (argv[optind] && *argv[optind] == '-') + opts = &argv[optind++][1]; + + root_newmap(dir, opts, map, NULL); + } + + if (optind == argc) { + /* + * Append domain name to hostname. + * sub_domain overrides hostdomain + * if given. + */ + if (gopt.sub_domain) + hostdomain = gopt.sub_domain; + if (*hostdomain == '.') + hostdomain++; + xstrlcat(hostd, ".", sizeof(hostd)); + xstrlcat(hostd, hostdomain, sizeof(hostd)); + +#ifdef MOUNT_TABLE_ON_FILE + if (amuDebug(D_MTAB)) + if (gopt.debug_mtab_file) + mnttab_file_name = gopt.debug_mtab_file; /* user supplied debug mtab path */ + else + mnttab_file_name = DEBUG_MNTTAB_FILE; /* default debug mtab path */ + else + mnttab_file_name = MNTTAB_FILE_NAME; +#else /* not MOUNT_TABLE_ON_FILE */ + if (amuDebug(D_MTAB)) + dlog("-D mtab option ignored"); +# ifdef MNTTAB_FILE_NAME + mnttab_file_name = MNTTAB_FILE_NAME; +# endif /* MNTTAB_FILE_NAME */ +#endif /* not MOUNT_TABLE_ON_FILE */ + + /* + * If the kernel architecture was not specified + * then use the machine architecture. + */ + if (gopt.karch == NULL) + gopt.karch = gopt.arch; + + if (gopt.cluster == NULL) + gopt.cluster = hostdomain; + + /* sanity checking, normalize values just in case (toplvl too) */ + for (i=0; i<AMU_TYPE_MAX; ++i) { + if (gopt.amfs_auto_timeo[i] == 0) + gopt.amfs_auto_timeo[i] = AMFS_AUTO_TIMEO; + if (gopt.amfs_auto_retrans[i] == 0) + gopt.amfs_auto_retrans[i] = AMFS_AUTO_RETRANS(i); + if (gopt.amfs_auto_retrans[i] == 0) + gopt.amfs_auto_retrans[i] = 3; /* under very unusual circumstances, could be zero */ + } + } + + /* finally print version string and exit, if asked for */ + if (print_version) { + fputs(get_version_string(), stderr); + exit(0); + } + + if (switch_to_logfile(gopt.logfile, orig_umask, + (gopt.flags & CFM_TRUNCATE_LOG)) != 0) + plog(XLOG_USER, "Cannot switch logfile"); + + return; +} diff --git a/amd/info_exec.c b/amd/info_exec.c new file mode 100644 index 000000000000..8ccab9aee579 --- /dev/null +++ b/amd/info_exec.c @@ -0,0 +1,423 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/info_exec.c + * + */ + +/* + * Get info from executable map + * + * Original from Erik Kline, 2004. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +#define MAX_LINE_LEN 1500 + +/* forward declarations */ +int exec_init(mnt_map *m, char *map, time_t *tp); +int exec_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp); + + +/* + * a timed fgets() + */ +static char * +fgets_timed(char *s, int size, int rdfd, int secs) +{ + fd_set fds; + struct timeval timeo; + time_t start, now; + int rval=0, i=0; + + if (!s || size < 0 || rdfd < 0) + return 0; + + s[0] = 0; + if (size == 0) + return s; + + start = clocktime(NULL); + while (s[i] != '\n' && i < size-1) { + s[i+1] = 0; /* places the requisite trailing '\0' */ + + /* ready for reading */ + rval = read(rdfd, (void *)(s+i), 1); + if (rval == 1) { + if (s[i] == 0) { + rval = 0; + break; + } + i++; + continue; + } else if (rval == 0) { + break; + } else if (rval < 0 && errno != EAGAIN && errno != EINTR) { + plog(XLOG_WARNING, "fgets_timed read error: %m"); + break; + } + + timeo.tv_usec = 0; + now = clocktime(NULL) - start; + if (secs <= 0) + timeo.tv_sec = 0; + else if (now < secs) + timeo.tv_sec = secs - now; + else { + /* timed out (now>=secs) */ + plog(XLOG_WARNING, "executable map read timed out (> %d secs)", secs); + rval = -1; + break; + } + + FD_ZERO(&fds); + FD_SET(rdfd, &fds); + + rval = select(rdfd+1, &fds, 0, 0, &timeo); + if (rval < 0) { + /* error selecting */ + plog(XLOG_WARNING, "fgets_timed select error: %m"); + if (errno == EINTR) + continue; + rval = -1; + break; + } else if (rval == 0) { + /* timed out */ + plog(XLOG_WARNING, "executable map read timed out (> %d secs)", secs); + rval = -1; + break; + } + } + + if (rval > 0) + return s; + + close(rdfd); + return (rval == 0 ? s : 0); +} + + +static int +read_line(char *buf, int size, int fd) +{ + int done = 0; + + while (fgets_timed(buf, size, fd, gopt.exec_map_timeout)) { + int len = strlen(buf); + done += len; + if (len > 1 && buf[len - 2] == '\\' && + buf[len - 1] == '\n') { + buf += len - 2; + size -= len - 2; + *buf = '\n'; + buf[1] = '\0'; + } else { + return done; + } + } + + return done; +} + + +/* + * Try to locate a value in a query answer + */ +static int +exec_parse_qanswer(int fd, char *map, char *key, char **pval, time_t *tp) +{ + char qanswer[MAX_LINE_LEN], *dc = 0; + int chuck = 0; + int line_no = 0; + + while (read_line(qanswer, sizeof(qanswer), fd)) { + char *cp; + char *hash; + int len = strlen(qanswer); + line_no++; + + /* + * Make sure we got the whole line + */ + if (qanswer[len - 1] != '\n') { + plog(XLOG_WARNING, "line %d in \"%s\" is too long", line_no, map); + chuck = 1; + } else { + qanswer[len - 1] = '\0'; + } + + /* + * Strip comments + */ + hash = strchr(qanswer, '#'); + if (hash) + *hash = '\0'; + + /* + * Find beginning of value (query answer) + */ + for (cp = qanswer; *cp && !isascii((int)*cp) && !isspace((int)*cp); cp++) + ;; + + /* Ignore blank lines */ + if (!*cp) + goto again; + + /* + * Return a copy of the data + */ + dc = strdup(cp); + *pval = dc; + dlog("%s returns %s", key, dc); + + close(fd); + return 0; + + again: + /* + * If the last read didn't get a whole line then + * throw away the remainder before continuing... + */ + if (chuck) { + while (fgets_timed(qanswer, sizeof(qanswer), fd, gopt.exec_map_timeout) && + !strchr(qanswer, '\n')) ; + chuck = 0; + } + } + + return ENOENT; +} + + +static int +set_nonblock(int fd) +{ + int val; + + if (fd < 0) + return 0; + + if ((val = fcntl(fd, F_GETFL, 0)) < 0) { + plog(XLOG_WARNING, "set_nonblock fcntl F_GETFL error: %m"); + return 0; + } + + val |= O_NONBLOCK; + if (fcntl(fd, F_SETFL, val) < 0) { + plog(XLOG_WARNING, "set_nonblock fcntl F_SETFL error: %m"); + return 0; + } + + return 1; +} + + +static int +exec_map_open(char *emap, char *key) +{ + pid_t p1, p2; + int pdes[2], nullfd, i; + char *argv[3]; + + if (!emap) + return 0; + + argv[0] = emap; + argv[1] = key; + argv[2] = NULL; + + if ((nullfd = open("/dev/null", O_WRONLY|O_NOCTTY)) < 0) + return -1; + + if (pipe(pdes) < 0) { + close(nullfd); + return -1; + } + + switch ((p1 = vfork())) { + case -1: + /* parent: fork error */ + close(nullfd); + close(pdes[0]); + close(pdes[1]); + return -1; + case 0: + /* child #1 */ + p2 = vfork(); + switch (p2) { + case -1: + /* child #1: fork error */ + exit(errno); + case 0: + /* child #2: init will reap our status */ + if (pdes[1] != STDOUT_FILENO) { + dup2(pdes[1], STDOUT_FILENO); + close(pdes[1]); + } + + if (nullfd != STDERR_FILENO) { + dup2(nullfd, STDERR_FILENO); + close(nullfd); + } + + for (i=0; i<FD_SETSIZE; i++) + if (i != STDOUT_FILENO && i != STDERR_FILENO) + close(i); + + /* make the write descriptor non-blocking */ + if (!set_nonblock(STDOUT_FILENO)) { + close(STDOUT_FILENO); + exit(-1); + } + + execve(emap, argv, NULL); + exit(errno); /* in case execve failed */ + } + + /* child #1 */ + exit(0); + } + + /* parent */ + close(nullfd); + close(pdes[1]); + + /* anti-zombie insurance */ + while (waitpid(p1,0,0) < 0) + if (errno != EINTR) + exit(errno); + + /* make the read descriptor non-blocking */ + if (!set_nonblock(pdes[0])) { + close(pdes[0]); + return -1; + } + + return pdes[0]; +} + + +/* + * Check for various permissions on executable map without trying to + * fork a new executable-map process. + * + * return: >0 (errno) if failed + * 0 if ok + */ +static int +exec_check_perm(char *map) +{ + struct stat sb; + + /* sanity and permission checks */ + if (!map) { + dlog("exec_check_permission got a NULL map"); + return EINVAL; + } + if (stat(map, &sb)) { + plog(XLOG_ERROR, "map \"%s\" stat failure: %m", map); + return errno; + } + if (!S_ISREG(sb.st_mode)) { + plog(XLOG_ERROR, "map \"%s\" should be regular file", map); + return EINVAL; + } + if (sb.st_uid != 0) { + plog(XLOG_ERROR, "map \"%s\" owned by uid %u (must be 0)", map, (u_int) sb.st_uid); + return EACCES; + } + if (!(sb.st_mode & S_IXUSR)) { + plog(XLOG_ERROR, "map \"%s\" should be executable", map); + return EACCES; + } + if (sb.st_mode & (S_ISUID|S_ISGID)) { + plog(XLOG_ERROR, "map \"%s\" should not be setuid/setgid", map); + return EACCES; + } + if (sb.st_mode & S_IWOTH) { + plog(XLOG_ERROR, "map \"%s\" should not be world writeable", map); + return EACCES; + } + + return 0; /* all is well */ +} + + +int +exec_init(mnt_map *m, char *map, time_t *tp) +{ + /* + * Basically just test that the executable map can be found + * and has proper permissions. + */ + return exec_check_perm(map); +} + + +int +exec_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp) +{ + int mapfd, ret; + + if ((ret = exec_check_perm(map)) != 0) { + return ret; + } + + if (!key) + return 0; + + if (logfp) + fflush(logfp); + dlog("exec_search \"%s\", key: \"%s\"", map, key); + mapfd = exec_map_open(map, key); + + if (mapfd >= 0) { + if (tp) + *tp = clocktime(NULL); + + return exec_parse_qanswer(mapfd, map, key, pval, tp); + } + + return errno; +} diff --git a/amd/info_file.c b/amd/info_file.c new file mode 100644 index 000000000000..3777a2aebe94 --- /dev/null +++ b/amd/info_file.c @@ -0,0 +1,253 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/info_file.c + * + */ + +/* + * Get info from file + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +#define MAX_LINE_LEN 1500 + +/* forward declarations */ +int file_init_or_mtime(mnt_map *m, char *map, time_t *tp); +int file_reload(mnt_map *m, char *map, void (*fn) (mnt_map *, char *, char *)); +int file_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp); + + +static int +read_line(char *buf, int size, FILE *fp) +{ + int done = 0; + + do { + while (fgets(buf, size, fp)) { + int len = strlen(buf); + done += len; + if (len > 1 && buf[len - 2] == '\\' && + buf[len - 1] == '\n') { + int ch; + buf += len - 2; + size -= len - 2; + *buf = '\n'; + buf[1] = '\0'; + /* + * Skip leading white space on next line + */ + while ((ch = getc(fp)) != EOF && + isascii(ch) && isspace(ch)) ; + (void) ungetc(ch, fp); + } else { + return done; + } + } + } while (size > 0 && !feof(fp) && !ferror(fp)); + + return done; +} + + +/* + * Try to locate a key in a file + */ +static int +file_search_or_reload(FILE *fp, + char *map, + char *key, + char **val, + mnt_map *m, + void (*fn) (mnt_map *m, char *, char *)) +{ + char key_val[MAX_LINE_LEN]; + int chuck = 0; + int line_no = 0; + + while (read_line(key_val, sizeof(key_val), fp)) { + char *kp; + char *cp; + char *hash; + int len = strlen(key_val); + line_no++; + + /* + * Make sure we got the whole line + */ + if (key_val[len - 1] != '\n') { + plog(XLOG_WARNING, "line %d in \"%s\" is too long", line_no, map); + chuck = 1; + } else { + key_val[len - 1] = '\0'; + } + + /* + * Strip comments + */ + hash = strchr(key_val, '#'); + if (hash) + *hash = '\0'; + + /* + * Find start of key + */ + for (kp = key_val; *kp && isascii(*kp) && isspace((int)*kp); kp++) ; + + /* + * Ignore blank lines + */ + if (!*kp) + goto again; + + /* + * Find end of key + */ + for (cp = kp; *cp && (!isascii(*cp) || !isspace((int)*cp)); cp++) ; + + /* + * Check whether key matches + */ + if (*cp) + *cp++ = '\0'; + + if (fn || (*key == *kp && STREQ(key, kp))) { + while (*cp && isascii(*cp) && isspace((int)*cp)) + cp++; + if (*cp) { + /* + * Return a copy of the data + */ + char *dc = strdup(cp); + if (fn) { + (*fn) (m, strdup(kp), dc); + } else { + *val = dc; + dlog("%s returns %s", key, dc); + } + if (!fn) + return 0; + } else { + plog(XLOG_USER, "%s: line %d has no value field", map, line_no); + } + } + + again: + /* + * If the last read didn't get a whole line then + * throw away the remainder before continuing... + */ + if (chuck) { + while (fgets(key_val, sizeof(key_val), fp) && + !strchr(key_val, '\n')) ; + chuck = 0; + } + } + + return fn ? 0 : ENOENT; +} + + +static FILE * +file_open(char *map, time_t *tp) +{ + FILE *mapf = fopen(map, "r"); + + if (mapf && tp) { + struct stat stb; + if (fstat(fileno(mapf), &stb) < 0) + *tp = clocktime(NULL); + else + *tp = stb.st_mtime; + } + return mapf; +} + + +int +file_init_or_mtime(mnt_map *m, char *map, time_t *tp) +{ + FILE *mapf = file_open(map, tp); + + if (mapf) { + fclose(mapf); + return 0; + } + return errno; +} + + +int +file_reload(mnt_map *m, char *map, void (*fn) (mnt_map *, char *, char *)) +{ + FILE *mapf = file_open(map, (time_t *) 0); + + if (mapf) { + int error = file_search_or_reload(mapf, map, 0, 0, m, fn); + (void) fclose(mapf); + return error; + } + return errno; +} + + +int +file_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp) +{ + time_t t; + FILE *mapf = file_open(map, &t); + + if (mapf) { + int error; + if (*tp < t) { + *tp = t; + error = -1; + } else { + error = file_search_or_reload(mapf, map, key, pval, 0, 0); + } + (void) fclose(mapf); + return error; + } + return errno; +} diff --git a/amd/info_hesiod.c b/amd/info_hesiod.c new file mode 100644 index 000000000000..e345c9961508 --- /dev/null +++ b/amd/info_hesiod.c @@ -0,0 +1,190 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * 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. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/info_hesiod.c + * + */ + +/* + * Get info from Hesiod + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +#define HES_PREFIX "hesiod." +#define HES_PREFLEN 7 + +#ifdef HAVE_HESIOD_INIT +/* bsdi3 does not define this extern in any header file */ +extern char **hesiod_resolve(void *context, const char *name, const char *type); +extern int hesiod_init(void **context); +static voidp hesiod_context; +#endif /* HAVE_HESIOD_INIT */ + +/* forward declarations */ +int amu_hesiod_init(mnt_map *m, char *map, time_t *tp); +int hesiod_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp); +int hesiod_isup(mnt_map *m, char *map); + +/* + * No easy way to probe the server - check the map name begins with "hesiod." + * Note: this name includes 'amu_' so as to not conflict with libhesiod's + * hesiod_init() function. + */ +int +amu_hesiod_init(mnt_map *m, char *map, time_t *tp) +{ + dlog("amu_hesiod_init(%s)", map); + *tp = 0; + +#ifdef HAVE_HESIOD_INIT + if (!hesiod_context && hesiod_init(&hesiod_context) != 0) + return ENOENT; +#endif /* HAVE_HESIOD_INIT */ + + return NSTREQ(map, HES_PREFIX, HES_PREFLEN) ? 0 : ENOENT; +} + + +/* + * Do a Hesiod nameserver call. + * Modify time is ignored by Hesiod - XXX + */ +int +hesiod_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp) +{ + char hes_key[MAXPATHLEN]; + char **rvec; +#ifndef HAVE_HESIOD_INIT + int error; +#endif /* not HAVE_HESIOD_INIT */ + + dlog("hesiod_search(m=%lx, map=%s, key=%s, pval=%lx tp=%lx)", + (unsigned long) m, map, key, (unsigned long) pval, (unsigned long) tp); + + if (key[0] == '.') + return ENOENT; + + xsnprintf(hes_key, sizeof(hes_key), "%s.%s", key, map + HES_PREFLEN); + + /* + * Call the resolver + */ + dlog("Hesiod base is: %s\n", gopt.hesiod_base); + dlog("hesiod_search: hes_resolve(%s, %s)", hes_key, gopt.hesiod_base); + if (amuDebug(D_INFO)) + _res.options |= RES_DEBUG; + +#ifdef HAVE_HESIOD_INIT + /* new style hesiod */ + rvec = hesiod_resolve(hesiod_context, hes_key, gopt.hesiod_base); +#else /* not HAVE_HESIOD_INIT */ + rvec = hes_resolve(hes_key, gopt.hesiod_base); +#endif /* not HAVE_HESIOD_INIT */ + + /* + * If a reply was forthcoming then return + * it (and free subsequent replies) + */ + if (rvec && *rvec) { + *pval = *rvec; + while (*++rvec) + XFREE(*rvec); + return 0; + } + +#ifdef HAVE_HESIOD_INIT + /* new style hesiod */ + return errno; +#else /* not HAVE_HESIOD_INIT */ + /* + * Otherwise reflect the hesiod error into a Un*x error + */ + dlog("hesiod_search: Error: %d", hes_error()); + switch (hes_error()) { + case HES_ER_NOTFOUND: + error = ENOENT; + break; + case HES_ER_CONFIG: + error = EIO; + break; + case HES_ER_NET: + error = ETIMEDOUT; + break; + default: + error = EINVAL; + break; + } + dlog("hesiod_search: Returning: %d", error); + return error; +#endif /* not HAVE_HESIOD_INIT */ +} + + +/* + * Check if Hesiod is up, so we can determine if to clear the map or not. + * Test it by querying for /defaults. + * Returns: 0 if Hesiod is down, 1 if it is up. + */ +int +hesiod_isup(mnt_map *m, char *map) +{ + int error; + char *val; + time_t mtime; + static int last_status = 1; /* assume up by default */ + + error = hesiod_search(m, map, "/defaults", &val, &mtime); + dlog("hesiod_isup(%s): %s", map, strerror(error)); + if (error != 0 && error != ENOENT) { + plog(XLOG_ERROR, + "hesiod_isup: error getting `/defaults' entry in map %s: %m", map); + last_status = 0; + return 0; /* Hesiod is down */ + } + if (last_status == 0) { /* if was down before */ + plog(XLOG_INFO, "hesiod_isup: Hesiod came back up for map %s", map); + last_status = 1; + } + return 1; /* Hesiod is up */ +} diff --git a/amd/info_ldap.c b/amd/info_ldap.c new file mode 100644 index 000000000000..36ce1c8693d3 --- /dev/null +++ b/amd/info_ldap.c @@ -0,0 +1,573 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * 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. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/info_ldap.c + * + */ + + +/* + * Get info from LDAP (Lightweight Directory Access Protocol) + * LDAP Home Page: http://www.umich.edu/~rsug/ldap/ + */ + +/* + * WARNING: as of Linux Fedora Core 5 (which comes with openldap-2.3.9), the + * ldap.h headers deprecate several functions used in this file, such as + * ldap_unbind. You get compile errors about missing extern definitions. + * Those externs are still in <ldap.h>, but surrounded by an ifdef + * LDAP_DEPRECATED. I am turning on that ifdef here, under the assumption + * that the functions may be deprecated, but they still work for this + * (older?) version of the LDAP API. It gets am-utils to compile, but it is + * not clear if it will work perfectly. + */ +#ifndef LDAP_DEPRECATED +# define LDAP_DEPRECATED 1 +#endif /* not LDAP_DEPRECATED */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + + +/* + * MACROS: + */ +#define AMD_LDAP_TYPE "ldap" +/* Time to live for an LDAP cached in an mnt_map */ +#define AMD_LDAP_TTL 3600 +#define AMD_LDAP_RETRIES 5 +#define AMD_LDAP_HOST "ldap" +#ifndef LDAP_PORT +# define LDAP_PORT 389 +#endif /* LDAP_PORT */ + +/* How timestamps are searched */ +#define AMD_LDAP_TSFILTER "(&(objectClass=amdmapTimestamp)(amdmapName=%s))" +/* How maps are searched */ +#define AMD_LDAP_FILTER "(&(objectClass=amdmap)(amdmapName=%s)(amdmapKey=%s))" +/* How timestamps are stored */ +#define AMD_LDAP_TSATTR "amdmaptimestamp" +/* How maps are stored */ +#define AMD_LDAP_ATTR "amdmapvalue" + +/* + * TYPEDEFS: + */ +typedef struct ald_ent ALD; +typedef struct cr_ent CR; +typedef struct he_ent HE_ENT; + +/* + * STRUCTURES: + */ +struct ald_ent { + LDAP *ldap; + HE_ENT *hostent; + CR *credentials; + time_t timestamp; +}; + +struct cr_ent { + char *who; + char *pw; + int method; +}; + +struct he_ent { + char *host; + int port; + struct he_ent *next; +}; + +/* + * FORWARD DECLARATIONS: + */ +static int amu_ldap_rebind(ALD *a); +static int get_ldap_timestamp(ALD *a, char *map, time_t *ts); + + +/* + * FUNCTIONS: + */ + +static void +he_free(HE_ENT *h) +{ + XFREE(h->host); + if (h->next != NULL) + he_free(h->next); + XFREE(h); +} + + +static HE_ENT * +string2he(char *s_orig) +{ + char *c, *p; + char *s; + HE_ENT *new, *old = NULL; + + if (NULL == s_orig || NULL == (s = strdup(s_orig))) + return NULL; + for (p = s; p; p = strchr(p, ',')) { + if (old != NULL) { + new = ALLOC(HE_ENT); + old->next = new; + old = new; + } else { + old = ALLOC(HE_ENT); + old->next = NULL; + } + c = strchr(p, ':'); + if (c) { /* Host and port */ + *c++ = '\0'; + old->host = strdup(p); + old->port = atoi(c); + } else + old->host = strdup(p); + + } + XFREE(s); + return (old); +} + + +static void +cr_free(CR *c) +{ + XFREE(c->who); + XFREE(c->pw); + XFREE(c); +} + + +/* + * Special ldap_unbind function to handle SIGPIPE. + * We first ignore SIGPIPE, in case a remote LDAP server was + * restarted, then we reinstall the handler. + */ +static int +amu_ldap_unbind(LDAP *ld) +{ + int e; +#ifdef HAVE_SIGACTION + struct sigaction sa; +#else /* not HAVE_SIGACTION */ + void (*handler)(int); +#endif /* not HAVE_SIGACTION */ + + dlog("amu_ldap_unbind()\n"); + +#ifdef HAVE_SIGACTION + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + sigemptyset(&(sa.sa_mask)); + sigaddset(&(sa.sa_mask), SIGPIPE); + sigaction(SIGPIPE, &sa, &sa); /* set IGNORE, and get old action */ +#else /* not HAVE_SIGACTION */ + handler = signal(SIGPIPE, SIG_IGN); +#endif /* not HAVE_SIGACTION */ + + e = ldap_unbind(ld); + +#ifdef HAVE_SIGACTION + sigemptyset(&(sa.sa_mask)); + sigaddset(&(sa.sa_mask), SIGPIPE); + sigaction(SIGPIPE, &sa, NULL); +#else /* not HAVE_SIGACTION */ + (void) signal(SIGPIPE, handler); +#endif /* not HAVE_SIGACTION */ + + return e; +} + + +static void +ald_free(ALD *a) +{ + he_free(a->hostent); + cr_free(a->credentials); + if (a->ldap != NULL) + amu_ldap_unbind(a->ldap); + XFREE(a); +} + + +int +amu_ldap_init(mnt_map *m, char *map, time_t *ts) +{ + ALD *aldh; + CR *creds; + + dlog("-> amu_ldap_init: map <%s>\n", map); + + /* + * XXX: by checking that map_type must be defined, aren't we + * excluding the possibility of automatic searches through all + * map types? + */ + if (!gopt.map_type || !STREQ(gopt.map_type, AMD_LDAP_TYPE)) { + dlog("amu_ldap_init called with map_type <%s>\n", + (gopt.map_type ? gopt.map_type : "null")); + } else { + dlog("Map %s is ldap\n", map); + } + + aldh = ALLOC(ALD); + creds = ALLOC(CR); + aldh->ldap = NULL; + aldh->hostent = string2he(gopt.ldap_hostports); + if (aldh->hostent == NULL) { + plog(XLOG_USER, "Unable to parse hostport %s for ldap map %s", + gopt.ldap_hostports ? gopt.ldap_hostports : "(null)", map); + XFREE(creds); + XFREE(aldh); + return (ENOENT); + } + creds->who = ""; + creds->pw = ""; + creds->method = LDAP_AUTH_SIMPLE; + aldh->credentials = creds; + aldh->timestamp = 0; + aldh->ldap = NULL; + dlog("Trying for %s:%d\n", aldh->hostent->host, aldh->hostent->port); + if (amu_ldap_rebind(aldh)) { + ald_free(aldh); + return (ENOENT); + } + m->map_data = (void *) aldh; + dlog("Bound to %s:%d\n", aldh->hostent->host, aldh->hostent->port); + if (get_ldap_timestamp(aldh, map, ts)) + return (ENOENT); + dlog("Got timestamp for map %s: %ld\n", map, (u_long) *ts); + + return (0); +} + + +static int +amu_ldap_rebind(ALD *a) +{ + LDAP *ld; + HE_ENT *h; + CR *c = a->credentials; + time_t now = clocktime(NULL); + int try; + + dlog("-> amu_ldap_rebind\n"); + + if (a->ldap != NULL) { + if ((a->timestamp - now) > AMD_LDAP_TTL) { + dlog("Re-establishing ldap connection\n"); + amu_ldap_unbind(a->ldap); + a->timestamp = now; + a->ldap = NULL; + } else { + /* Assume all is OK. If it wasn't we'll be back! */ + dlog("amu_ldap_rebind: timestamp OK\n"); + return (0); + } + } + + for (try=0; try<10; try++) { /* XXX: try up to 10 times (makes sense?) */ + for (h = a->hostent; h != NULL; h = h->next) { + if ((ld = ldap_open(h->host, h->port)) == NULL) { + plog(XLOG_WARNING, "Unable to ldap_open to %s:%d\n", h->host, h->port); + break; + } +#if LDAP_VERSION_MAX > LDAP_VERSION2 + /* handle LDAPv3 and heigher, if available and amd.conf-igured */ + if (gopt.ldap_proto_version > LDAP_VERSION2) { + if (!ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &gopt.ldap_proto_version)) { + dlog("amu_ldap_rebind: LDAP protocol version set to %ld\n", + gopt.ldap_proto_version); + } else { + plog(XLOG_WARNING, "Unable to set ldap protocol version to %ld\n", + gopt.ldap_proto_version); + break; + } + } +#endif /* LDAP_VERSION_MAX > LDAP_VERSION2 */ + if (ldap_bind_s(ld, c->who, c->pw, c->method) != LDAP_SUCCESS) { + plog(XLOG_WARNING, "Unable to ldap_bind to %s:%d as %s\n", + h->host, h->port, c->who); + break; + } + if (gopt.ldap_cache_seconds > 0) { +#if defined(HAVE_LDAP_ENABLE_CACHE) && defined(HAVE_EXTERN_LDAP_ENABLE_CACHE) + ldap_enable_cache(ld, gopt.ldap_cache_seconds, gopt.ldap_cache_maxmem); +#else /* not defined(HAVE_LDAP_ENABLE_CACHE) && defined(HAVE_EXTERN_LDAP_ENABLE_CACHE) */ + plog(XLOG_WARNING, "ldap_enable_cache(%ld) is not available on this system!\n", gopt.ldap_cache_seconds); +#endif /* not defined(HAVE_LDAP_ENABLE_CACHE) && defined(HAVE_EXTERN_LDAP_ENABLE_CACHE) */ + } + a->ldap = ld; + a->timestamp = now; + return (0); + } + plog(XLOG_WARNING, "Exhausted list of ldap servers, looping.\n"); + } + + plog(XLOG_USER, "Unable to (re)bind to any ldap hosts\n"); + return (ENOENT); +} + + +static int +get_ldap_timestamp(ALD *a, char *map, time_t *ts) +{ + struct timeval tv; + char **vals, *end; + char filter[MAXPATHLEN]; + int i, err = 0, nentries = 0; + LDAPMessage *res = NULL, *entry; + + dlog("-> get_ldap_timestamp: map <%s>\n", map); + + tv.tv_sec = 3; + tv.tv_usec = 0; + xsnprintf(filter, sizeof(filter), AMD_LDAP_TSFILTER, map); + dlog("Getting timestamp for map %s\n", map); + dlog("Filter is: %s\n", filter); + dlog("Base is: %s\n", gopt.ldap_base); + for (i = 0; i < AMD_LDAP_RETRIES; i++) { + err = ldap_search_st(a->ldap, + gopt.ldap_base, + LDAP_SCOPE_SUBTREE, + filter, + 0, + 0, + &tv, + &res); + if (err == LDAP_SUCCESS) + break; + if (res) { + ldap_msgfree(res); + res = NULL; + } + plog(XLOG_USER, "Timestamp LDAP search attempt %d failed: %s\n", + i + 1, ldap_err2string(err)); + if (err != LDAP_TIMEOUT) { + dlog("get_ldap_timestamp: unbinding...\n"); + amu_ldap_unbind(a->ldap); + a->ldap = NULL; + if (amu_ldap_rebind(a)) + return (ENOENT); + } + dlog("Timestamp search failed, trying again...\n"); + } + + if (err != LDAP_SUCCESS) { + *ts = 0; + plog(XLOG_USER, "LDAP timestamp search failed: %s\n", + ldap_err2string(err)); + if (res) + ldap_msgfree(res); + return (ENOENT); + } + + nentries = ldap_count_entries(a->ldap, res); + if (nentries == 0) { + plog(XLOG_USER, "No timestamp entry for map %s\n", map); + *ts = 0; + ldap_msgfree(res); + return (ENOENT); + } + + entry = ldap_first_entry(a->ldap, res); + vals = ldap_get_values(a->ldap, entry, AMD_LDAP_TSATTR); + if (ldap_count_values(vals) == 0) { + plog(XLOG_USER, "Missing timestamp value for map %s\n", map); + *ts = 0; + ldap_value_free(vals); + ldap_msgfree(res); + return (ENOENT); + } + dlog("TS value is:%s:\n", vals[0]); + + if (vals[0]) { + *ts = (time_t) strtol(vals[0], &end, 10); + if (end == vals[0]) { + plog(XLOG_USER, "Unable to decode ldap timestamp %s for map %s\n", + vals[0], map); + err = ENOENT; + } + if (!*ts > 0) { + plog(XLOG_USER, "Nonpositive timestamp %ld for map %s\n", + (u_long) *ts, map); + err = ENOENT; + } + } else { + plog(XLOG_USER, "Empty timestamp value for map %s\n", map); + *ts = 0; + err = ENOENT; + } + + ldap_value_free(vals); + ldap_msgfree(res); + dlog("The timestamp for %s is %ld (err=%d)\n", map, (u_long) *ts, err); + return (err); +} + + +int +amu_ldap_search(mnt_map *m, char *map, char *key, char **pval, time_t *ts) +{ + char **vals, filter[MAXPATHLEN], filter2[2 * MAXPATHLEN]; + char *f1, *f2; + struct timeval tv; + int i, err = 0, nvals = 0, nentries = 0; + LDAPMessage *entry, *res = NULL; + ALD *a = (ALD *) (m->map_data); + + dlog("-> amu_ldap_search: map <%s>, key <%s>\n", map, key); + + tv.tv_sec = 2; + tv.tv_usec = 0; + if (a == NULL) { + plog(XLOG_USER, "LDAP panic: no map data\n"); + return (EIO); + } + if (amu_ldap_rebind(a)) /* Check that's the handle is still valid */ + return (ENOENT); + + xsnprintf(filter, sizeof(filter), AMD_LDAP_FILTER, map, key); + /* "*" is special to ldap_search(); run through the filter escaping it. */ + f1 = filter; f2 = filter2; + while (*f1) { + if (*f1 == '*') { + *f2++ = '\\'; *f2++ = '2'; *f2++ = 'a'; + f1++; + } else { + *f2++ = *f1++; + } + } + *f2 = '\0'; + dlog("Search with filter: <%s>\n", filter2); + for (i = 0; i < AMD_LDAP_RETRIES; i++) { + err = ldap_search_st(a->ldap, + gopt.ldap_base, + LDAP_SCOPE_SUBTREE, + filter2, + 0, + 0, + &tv, + &res); + if (err == LDAP_SUCCESS) + break; + if (res) { + ldap_msgfree(res); + res = NULL; + } + plog(XLOG_USER, "LDAP search attempt %d failed: %s\n", + i + 1, ldap_err2string(err)); + if (err != LDAP_TIMEOUT) { + dlog("amu_ldap_search: unbinding...\n"); + amu_ldap_unbind(a->ldap); + a->ldap = NULL; + if (amu_ldap_rebind(a)) + return (ENOENT); + } + } + + switch (err) { + case LDAP_SUCCESS: + break; + case LDAP_NO_SUCH_OBJECT: + dlog("No object\n"); + if (res) + ldap_msgfree(res); + return (ENOENT); + default: + plog(XLOG_USER, "LDAP search failed: %s\n", + ldap_err2string(err)); + if (res) + ldap_msgfree(res); + return (EIO); + } + + nentries = ldap_count_entries(a->ldap, res); + dlog("Search found %d entries\n", nentries); + if (nentries == 0) { + ldap_msgfree(res); + return (ENOENT); + } + entry = ldap_first_entry(a->ldap, res); + vals = ldap_get_values(a->ldap, entry, AMD_LDAP_ATTR); + nvals = ldap_count_values(vals); + if (nvals == 0) { + plog(XLOG_USER, "Missing value for %s in map %s\n", key, map); + ldap_value_free(vals); + ldap_msgfree(res); + return (EIO); + } + dlog("Map %s, %s => %s\n", map, key, vals[0]); + if (vals[0]) { + *pval = strdup(vals[0]); + err = 0; + } else { + plog(XLOG_USER, "Empty value for %s in map %s\n", key, map); + err = ENOENT; + } + ldap_msgfree(res); + ldap_value_free(vals); + + return (err); +} + + +int +amu_ldap_mtime(mnt_map *m, char *map, time_t *ts) +{ + ALD *aldh = (ALD *) (m->map_data); + + if (aldh == NULL) { + dlog("LDAP panic: unable to find map data\n"); + return (ENOENT); + } + if (amu_ldap_rebind(aldh)) { + return (ENOENT); + } + if (get_ldap_timestamp(aldh, map, ts)) { + return (ENOENT); + } + return (0); +} diff --git a/amd/info_ndbm.c b/amd/info_ndbm.c new file mode 100644 index 000000000000..8a52ba9e201d --- /dev/null +++ b/amd/info_ndbm.c @@ -0,0 +1,140 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * 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. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/info_ndbm.c + * + */ + +/* + * Get info from NDBM map + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* forward declarations */ +int ndbm_init(mnt_map *m, char *map, time_t *tp); +int ndbm_mtime(mnt_map *m, char *map, time_t *tp); +int ndbm_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp); + + +static int +search_ndbm(DBM *db, char *key, char **val) +{ + datum k, v; + + k.dptr = key; + k.dsize = strlen(key) + 1; + v = dbm_fetch(db, k); + if (v.dptr) { + *val = strdup(v.dptr); + return 0; + } + return ENOENT; +} + + +int +ndbm_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp) +{ + DBM *db; + + db = dbm_open(map, O_RDONLY, 0); + if (db) { + struct stat stb; + int error; +#ifdef DBM_SUFFIX + char dbfilename[256]; + + xstrlcpy(dbfilename, map, sizeof(dbfilename)); + xstrlcat(dbfilename, DBM_SUFFIX, sizeof(dbfilename)); + error = stat(dbfilename, &stb); +#else /* not DBM_SUFFIX */ + error = fstat(dbm_pagfno(db), &stb); +#endif /* not DBM_SUFFIX */ + if (!error && *tp < stb.st_mtime) { + *tp = stb.st_mtime; + error = -1; + } else { + error = search_ndbm(db, key, pval); + } + (void) dbm_close(db); + return error; + } + return errno; +} + + +int +ndbm_init(mnt_map *m, char *map, time_t *tp) +{ + DBM *db; + + db = dbm_open(map, O_RDONLY, 0); + if (db) { + struct stat stb; + int error; +#ifdef DBM_SUFFIX + char dbfilename[256]; + + xstrlcpy(dbfilename, map, sizeof(dbfilename)); + xstrlcat(dbfilename, DBM_SUFFIX, sizeof(dbfilename)); + error = stat(dbfilename, &stb); +#else /* not DBM_SUFFIX */ + error = fstat(dbm_pagfno(db), &stb); +#endif /* not DBM_SUFFIX */ + if (error < 0) + *tp = clocktime(NULL); + else + *tp = stb.st_mtime; + dbm_close(db); + return 0; + } + return errno; +} + + +int +ndbm_mtime(mnt_map *m, char *map, time_t *tp) +{ + return ndbm_init(m,map, tp); +} diff --git a/amd/info_nis.c b/amd/info_nis.c new file mode 100644 index 000000000000..bcb87d5d02aa --- /dev/null +++ b/amd/info_nis.c @@ -0,0 +1,441 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * 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. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/info_nis.c + * + */ + +/* + * Get info from NIS map + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + + +/* + * NIS+ servers in NIS compat mode don't have yp_order() + * + * has_yp_order = 1 NIS server + * = 0 NIS+ server + * = -1 server is down + */ +static int has_yp_order = -1; + +/* forward declarations */ +int nis_reload(mnt_map *m, char *map, void (*fn) (mnt_map *, char *, char *)); +int nis_search(mnt_map *m, char *map, char *key, char **val, time_t *tp); +int nis_init(mnt_map *m, char *map, time_t *tp); +int nis_isup(mnt_map *m, char *map); +int nis_mtime(mnt_map *m, char *map, time_t *tp); + +/* typedefs */ +typedef void (*nis_callback_fxn_t)(mnt_map *, char *, char *); +#ifndef DEFINED_YPALL_CALLBACK_FXN_T +typedef int (*ypall_callback_fxn_t)(); +#endif /* DEFINED_YPALL_CALLBACK_FXN_T */ + +struct nis_callback_data { + mnt_map *ncd_m; + char *ncd_map; + nis_callback_fxn_t ncd_fn; +}; + +/* Map to the right version of yp_all */ +#ifdef HAVE_BAD_YP_ALL +# define yp_all am_yp_all +static int am_yp_all(char *indomain, char *inmap, struct ypall_callback *incallback); +#endif /* HAVE_BAD_YP_ALL */ + + +/* + * Figure out the nis domain name + */ +static int +determine_nis_domain(void) +{ + static int nis_not_running = 0; + char default_domain[YPMAXDOMAIN]; + + if (nis_not_running) + return ENOENT; + + if (getdomainname(default_domain, sizeof(default_domain)) < 0) { + nis_not_running = 1; + plog(XLOG_ERROR, "getdomainname: %m"); + return EIO; + } + if (!*default_domain) { + nis_not_running = 1; + plog(XLOG_WARNING, "NIS domain name is not set. NIS ignored."); + return ENOENT; + } + gopt.nis_domain = strdup(default_domain); + + return 0; +} + + +/* + * Callback from yp_all + */ +static int +callback(int status, char *key, int kl, char *val, int vl, char *data) +{ + struct nis_callback_data *ncdp = (struct nis_callback_data *) data; + + if (status == YP_TRUE) { + + /* add to list of maps */ + char *kp = strnsave(key, kl); + char *vp = strnsave(val, vl); + + (*ncdp->ncd_fn) (ncdp->ncd_m, kp, vp); + + /* we want more ... */ + return FALSE; + + } else { + + /* NOMORE means end of map - otherwise log error */ + if (status != YP_NOMORE) { + /* check what went wrong */ + int e = ypprot_err(status); + + plog(XLOG_ERROR, "yp enumeration of %s: %s, status=%d, e=%d", + ncdp->ncd_map, yperr_string(e), status, e); + } + return TRUE; + } +} + + +int +nis_reload(mnt_map *m, char *map, void (*fn) (mnt_map *, char *, char *)) +{ + int error; + struct nis_callback_data data; + struct ypall_callback cbinfo; + + if (!gopt.nis_domain) { + error = determine_nis_domain(); + if (error) + return error; + } + data.ncd_m = m; + data.ncd_map = map; + data.ncd_fn = fn; + cbinfo.data = (voidp) &data; + cbinfo.foreach = (ypall_callback_fxn_t) callback; + + /* + * If you are using NIS and your yp_all function is "broken", you have to + * get it fixed. The bug in yp_all() is that it does not close a TCP + * connection to ypserv, and this ypserv runs out of open file descriptors, + * getting into an infinite loop, thus all YP clients eventually unbind + * and hang too. + */ + error = yp_all(gopt.nis_domain, map, &cbinfo); + + if (error) + plog(XLOG_ERROR, "error grabbing nis map of %s: %s", map, yperr_string(ypprot_err(error))); + return error; +} + + +/* + * Check if NIS is up, so we can determine if to clear the map or not. + * Test it by checking the yp order. + * Returns: 0 if NIS is down, 1 if it is up. + */ +int +nis_isup(mnt_map *m, char *map) +{ + YP_ORDER_OUTORDER_TYPE order; + int error; + char *master; + static int last_status = 1; /* assume up by default */ + + switch (has_yp_order) { + case 1: + /* + * NIS server with yp_order + */ + error = yp_order(gopt.nis_domain, map, &order); + if (error != 0) { + plog(XLOG_ERROR, + "nis_isup: error getting the order of map %s: %s", + map, yperr_string(ypprot_err(error))); + last_status = 0; + return 0; /* NIS is down */ + } + break; + + case 0: + /* + * NIS+ server without yp_order + */ + error = yp_master(gopt.nis_domain, map, &master); + if (error != 0) { + plog(XLOG_ERROR, + "nis_isup: error getting the master of map %s: %s", + map, yperr_string(ypprot_err(error))); + last_status = 0; + return 0; /* NIS+ is down */ + } + break; + + default: + /* + * server was down + */ + last_status = 0; + } + + if (last_status == 0) { /* reinitialize if was down before */ + time_t dummy; + error = nis_init(m, map, &dummy); + if (error) + return 0; /* still down */ + plog(XLOG_INFO, "nis_isup: NIS came back up for map %s", map); + last_status = 1; + } + return 1; /* NIS is up */ +} + + +/* + * Try to locate a key using NIS. + */ +int +nis_search(mnt_map *m, char *map, char *key, char **val, time_t *tp) +{ + int outlen; + int res; + YP_ORDER_OUTORDER_TYPE order; + + /* + * Make sure domain initialized + */ + if (!gopt.nis_domain) { + int error = determine_nis_domain(); + if (error) + return error; + } + + + switch (has_yp_order) { + case 1: + /* + * NIS server with yp_order + * Check if map has changed + */ + if (yp_order(gopt.nis_domain, map, &order)) + return EIO; + if ((time_t) order > *tp) { + *tp = (time_t) order; + return -1; + } + break; + + case 0: + /* + * NIS+ server without yp_order + * Check if timeout has expired to invalidate the cache + */ + order = time(NULL); + if ((time_t)order - *tp > gopt.am_timeo) { + *tp = (time_t)order; + return(-1); + } + break; + + default: + /* + * server was down + */ + if (nis_isup(m, map)) + return -1; + return EIO; + } + + /* + * Lookup key + */ + res = yp_match(gopt.nis_domain, map, key, strlen(key), val, &outlen); + + /* + * Do something interesting with the return code + */ + switch (res) { + case 0: + return 0; + + case YPERR_KEY: + return ENOENT; + + default: + plog(XLOG_ERROR, "nis_search: %s: %s", map, yperr_string(res)); + return EIO; + } +} + + +int +nis_init(mnt_map *m, char *map, time_t *tp) +{ + YP_ORDER_OUTORDER_TYPE order; + int yp_order_result; + char *master; + + if (!gopt.nis_domain) { + int error = determine_nis_domain(); + if (error) + return error; + } + + /* + * To see if the map exists, try to find + * a master for it. + */ + yp_order_result = yp_order(gopt.nis_domain, map, &order); + switch (yp_order_result) { + case 0: + /* NIS server found */ + has_yp_order = 1; + *tp = (time_t) order; + dlog("NIS master for %s@%s has order %lu", map, gopt.nis_domain, (unsigned long) order); + break; + case YPERR_YPERR: + /* NIS+ server found ! */ + has_yp_order = 0; + /* try yp_master() instead */ + if (yp_master(gopt.nis_domain, map, &master)) { + return ENOENT; + } else { + dlog("NIS master for %s@%s is a NIS+ server", map, gopt.nis_domain); + /* Use fake timestamps */ + *tp = time(NULL); + } + break; + default: + /* server is down */ + has_yp_order = -1; + return ENOENT; + } + return 0; +} + + +int +nis_mtime(mnt_map *m, char *map, time_t *tp) +{ + return nis_init(m, map, tp); +} + + +#ifdef HAVE_BAD_YP_ALL +/* + * If you are using NIS and your yp_all function is "broken", use an + * alternate code which avoids a bug in yp_all(). The bug in yp_all() is + * that it does not close a TCP connection to ypserv, and this ypserv runs + * out of open filedescriptors, getting into an infinite loop, thus all YP + * clients eventually unbind and hang too. + * + * Systems known to be plagued with this bug: + * earlier SunOS 4.x + * all irix systems (at this time, up to 6.4 was checked) + * + * -Erez Zadok <ezk@cs.columbia.edu> + * -James Tanis <jtt@cs.columbia.edu> */ +static int +am_yp_all(char *indomain, char *inmap, struct ypall_callback *incallback) +{ + int i, j; + char *outkey, *outval; + int outkeylen, outvallen; + char *outkey_old; + int outkeylen_old; + + plog(XLOG_INFO, "NIS map %s reloading using am_yp_all", inmap); + + i = yp_first(indomain, inmap, &outkey, &outkeylen, &outval, &outvallen); + if (i) { + plog(XLOG_ERROR, "yp_first() returned error: %s\n", yperr_string(i)); + } + do { + j = (incallback->foreach)(YP_TRUE, + outkey, + outkeylen, + outval, + outvallen, + incallback->data); + if (j != FALSE) /* terminate loop */ + break; + + /* + * We have to manually free all char ** arguments to yp_first/yp_next + * outval must be freed *before* calling yp_next again, outkey can be + * freed as outkey_old *after* the call (this saves one call to + * strnsave). + */ + XFREE(outval); + outkey_old = outkey; + outkeylen_old = outkeylen; + i = yp_next(indomain, + inmap, + outkey_old, + outkeylen_old, + &outkey, + &outkeylen, + &outval, + &outvallen); + XFREE(outkey_old); + } while (!i); + if (i) { + dlog("yp_next() returned error: %s\n", yperr_string(i)); + } + if (i == YPERR_NOMORE) + return 0; + return i; +} +#endif /* HAVE_BAD_YP_ALL */ diff --git a/amd/info_nisplus.c b/amd/info_nisplus.c new file mode 100644 index 000000000000..d392b48fed6a --- /dev/null +++ b/amd/info_nisplus.c @@ -0,0 +1,307 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * 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. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/info_nisplus.c + * + */ + +/* + * Get info from NIS+ (version 3) map + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +#define NISPLUS_KEY "key=" +#define NISPLUS_ORGDIR ".org_dir" + +struct nis_callback_data { + mnt_map *ncd_m; + char *ncd_map; + void (*ncd_fn)(); +}; + +struct nisplus_search_callback_data { + nis_name key; + char *value; +}; + + +static int +nisplus_callback(const nis_name key, const nis_object *value, voidp opaquedata) +{ + char *kp = strnsave(ENTRY_VAL(value, 0), ENTRY_LEN(value, 0)); + char *vp = strnsave(ENTRY_VAL(value, 1), ENTRY_LEN(value, 1)); + struct nis_callback_data *data = (struct nis_callback_data *) opaquedata; + + dlog("NISplus callback for <%s,%s>", kp, vp); + + (*data->ncd_fn) (data->ncd_m, kp, vp); + + /* + * We want more ... + */ + return FALSE; +} + + +int +nisplus_reload(mnt_map *m, char *map, void (*fn) ()) +{ + int error = 0; + struct nis_callback_data data; + nis_result *result; + char *org; /* if map does not have ".org_dir" then append it */ + nis_name map_name; + size_t l; + + org = strstr(map, NISPLUS_ORGDIR); + if (org == NULL) + org = NISPLUS_ORGDIR; + else + org = ""; + + /* make some room for the NIS map_name */ + l = strlen(map) + sizeof(NISPLUS_ORGDIR); + map_name = xmalloc(l); + if (map_name == NULL) { + plog(XLOG_ERROR, "Unable to create map_name %s: %s", + map, strerror(ENOMEM)); + return ENOMEM; + } + xsnprintf(map_name, l, "%s%s", map, org); + + data.ncd_m = m; + data.ncd_map = map_name; + data.ncd_fn = fn; + + dlog("NISplus reload for %s", map); + + result = nis_list(map_name, + EXPAND_NAME | FOLLOW_LINKS | FOLLOW_PATH, + (int (*)()) nisplus_callback, + &data); + + /* free off the NIS map_name */ + XFREE(map_name); + + if (result->status != NIS_SUCCESS && result->status != NIS_CBRESULTS) + error = 1; + + if (error) + plog(XLOG_ERROR, "error grabbing nisplus map of %s: %s", + map, + nis_sperrno(result->status)); + + nis_freeresult(result); + return error; +} + + +static int +nisplus_search_callback(const nis_name key, const nis_object *value, voidp opaquedata) +{ + struct nisplus_search_callback_data *data = (struct nisplus_search_callback_data *) opaquedata; + + dlog("NISplus search callback for <%s>", ENTRY_VAL(value, 0)); + dlog("NISplus search callback value <%s>", ENTRY_VAL(value, 1)); + + data->value = strnsave(ENTRY_VAL(value, 1), ENTRY_LEN(value, 1)); + return TRUE; +} + + +/* + * Try to locate a key using NIS+. + */ +int +nisplus_search(mnt_map *m, char *map, char *key, char **val, time_t *tp) +{ + nis_result *result; + int error = 0; + struct nisplus_search_callback_data data; + nis_name index; + char *org; /* if map does not have ".org_dir" then append it */ + size_t l; + + org = strstr(map, NISPLUS_ORGDIR); + if (org == NULL) + org = NISPLUS_ORGDIR; + else + org = ""; + + /* make some room for the NIS index */ + l = sizeof('[') /* for opening selection criteria */ + + sizeof(NISPLUS_KEY) + + strlen(key) + + sizeof(']') /* for closing selection criteria */ + + sizeof(',') /* + 1 for , separator */ + + strlen(map) + + sizeof(NISPLUS_ORGDIR); + index = xmalloc(l); + if (index == NULL) { + plog(XLOG_ERROR, + "Unable to create index %s: %s", + map, + strerror(ENOMEM)); + return ENOMEM; + } + xsnprintf(index, l, "[%s%s],%s%s", NISPLUS_KEY, key, map, org); + + data.key = key; + data.value = NULL; + + dlog("NISplus search for %s", index); + + result = nis_list(index, + EXPAND_NAME | FOLLOW_LINKS | FOLLOW_PATH, + (int (*)()) nisplus_search_callback, + &data); + + /* free off the NIS index */ + XFREE(index); + + if (result == NULL) { + plog(XLOG_ERROR, "nisplus_search: %s: %s", map, strerror(ENOMEM)); + return ENOMEM; + } + + /* + * Do something interesting with the return code + */ + switch (result->status) { + case NIS_SUCCESS: + case NIS_CBRESULTS: + + if (data.value == NULL) { + nis_object *value = result->objects.objects_val; + dlog("NISplus search found <nothing>"); + dlog("NISplus search for %s: %s(%d)", + map, nis_sperrno(result->status), result->status); + + if (value != NULL) + data.value = strnsave(ENTRY_VAL(value, 1), ENTRY_LEN(value, 1)); + } + *val = data.value; + + if (*val) { + error = 0; + dlog("NISplus search found %s", *val); + } else { + error = ENOENT; + dlog("NISplus search found nothing"); + } + + *tp = 0; + break; + + case NIS_NOSUCHNAME: + dlog("NISplus search returned %d", result->status); + error = ENOENT; + break; + + default: + plog(XLOG_ERROR, "nisplus_search: %s: %s", map, nis_sperrno(result->status)); + error = EIO; + break; + } + nis_freeresult(result); + + return error; +} + + +int +nisplus_init(mnt_map *m, char *map, time_t *tp) +{ + nis_result *result; + char *org; /* if map does not have ".org_dir" then append it */ + nis_name map_name; + int error = 0; + size_t l; + + org = strstr(map, NISPLUS_ORGDIR); + if (org == NULL) + org = NISPLUS_ORGDIR; + else + org = ""; + + /* make some room for the NIS map_name */ + l = strlen(map) + sizeof(NISPLUS_ORGDIR); + map_name = xmalloc(l); + if (map_name == NULL) { + plog(XLOG_ERROR, + "Unable to create map_name %s: %s", + map, + strerror(ENOMEM)); + return ENOMEM; + } + xsnprintf(map_name, l, "%s%s", map, org); + + result = nis_lookup(map_name, (EXPAND_NAME | FOLLOW_LINKS | FOLLOW_PATH)); + + /* free off the NIS map_name */ + XFREE(map_name); + + if (result == NULL) { + plog(XLOG_ERROR, "NISplus init <%s>: %s", map, strerror(ENOMEM)); + return ENOMEM; + } + + if (result->status != NIS_SUCCESS) { + dlog("NISplus init <%s>: %s (%d)", + map, nis_sperrno(result->status), result->status); + + error = ENOENT; + } + + *tp = 0; /* no time */ + nis_freeresult(result); + return error; +} + + +int +nisplus_mtime(mnt_map *m, char *map, time_t *tp) +{ + return nisplus_init(m,map, tp); +} diff --git a/amd/info_passwd.c b/amd/info_passwd.c new file mode 100644 index 000000000000..c8bf388b1d65 --- /dev/null +++ b/amd/info_passwd.c @@ -0,0 +1,192 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/info_passwd.c + * + */ + +/* + * Get info from password "file" + * + * This is experimental and probably doesn't do what you expect. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +#define PASSWD_MAP "/etc/passwd" + +/* forward declarations */ +int passwd_init(mnt_map *m, char *map, time_t *tp); +int passwd_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp); + + +/* + * Nothing to probe - check the map name is PASSWD_MAP. + */ +int +passwd_init(mnt_map *m, char *map, time_t *tp) +{ + *tp = 0; + + /* + * Recognize the old format "PASSWD_MAP" + * Uses default return string + * "type:=nfs;rfs:=/${var0}/${var1};rhost:=${var1};sublink:=${var2};fs:=${autodir}${var3}" + */ + if (STREQ(map, PASSWD_MAP)) + return 0; + /* + * Recognize the new format "PASSWD_MAP:pval-format" + */ + if (!NSTREQ(map, PASSWD_MAP, sizeof(PASSWD_MAP) - 1)) + return ENOENT; + if (map[sizeof(PASSWD_MAP)-1] != ':') + return ENOENT; + + return 0; +} + + +/* + * Grab the entry via the getpwname routine + * Modify time is ignored by passwd - XXX + */ +int +passwd_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp) +{ + char *dir = 0; + struct passwd *pw; + + if (STREQ(key, "/defaults")) { + *pval = strdup("type:=nfs"); + return 0; + } + pw = getpwnam(key); + + if (pw) { + /* + * We chop the home directory up as follows: + * /anydir/dom1/dom2/dom3/user + * + * and return + * rfs:=/anydir/dom3;rhost:=dom3.dom2.dom1;sublink:=user + * and now have + * var0:=pw-prefix:=anydir + * var1:=pw-rhost:=dom3.dom2.dom1 + * var2:=pw-user:=user + * var3:=pw-home:=/anydir/dom1/dom2/dom3/user + * + * This allows cross-domain entries in your passwd file. + * ... but forget about security! + */ + char *user; + char *p, *q; + char val[MAXPATHLEN]; + char rhost[MAXHOSTNAMELEN]; + dir = strdup(pw->pw_dir); + + /* + * Find user name. If no / then Invalid... + */ + user = strrchr(dir, '/'); + if (!user) + goto enoent; + *user++ = '\0'; + + /* + * Find start of host "path". If no / then Invalid... + */ + p = strchr(dir + 1, '/'); + if (!p) + goto enoent; + *p++ = '\0'; + + /* + * At this point, p is dom1/dom2/dom3 + * Copy, backwards, into rhost replacing + * / with . + */ + rhost[0] = '\0'; + do { + q = strrchr(p, '/'); + if (q) { + xstrlcat(rhost, q + 1, sizeof(rhost)); + xstrlcat(rhost, ".", sizeof(rhost)); + *q = '\0'; + } else { + xstrlcat(rhost, p, sizeof(rhost)); + } + } while (q); + + /* + * Sanity check + */ + if (*rhost == '\0' || *user == '\0' || *dir == '\0') + goto enoent; + + /* + * Make up return string + */ + q = strchr(rhost, '.'); + if (q) + *q = '\0'; + p = strchr(map, ':'); + if (p) + p++; + else + p = "type:=nfs;rfs:=/${var0}/${var1};rhost:=${var1};sublink:=${var2};fs:=${autodir}${var3}"; + xsnprintf(val, sizeof(val), "var0:=%s;var1:=%s;var2:=%s;var3:=%s;%s", + dir+1, rhost, user, pw->pw_dir, p); + dlog("passwd_search: map=%s key=%s -> %s", map, key, val); + if (q) + *q = '.'; + *pval = strdup(val); + return 0; + } + +enoent: + if (dir) + XFREE(dir); + + return ENOENT; +} diff --git a/amd/info_union.c b/amd/info_union.c new file mode 100644 index 000000000000..8f0631c64ac9 --- /dev/null +++ b/amd/info_union.c @@ -0,0 +1,147 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/info_union.c + * + */ + +/* + * Get info from the system namespace + * + * NOTE: Cannot handle reads back through the automounter. + * THIS WILL CAUSE A DEADLOCK! + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +#define UNION_PREFIX "union:" +#define UNION_PREFLEN 6 + +/* forward declarations */ +int union_init(mnt_map *m, char *map, time_t *tp); +int union_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp); +int union_reload(mnt_map *m, char *map, void (*fn) (mnt_map *, char *, char *)); + + +/* + * No way to probe - check the map name begins with "union:" + */ +int +union_init(mnt_map *m, char *map, time_t *tp) +{ + *tp = 0; + return NSTREQ(map, UNION_PREFIX, UNION_PREFLEN) ? 0 : ENOENT; +} + + +int +union_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp) +{ + char *mapd = strdup(map + UNION_PREFLEN); + char **v = strsplit(mapd, ':', '\"'); + char **p; + size_t l; + + for (p = v; p[1]; p++) ; + l = strlen(*p) + 5; + *pval = xmalloc(l); + xsnprintf(*pval, l, "fs:=%s", *p); + XFREE(mapd); + XFREE(v); + return 0; +} + + +int +union_reload(mnt_map *m, char *map, void (*fn) (mnt_map *, char *, char *)) +{ + char *mapd = strdup(map + UNION_PREFLEN); + char **v = strsplit(mapd, ':', '\"'); + char **dir; + + /* + * Add fake /defaults entry + */ + (*fn) (m, strdup("/defaults"), strdup("type:=link;opts:=nounmount;sublink:=${key}")); + + for (dir = v; *dir; dir++) { + size_t l; + struct dirent *dp; + + DIR *dirp = opendir(*dir); + if (!dirp) { + plog(XLOG_USER, "Cannot read directory %s: %m", *dir); + continue; + } + l = strlen(*dir) + 5; + + dlog("Reading directory %s...", *dir); + while ((dp = readdir(dirp))) { + char *val, *dpname = &dp->d_name[0]; + if (dpname[0] == '.' && + (dpname[1] == '\0' || + (dpname[1] == '.' && dpname[2] == '\0'))) + continue; + + dlog("... gives %s", dp->d_name); + val = xmalloc(l); + xsnprintf(val, l + 5, "fs:=%s", *dir); + (*fn) (m, strdup(dp->d_name), val); + } + closedir(dirp); + } + + /* + * Add wildcard entry + */ + { + size_t l = strlen(*(dir-1)) + 5; + char *val = xmalloc(l); + + xsnprintf(val, l, "fs:=%s", *(dir-1)); + (*fn) (m, strdup("*"), val); + } + XFREE(mapd); + XFREE(v); + return 0; +} diff --git a/amd/map.c b/amd/map.c new file mode 100644 index 000000000000..8696dfd6984d --- /dev/null +++ b/amd/map.c @@ -0,0 +1,1072 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/map.c + * + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +#define smallest_t(t1, t2) (t1 != NEVER ? (t2 != NEVER ? (t1 < t2 ? t1 : t2) : t1) : t2) +#define IGNORE_FLAGS (MFF_MOUNTING|MFF_UNMOUNTING|MFF_RESTART) +#define new_gen() (am_gen++) + +/* + * Generation Numbers. + * + * Generation numbers are allocated to every node created + * by amd. When a filehandle is computed and sent to the + * kernel, the generation number makes sure that it is safe + * to reallocate a node slot even when the kernel has a cached + * reference to its old incarnation. + * No garbage collection is done, since it is assumed that + * there is no way that 2^32 generation numbers could ever + * be allocated by a single run of amd - there is simply + * not enough cpu time available. + * Famous last words... -Ion + */ +static u_int am_gen = 2; /* Initial generation number */ +static int timeout_mp_id; /* Id from last call to timeout */ + +static am_node *root_node; /* The root of the mount tree */ +static am_node **exported_ap = (am_node **) 0; +static int exported_ap_size = 0; +static int first_free_map = 0; /* First available free slot */ +static int last_used_map = -1; /* Last unavailable used slot */ + + +/* + * This is the default attributes field which + * is copied into every new node to be created. + * The individual filesystem fs_init() routines + * patch the copy to represent the particular + * details for the relevant filesystem type + */ +static nfsfattr gen_fattr = +{ + NFLNK, /* type */ + NFSMODE_LNK | 0777, /* mode */ + 1, /* nlink */ + 0, /* uid */ + 0, /* gid */ + 0, /* size */ + 4096, /* blocksize */ + 0, /* rdev */ + 1, /* blocks */ + 0, /* fsid */ + 0, /* fileid */ + {0, 0}, /* atime */ + {0, 0}, /* mtime */ + {0, 0}, /* ctime */ +}; + +/* forward declarations */ +static int unmount_node(opaque_t arg); +static void exported_ap_free(am_node *mp); +static void remove_am(am_node *mp); +static am_node *get_root_ap(char *dir); + + +/* + * Iterator functions for exported_ap[] + */ +am_node * +get_first_exported_ap(int *index) +{ + *index = -1; + return get_next_exported_ap(index); +} + + +am_node * +get_next_exported_ap(int *index) +{ + (*index)++; + while (*index < exported_ap_size) { + if (exported_ap[*index] != NULL) + return exported_ap[*index]; + (*index)++; + } + return NULL; +} + + +/* + * Get exported_ap by index + */ +am_node * +get_exported_ap(int index) +{ + if (index < 0 || index >= exported_ap_size) + return 0; + return exported_ap[index]; +} + + +/* + * Get exported_ap by path + */ +am_node * +path_to_exported_ap(char *path) +{ + int index; + am_node *mp; + + mp = get_first_exported_ap(&index); + while (mp != NULL) { + if (STREQ(mp->am_path, path)) + break; + mp = get_next_exported_ap(&index); + } + return mp; +} + + +/* + * Resize exported_ap map + */ +static int +exported_ap_realloc_map(int nsize) +{ + /* + * this shouldn't happen, but... + */ + if (nsize < 0 || nsize == exported_ap_size) + return 0; + + exported_ap = (am_node **) xrealloc((voidp) exported_ap, nsize * sizeof(am_node *)); + + if (nsize > exported_ap_size) + memset((char *) (exported_ap + exported_ap_size), 0, + (nsize - exported_ap_size) * sizeof(am_node *)); + exported_ap_size = nsize; + + return 1; +} + + + +am_node * +get_ap_child(am_node *mp, char *fname) +{ + am_node *new_mp; + mntfs *mf = mp->am_mnt; + + /* + * Allocate a new map + */ + new_mp = exported_ap_alloc(); + if (new_mp) { + /* + * Fill it in + */ + init_map(new_mp, fname); + + /* + * Put it in the table + */ + insert_am(new_mp, mp); + + /* + * Fill in some other fields, + * path and mount point. + * + * bugfix: do not prepend old am_path if direct map + * <wls@astro.umd.edu> William Sebok + */ + new_mp->am_path = str3cat(new_mp->am_path, + (mf->mf_fsflags & FS_DIRECT) + ? "" + : mp->am_path, + *fname == '/' ? "" : "/", fname); + dlog("setting path to %s", new_mp->am_path); + } + + return new_mp; +} + +/* + * Allocate a new mount slot and create + * a new node. + * Fills in the map number of the node, + * but leaves everything else uninitialized. + */ +am_node * +exported_ap_alloc(void) +{ + am_node *mp, **mpp; + + /* + * First check if there are any slots left, realloc if needed + */ + if (first_free_map >= exported_ap_size) + if (!exported_ap_realloc_map(exported_ap_size + NEXP_AP)) + return 0; + + /* + * Grab the next free slot + */ + mpp = exported_ap + first_free_map; + mp = *mpp = ALLOC(struct am_node); + memset((char *) mp, 0, sizeof(struct am_node)); + + mp->am_mapno = first_free_map++; + + /* + * Update free pointer + */ + while (first_free_map < exported_ap_size && exported_ap[first_free_map]) + first_free_map++; + + if (first_free_map > last_used_map) + last_used_map = first_free_map - 1; + + return mp; +} + + +/* + * Free a mount slot + */ +static void +exported_ap_free(am_node *mp) +{ + /* + * Sanity check + */ + if (!mp) + return; + + /* + * Zero the slot pointer to avoid double free's + */ + exported_ap[mp->am_mapno] = 0; + + /* + * Update the free and last_used indices + */ + if (mp->am_mapno == last_used_map) + while (last_used_map >= 0 && exported_ap[last_used_map] == 0) + --last_used_map; + + if (first_free_map > mp->am_mapno) + first_free_map = mp->am_mapno; + + /* + * Free the mount node, and zero out it's internal struct data. + */ + memset((char *) mp, 0, sizeof(am_node)); + XFREE(mp); +} + + +/* + * Insert mp into the correct place, + * where p_mp is its parent node. + * A new node gets placed as the youngest sibling + * of any other children, and the parent's child + * pointer is adjusted to point to the new child node. + */ +void +insert_am(am_node *mp, am_node *p_mp) +{ + /* + * If this is going in at the root then flag it + * so that it cannot be unmounted by amq. + */ + if (p_mp == root_node) + mp->am_flags |= AMF_ROOT; + /* + * Fill in n-way links + */ + mp->am_parent = p_mp; + mp->am_osib = p_mp->am_child; + if (mp->am_osib) + mp->am_osib->am_ysib = mp; + p_mp->am_child = mp; +#ifdef HAVE_FS_AUTOFS + if (p_mp->am_mnt->mf_flags & MFF_IS_AUTOFS) + mp->am_flags |= AMF_AUTOFS; +#endif /* HAVE_FS_AUTOFS */ +} + + +/* + * Remove am from its place in the mount tree + */ +static void +remove_am(am_node *mp) +{ + /* + * 1. Consistency check + */ + if (mp->am_child && mp->am_parent) { + plog(XLOG_WARNING, "children of \"%s\" still exist - deleting anyway", mp->am_path); + } + + /* + * 2. Update parent's child pointer + */ + if (mp->am_parent && mp->am_parent->am_child == mp) + mp->am_parent->am_child = mp->am_osib; + + /* + * 3. Unlink from sibling chain + */ + if (mp->am_ysib) + mp->am_ysib->am_osib = mp->am_osib; + if (mp->am_osib) + mp->am_osib->am_ysib = mp->am_ysib; +} + + +/* + * Compute a new time to live value for a node. + */ +void +new_ttl(am_node *mp) +{ + mp->am_timeo_w = 0; + mp->am_ttl = clocktime(&mp->am_fattr.na_atime); + mp->am_ttl += mp->am_timeo; /* sun's -tl option */ +} + + +void +mk_fattr(nfsfattr *fattr, nfsftype vntype) +{ + switch (vntype) { + case NFDIR: + fattr->na_type = NFDIR; + fattr->na_mode = NFSMODE_DIR | 0555; + fattr->na_nlink = 2; + fattr->na_size = 512; + break; + case NFLNK: + fattr->na_type = NFLNK; + fattr->na_mode = NFSMODE_LNK | 0777; + fattr->na_nlink = 1; + fattr->na_size = 0; + break; + default: + plog(XLOG_FATAL, "Unknown fattr type %d - ignored", vntype); + break; + } +} + + +/* + * Initialize an allocated mount node. + * It is assumed that the mount node was b-zero'd + * before getting here so anything that would + * be set to zero isn't done here. + */ +void +init_map(am_node *mp, char *dir) +{ + /* + * mp->am_mapno is initialized by exported_ap_alloc + * other fields don't need to be set to zero. + */ + mp->am_mnt = new_mntfs(); + mp->am_mfarray = 0; + mp->am_name = strdup(dir); + mp->am_path = strdup(dir); + mp->am_gen = new_gen(); +#ifdef HAVE_FS_AUTOFS + mp->am_autofs_fh = 0; +#endif /* HAVE_FS_AUTOFS */ + + mp->am_timeo = gopt.am_timeo; + mp->am_attr.ns_status = NFS_OK; + mp->am_fattr = gen_fattr; + mp->am_fattr.na_fsid = 42; + mp->am_fattr.na_fileid = mp->am_gen; + clocktime(&mp->am_fattr.na_atime); + /* next line copies a "struct nfstime" among several fields */ + mp->am_fattr.na_mtime = mp->am_fattr.na_ctime = mp->am_fattr.na_atime; + + new_ttl(mp); + mp->am_stats.s_mtime = mp->am_fattr.na_atime.nt_seconds; + mp->am_dev = -1; + mp->am_rdev = -1; +} + + +/* + * Free a mount node. + * The node must be already unmounted. + */ +void +free_map(am_node *mp) +{ + remove_am(mp); + + if (mp->am_link) + XFREE(mp->am_link); + if (mp->am_name) + XFREE(mp->am_name); + if (mp->am_path) + XFREE(mp->am_path); + if (mp->am_pref) + XFREE(mp->am_pref); + if (mp->am_transp) + XFREE(mp->am_transp); + + if (mp->am_mnt) + free_mntfs(mp->am_mnt); + + if (mp->am_mfarray) { + mntfs **temp_mf; + for (temp_mf = mp->am_mfarray; *temp_mf; temp_mf++) + free_mntfs(*temp_mf); + XFREE(mp->am_mfarray); + } + +#ifdef HAVE_FS_AUTOFS + if (mp->am_autofs_fh) + autofs_release_fh(mp); +#endif /* HAVE_FS_AUTOFS */ + + exported_ap_free(mp); +} + + +static am_node * +find_ap_recursive(char *dir, am_node *mp) +{ + if (mp) { + am_node *mp2; + if (STREQ(mp->am_path, dir)) + return mp; + + if ((mp->am_mnt->mf_flags & MFF_MOUNTED) && + STREQ(mp->am_mnt->mf_mount, dir)) + return mp; + + mp2 = find_ap_recursive(dir, mp->am_osib); + if (mp2) + return mp2; + return find_ap_recursive(dir, mp->am_child); + } + + return 0; +} + + +/* + * Find the mount node corresponding to dir. dir can match either the + * automount path or, if the node is mounted, the mount location. + */ +am_node * +find_ap(char *dir) +{ + int i; + + for (i = last_used_map; i >= 0; --i) { + am_node *mp = exported_ap[i]; + if (mp && (mp->am_flags & AMF_ROOT)) { + mp = find_ap_recursive(dir, exported_ap[i]); + if (mp) { + return mp; + } + } + } + + return 0; +} + + +/* + * Find the mount node corresponding + * to the mntfs structure. + */ +am_node * +find_mf(mntfs *mf) +{ + int i; + + for (i = last_used_map; i >= 0; --i) { + am_node *mp = exported_ap[i]; + if (mp && mp->am_mnt == mf) + return mp; + } + + return 0; +} + + +/* + * Get the filehandle for a particular named directory. + * This is used during the bootstrap to tell the kernel + * the filehandles of the initial automount points. + */ +am_nfs_fh * +get_root_nfs_fh(char *dir) +{ + static am_nfs_fh nfh; + am_node *mp = get_root_ap(dir); + if (mp) { + mp_to_fh(mp, &nfh); + return &nfh; + } + + /* + * Should never get here... + */ + plog(XLOG_ERROR, "Can't find root filehandle for %s", dir); + + return 0; +} + + +static am_node * +get_root_ap(char *dir) +{ + am_node *mp = find_ap(dir); + + if (mp && mp->am_parent == root_node) + return mp; + + return 0; +} + + +/* + * Timeout all nodes waiting on + * a given Fserver. + */ +void +map_flush_srvr(fserver *fs) +{ + int i; + int done = 0; + + for (i = last_used_map; i >= 0; --i) { + am_node *mp = exported_ap[i]; + if (mp && mp->am_mnt && mp->am_mnt->mf_server == fs) { + plog(XLOG_INFO, "Flushed %s; dependent on %s", mp->am_path, fs->fs_host); + mp->am_ttl = clocktime(NULL); + done = 1; + } + } + if (done) + reschedule_timeout_mp(); +} + + +/* + * Mount a top level automount node + * by calling lookup in the parent + * (root) node which will cause the + * automount node to be automounted. + */ +int +mount_auto_node(char *dir, opaque_t arg) +{ + int error = 0; + am_node *mp = (am_node *) arg; + am_node *new_mp; + + new_mp = mp->am_mnt->mf_ops->lookup_child(mp, dir, &error, VLOOK_CREATE); + if (new_mp && error < 0) { + /* + * We can't allow the fileid of the root node to change. + * Should be ok to force it to 1, always. + */ + new_mp->am_gen = new_mp->am_fattr.na_fileid = 1; + + new_mp = mp->am_mnt->mf_ops->mount_child(new_mp, &error); + } + + if (error > 0) { + errno = error; /* XXX */ + plog(XLOG_ERROR, "Could not mount %s: %m", dir); + } + return error; +} + + +/* + * Cause all the top-level mount nodes + * to be automounted + */ +int +mount_exported(void) +{ + /* + * Iterate over all the nodes to be started + */ + return root_keyiter(mount_auto_node, root_node); +} + + +/* + * Construct top-level node + */ +void +make_root_node(void) +{ + mntfs *root_mnt; + char *rootmap = ROOT_MAP; + root_node = exported_ap_alloc(); + + /* + * Allocate a new map + */ + init_map(root_node, ""); + + /* + * Allocate a new mounted filesystem + */ + root_mnt = find_mntfs(&amfs_root_ops, (am_opts *) 0, "", rootmap, "", "", ""); + + /* + * Replace the initial null reference + */ + free_mntfs(root_node->am_mnt); + root_node->am_mnt = root_mnt; + + /* + * Initialize the root + */ + if (root_mnt->mf_ops->fs_init) + (*root_mnt->mf_ops->fs_init) (root_mnt); + + /* + * Mount the root + */ + root_mnt->mf_error = root_mnt->mf_ops->mount_fs(root_node, root_mnt); +} + + +/* + * Cause all the nodes to be unmounted by timing + * them out. + */ +void +umount_exported(void) +{ + int i; + + for (i = last_used_map; i >= 0; --i) { + am_node *mp = exported_ap[i]; + mntfs *mf; + + if (!mp) + continue; + + mf = mp->am_mnt; + if (mf->mf_flags & MFF_UNMOUNTING) { + /* + * If this node is being unmounted then just ignore it. However, + * this could prevent amd from finishing if the unmount gets blocked + * since the am_node will never be free'd. am_unmounted needs + * telling about this possibility. - XXX + */ + continue; + } + + if (!(mf->mf_fsflags & FS_DIRECTORY)) + /* + * When shutting down this had better + * look like a directory, otherwise it + * can't be unmounted! + */ + mk_fattr(&mp->am_fattr, NFDIR); + + if ((--immediate_abort < 0 && + !(mp->am_flags & AMF_ROOT) && mp->am_parent) || + (mf->mf_flags & MFF_RESTART)) { + + /* + * Just throw this node away without bothering to unmount it. If + * the server is not known to be up then don't discard the mounted + * on directory or Amd might hang... + */ + if (mf->mf_server && + (mf->mf_server->fs_flags & (FSF_DOWN | FSF_VALID)) != FSF_VALID) + mf->mf_flags &= ~MFF_MKMNT; + if (gopt.flags & CFM_UNMOUNT_ON_EXIT || mp->am_flags & AMF_AUTOFS) { + plog(XLOG_INFO, "on-exit attempt to unmount %s", mf->mf_mount); + /* + * use unmount_mp, not unmount_node, so that unmounts be + * backgrounded as needed. + */ + unmount_mp((opaque_t) mp); + } else { + am_unmounted(mp); + } + exported_ap[i] = 0; + } else { + /* + * Any other node gets forcibly timed out. + */ + mp->am_flags &= ~AMF_NOTIMEOUT; + mp->am_mnt->mf_flags &= ~MFF_RSTKEEP; + mp->am_ttl = 0; + mp->am_timeo = 1; + mp->am_timeo_w = 0; + } + } +} + + +/* + * Try to mount a file system. Can be called directly or in a sub-process by run_task. + * + * Warning: this function might be running in a child process context. + * Don't expect any changes made here to survive in the parent amd process. + */ +int +mount_node(opaque_t arg) +{ + am_node *mp = (am_node *) arg; + mntfs *mf = mp->am_mnt; + int error = 0; + +#ifdef HAVE_FS_AUTOFS + if (mp->am_flags & AMF_AUTOFS) + error = autofs_mount_fs(mp, mf); + else +#endif /* HAVE_FS_AUTOFS */ + if (!(mf->mf_flags & MFF_MOUNTED)) + error = mf->mf_ops->mount_fs(mp, mf); + + if (error > 0) + dlog("mount_node: call to mf_ops->mount_fs(%s) failed: %s", + mp->am_path, strerror(error)); + return error; +} + + +static int +unmount_node(opaque_t arg) +{ + am_node *mp = (am_node *) arg; + mntfs *mf = mp->am_mnt; + int error = 0; + + if (mf->mf_flags & MFF_ERROR) { + /* + * Just unlink + */ + dlog("No-op unmount of error node %s", mf->mf_info); + } else { + dlog("Unmounting <%s> <%s> (%s) flags %x", + mp->am_path, mf->mf_mount, mf->mf_info, mf->mf_flags); +#ifdef HAVE_FS_AUTOFS + if (mp->am_flags & AMF_AUTOFS) + error = autofs_umount_fs(mp, mf); + else +#endif /* HAVE_FS_AUTOFS */ + if (mf->mf_refc == 1) + error = mf->mf_ops->umount_fs(mp, mf); + } + + /* do this again, it might have changed */ + mf = mp->am_mnt; + if (error) { + errno = error; /* XXX */ + dlog("%s: unmount: %m", mf->mf_mount); + } + + return error; +} + + +static void +free_map_if_success(int rc, int term, opaque_t arg) +{ + am_node *mp = (am_node *) arg; + mntfs *mf = mp->am_mnt; + wchan_t wchan = get_mntfs_wchan(mf); + + /* + * Not unmounting any more + */ + mf->mf_flags &= ~MFF_UNMOUNTING; + + /* + * If a timeout was deferred because the underlying filesystem + * was busy then arrange for a timeout as soon as possible. + */ + if (mf->mf_flags & MFF_WANTTIMO) { + mf->mf_flags &= ~MFF_WANTTIMO; + reschedule_timeout_mp(); + } + if (term) { + plog(XLOG_ERROR, "unmount for %s got signal %d", mp->am_path, term); +#if defined(DEBUG) && defined(SIGTRAP) + /* + * dbx likes to put a trap on exit(). + * Pretend it succeeded for now... + */ + if (term == SIGTRAP) { + am_unmounted(mp); + } +#endif /* DEBUG */ +#ifdef HAVE_FS_AUTOFS + if (mp->am_flags & AMF_AUTOFS) + autofs_umount_failed(mp); +#endif /* HAVE_FS_AUTOFS */ + amd_stats.d_uerr++; + } else if (rc) { + if (mf->mf_ops == &amfs_program_ops || rc == EBUSY) + plog(XLOG_STATS, "\"%s\" on %s still active", mp->am_path, mf->mf_mount); + else + plog(XLOG_ERROR, "%s: unmount: %s", mp->am_path, strerror(rc)); +#ifdef HAVE_FS_AUTOFS + if (mf->mf_flags & MFF_IS_AUTOFS) + autofs_get_mp(mp); + if (mp->am_flags & AMF_AUTOFS) + autofs_umount_failed(mp); +#endif /* HAVE_FS_AUTOFS */ + amd_stats.d_uerr++; + } else { + am_unmounted(mp); + } + + /* + * Wakeup anything waiting for this unmount + */ + wakeup(wchan); +} + + +int +unmount_mp(am_node *mp) +{ + int was_backgrounded = 0; + mntfs *mf = mp->am_mnt; + +#ifdef notdef + plog(XLOG_INFO, "\"%s\" on %s timed out (flags 0x%x)", + mp->am_path, mp->am_mnt->mf_mount, (int) mf->mf_flags); +#endif /* notdef */ + +#ifndef MNT2_NFS_OPT_SYMTTL + /* + * This code is needed to defeat Solaris 2.4's (and newer) symlink + * values cache. It forces the last-modified time of the symlink to be + * current. It is not needed if the O/S has an nfs flag to turn off the + * symlink-cache at mount time (such as Irix 5.x and 6.x). -Erez. + * + * Additionally, Linux currently ignores the nt_useconds field, + * so we must update the nt_seconds field every time if clocktime(NULL) + * didn't return a new number of seconds. + */ + if (mp->am_parent) { + time_t last = mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime.nt_seconds; + clocktime(&mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime); + /* defensive programming... can't we assert the above condition? */ + if (last == (time_t) mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime.nt_seconds) + mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime.nt_seconds++; + } +#endif /* not MNT2_NFS_OPT_SYMTTL */ + + if (mf->mf_refc == 1 && !FSRV_ISUP(mf->mf_server)) { + /* + * Don't try to unmount from a server that is known to be down + */ + if (!(mf->mf_flags & MFF_LOGDOWN)) { + /* Only log this once, otherwise gets a bit boring */ + plog(XLOG_STATS, "file server %s is down - timeout of \"%s\" ignored", mf->mf_server->fs_host, mp->am_path); + mf->mf_flags |= MFF_LOGDOWN; + } + return 0; + } + + dlog("\"%s\" on %s timed out", mp->am_path, mp->am_mnt->mf_mount); + mf->mf_flags |= MFF_UNMOUNTING; + +#ifdef HAVE_FS_AUTOFS + if (mf->mf_flags & MFF_IS_AUTOFS) + autofs_release_mp(mp); +#endif /* HAVE_FS_AUTOFS */ + + if ((mf->mf_fsflags & FS_UBACKGROUND) && + (mf->mf_flags & MFF_MOUNTED)) { + dlog("Trying unmount in background"); + run_task(unmount_node, (opaque_t) mp, + free_map_if_success, (opaque_t) mp); + was_backgrounded = 1; + } else { + dlog("Trying unmount in foreground"); + free_map_if_success(unmount_node((opaque_t) mp), 0, (opaque_t) mp); + dlog("unmount attempt done"); + } + + return was_backgrounded; +} + + +void +timeout_mp(opaque_t v) /* argument not used?! */ +{ + int i; + time_t t = NEVER; + time_t now = clocktime(NULL); + int backoff = NumChildren / 4; + + dlog("Timing out automount points..."); + + for (i = last_used_map; i >= 0; --i) { + am_node *mp = exported_ap[i]; + mntfs *mf; + + /* + * Just continue if nothing mounted + */ + if (!mp) + continue; + + /* + * Pick up mounted filesystem + */ + mf = mp->am_mnt; + if (!mf) + continue; + +#ifdef HAVE_FS_AUTOFS + if (mf->mf_flags & MFF_IS_AUTOFS && mp->am_autofs_ttl != NEVER) { + if (now >= mp->am_autofs_ttl) + autofs_timeout_mp(mp); + t = smallest_t(t, mp->am_autofs_ttl); + } +#endif /* HAVE_FS_AUTOFS */ + + if (mp->am_flags & AMF_NOTIMEOUT) + continue; + + /* + * Don't delete last reference to a restarted filesystem. + */ + if ((mf->mf_flags & MFF_RSTKEEP) && mf->mf_refc == 1) + continue; + + /* + * If there is action on this filesystem then ignore it + */ + if (!(mf->mf_flags & IGNORE_FLAGS)) { + int expired = 0; + mf->mf_flags &= ~MFF_WANTTIMO; + if (now >= mp->am_ttl) { + if (!backoff) { + expired = 1; + + /* + * Move the ttl forward to avoid thrashing effects + * on the next call to timeout! + */ + /* sun's -tw option */ + if (mp->am_timeo_w < 4 * gopt.am_timeo_w) + mp->am_timeo_w += gopt.am_timeo_w; + mp->am_ttl = now + mp->am_timeo_w; + + } else { + /* + * Just backoff this unmount for + * a couple of seconds to avoid + * many multiple unmounts being + * started in parallel. + */ + mp->am_ttl = now + backoff + 1; + } + } + + /* + * If the next ttl is smallest, use that + */ + t = smallest_t(t, mp->am_ttl); + + if (!mp->am_child && mf->mf_error >= 0 && expired) { + /* + * If the unmount was backgrounded then + * bump the backoff counter. + */ + if (unmount_mp(mp)) { + backoff = 2; + } + } + } else if (mf->mf_flags & MFF_UNMOUNTING) { + mf->mf_flags |= MFF_WANTTIMO; + } + } + + if (t == NEVER) { + dlog("No further timeouts"); + t = now + ONE_HOUR; + } + + /* + * Sanity check to avoid runaways. + * Absolutely should never get this but + * if you do without this trap amd will thrash. + */ + if (t <= now) { + t = now + 6; /* XXX */ + plog(XLOG_ERROR, "Got a zero interval in timeout_mp()!"); + } + + /* + * XXX - when shutting down, make things happen faster + */ + if ((int) amd_state >= (int) Finishing) + t = now + 1; + dlog("Next mount timeout in %lds", (long) (t - now)); + + timeout_mp_id = timeout(t - now, timeout_mp, 0); +} + + +/* + * Cause timeout_mp to be called soonest + */ +void +reschedule_timeout_mp(void) +{ + if (timeout_mp_id) + untimeout(timeout_mp_id); + timeout_mp_id = timeout(0, timeout_mp, 0); +} diff --git a/amd/mapc.c b/amd/mapc.c new file mode 100644 index 000000000000..d7efdbcfd217 --- /dev/null +++ b/amd/mapc.c @@ -0,0 +1,1192 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * 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. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/mapc.c + * + */ + +/* + * Mount map cache + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* + * Make a duplicate reference to an existing map + */ +#define mapc_dup(m) ((m)->refc++, (m)) + +/* + * Map cache types + * default, none, incremental, all, regexp + * MAPC_RE implies MAPC_ALL and must be numerically + * greater. + */ +#define MAPC_DFLT 0x000 +#define MAPC_NONE 0x001 +#define MAPC_INC 0x002 +#define MAPC_ROOT 0x004 +#define MAPC_ALL 0x010 +#define MAPC_CACHE_MASK 0x0ff +#define MAPC_SYNC 0x100 + +#ifdef HAVE_REGEXEC +# define MAPC_RE 0x020 +# define MAPC_ISRE(m) ((m)->alloc == MAPC_RE) +#else /* not HAVE_REGEXEC */ +# define MAPC_ISRE(m) FALSE +#endif /* not HAVE_REGEXEC */ + +/* + * Lookup recursion + */ +#define MREC_FULL 2 +#define MREC_PART 1 +#define MREC_NONE 0 + +static struct opt_tab mapc_opt[] = +{ + {"all", MAPC_ALL}, + {"default", MAPC_DFLT}, + {"inc", MAPC_INC}, + {"mapdefault", MAPC_DFLT}, + {"none", MAPC_NONE}, +#ifdef HAVE_REGEXEC + {"re", MAPC_RE}, + {"regexp", MAPC_RE}, +#endif /* HAVE_REGEXEC */ + {"sync", MAPC_SYNC}, + {0, 0} +}; + +/* + * Wildcard key + */ +static char wildcard[] = "*"; + +/* + * Map type + */ +typedef struct map_type map_type; +struct map_type { + char *name; /* Name of this map type */ + init_fn *init; /* Initialization */ + reload_fn *reload; /* Reload or fill */ + isup_fn *isup; /* Is service up or not? (1=up, 0=down) */ + search_fn *search; /* Search for new entry */ + mtime_fn *mtime; /* Find modify time */ + int def_alloc; /* Default allocation mode */ +}; + +/* + * Map for root node + */ +static mnt_map *root_map; + +/* + * List of known maps + */ +qelem map_list_head = {&map_list_head, &map_list_head}; + +/* + * Configuration + */ + +/* forward definitions */ +static const char *get_full_path(const char *map, const char *path, const char *type); +static int mapc_meta_search(mnt_map *, char *, char **, int); +static void mapc_sync(mnt_map *); +static void mapc_clear(mnt_map *); + +/* ROOT MAP */ +static int root_init(mnt_map *, char *, time_t *); + +/* ERROR MAP */ +static int error_init(mnt_map *, char *, time_t *); +static int error_reload(mnt_map *, char *, add_fn *); +static int error_search(mnt_map *, char *, char *, char **, time_t *); +static int error_mtime(mnt_map *, char *, time_t *); + +/* PASSWD MAPS */ +#ifdef HAVE_MAP_PASSWD +extern int passwd_init(mnt_map *, char *, time_t *); +extern int passwd_search(mnt_map *, char *, char *, char **, time_t *); +#endif /* HAVE_MAP_PASSWD */ + +/* HESIOD MAPS */ +#ifdef HAVE_MAP_HESIOD +extern int amu_hesiod_init(mnt_map *, char *map, time_t *tp); +extern int hesiod_isup(mnt_map *, char *); +extern int hesiod_search(mnt_map *, char *, char *, char **, time_t *); +#endif /* HAVE_MAP_HESIOD */ + +/* LDAP MAPS */ +#ifdef HAVE_MAP_LDAP +extern int amu_ldap_init(mnt_map *, char *map, time_t *tp); +extern int amu_ldap_search(mnt_map *, char *, char *, char **, time_t *); +extern int amu_ldap_mtime(mnt_map *, char *, time_t *); +#endif /* HAVE_MAP_LDAP */ + +/* UNION MAPS */ +#ifdef HAVE_MAP_UNION +extern int union_init(mnt_map *, char *, time_t *); +extern int union_search(mnt_map *, char *, char *, char **, time_t *); +extern int union_reload(mnt_map *, char *, add_fn *); +#endif /* HAVE_MAP_UNION */ + +/* Network Information Service PLUS (NIS+) */ +#ifdef HAVE_MAP_NISPLUS +extern int nisplus_init(mnt_map *, char *, time_t *); +extern int nisplus_reload(mnt_map *, char *, add_fn *); +extern int nisplus_search(mnt_map *, char *, char *, char **, time_t *); +extern int nisplus_mtime(mnt_map *, char *, time_t *); +#endif /* HAVE_MAP_NISPLUS */ + +/* Network Information Service (YP, Yellow Pages) */ +#ifdef HAVE_MAP_NIS +extern int nis_init(mnt_map *, char *, time_t *); +extern int nis_reload(mnt_map *, char *, add_fn *); +extern int nis_isup(mnt_map *, char *); +extern int nis_search(mnt_map *, char *, char *, char **, time_t *); +extern int nis_mtime(mnt_map *, char *, time_t *); +#endif /* HAVE_MAP_NIS */ + +/* NDBM MAPS */ +#ifdef HAVE_MAP_NDBM +extern int ndbm_init(mnt_map *, char *, time_t *); +extern int ndbm_search(mnt_map *, char *, char *, char **, time_t *); +extern int ndbm_mtime(mnt_map *, char *, time_t *); +#endif /* HAVE_MAP_NDBM */ + +/* EXECUTABLE MAPS */ +#ifdef HAVE_MAP_EXEC +extern int exec_init(mnt_map *, char *, time_t *); +extern int exec_search(mnt_map *, char *, char *, char **, time_t *); +#endif /* HAVE_MAP_EXEC */ + +/* FILE MAPS */ +#ifdef HAVE_MAP_FILE +extern int file_init_or_mtime(mnt_map *, char *, time_t *); +extern int file_reload(mnt_map *, char *, add_fn *); +extern int file_search(mnt_map *, char *, char *, char **, time_t *); +#endif /* HAVE_MAP_FILE */ + + +/* note that the choice of MAPC_{INC,ALL} will affect browsable_dirs */ +static map_type maptypes[] = +{ + { + "root", + root_init, + error_reload, + NULL, /* isup function */ + error_search, + error_mtime, + MAPC_ROOT + }, +#ifdef HAVE_MAP_PASSWD + { + "passwd", + passwd_init, + error_reload, + NULL, /* isup function */ + passwd_search, + error_mtime, + MAPC_INC + }, +#endif /* HAVE_MAP_PASSWD */ +#ifdef HAVE_MAP_HESIOD + { + "hesiod", + amu_hesiod_init, + error_reload, + hesiod_isup, /* is Hesiod up or not? */ + hesiod_search, + error_mtime, + MAPC_INC + }, +#endif /* HAVE_MAP_HESIOD */ +#ifdef HAVE_MAP_LDAP + { + "ldap", + amu_ldap_init, + error_reload, + NULL, /* isup function */ + amu_ldap_search, + amu_ldap_mtime, + MAPC_INC + }, +#endif /* HAVE_MAP_LDAP */ +#ifdef HAVE_MAP_UNION + { + "union", + union_init, + union_reload, + NULL, /* isup function */ + union_search, + error_mtime, + MAPC_ALL + }, +#endif /* HAVE_MAP_UNION */ +#ifdef HAVE_MAP_NISPLUS + { + "nisplus", + nisplus_init, + nisplus_reload, + NULL, /* isup function */ + nisplus_search, + nisplus_mtime, + MAPC_INC + }, +#endif /* HAVE_MAP_NISPLUS */ +#ifdef HAVE_MAP_NIS + { + "nis", + nis_init, + nis_reload, + nis_isup, /* is NIS up or not? */ + nis_search, + nis_mtime, + MAPC_ALL + }, +#endif /* HAVE_MAP_NIS */ +#ifdef HAVE_MAP_NDBM + { + "ndbm", + ndbm_init, + error_reload, + NULL, /* isup function */ + ndbm_search, + ndbm_mtime, + MAPC_INC + }, +#endif /* HAVE_MAP_NDBM */ +#ifdef HAVE_MAP_FILE + { + "file", + file_init_or_mtime, + file_reload, + NULL, /* isup function */ + file_search, + file_init_or_mtime, + MAPC_ALL + }, +#endif /* HAVE_MAP_FILE */ +#ifdef HAVE_MAP_EXEC + { + "exec", + exec_init, + error_reload, + NULL, /* isup function */ + exec_search, + error_mtime, + MAPC_INC + }, +#endif /* HAVE_MAP_EXEC */ + { + "error", + error_init, + error_reload, + NULL, /* isup function */ + error_search, + error_mtime, + MAPC_NONE + }, +}; + + +/* + * Hash function + */ +static u_int +kvhash_of(char *key) +{ + u_int i, j; + + for (i = 0; (j = *key++); i += j) ; + + return i % NKVHASH; +} + + +void +mapc_showtypes(char *buf, size_t l) +{ + map_type *mt=NULL, *lastmt; + int linesize = 0, i; + + i = sizeof(maptypes) / sizeof(maptypes[0]); + lastmt = maptypes + i; + buf[0] = '\0'; + for (mt = maptypes; mt < lastmt; mt++) { + xstrlcat(buf, mt->name, l); + if (mt == (lastmt-1)) + break; /* if last one, don't do xstrlcat's that follows */ + linesize += strlen(mt->name); + if (--i > 0) { + xstrlcat(buf, ", ", l); + linesize += 2; + } + if (linesize > 54) { + linesize = 0; + xstrlcat(buf, "\n\t\t ", l); + } + } +} + + +/* + * Check if a map of a certain type exists. + * Return 1 (true) if exists, 0 (false) if not. + */ +int +mapc_type_exists(const char *type) +{ + map_type *mt; + + if (!type) + return 0; + for (mt = maptypes; + mt < maptypes + sizeof(maptypes) / sizeof(maptypes[0]); + mt++) { + if (STREQ(type, mt->name)) + return 1; + } + return 0; /* not found anywhere */ +} + + +/* + * Add key and val to the map m. + * key and val are assumed to be safe copies + */ +void +mapc_add_kv(mnt_map *m, char *key, char *val) +{ + kv **h; + kv *n; + int hash = kvhash_of(key); +#ifdef HAVE_REGEXEC + regex_t re; +#endif /* HAVE_REGEXEC */ + + dlog("add_kv: %s -> %s", key, val); + +#ifdef HAVE_REGEXEC + if (MAPC_ISRE(m)) { + char pattern[MAXPATHLEN]; + int retval; + + /* + * Make sure the string is bound to the start and end + */ + xsnprintf(pattern, sizeof(pattern), "^%s$", key); + retval = regcomp(&re, pattern, REG_ICASE); + if (retval != 0) { + char errstr[256]; + + /* XXX: this code was recently ported, and must be tested -Erez */ + errstr[0] = '\0'; + regerror(retval, &re, errstr, 256); + plog(XLOG_USER, "error compiling RE \"%s\": %s", pattern, errstr); + return; + } + } +#endif /* HAVE_REGEXEC */ + + h = &m->kvhash[hash]; + n = ALLOC(struct kv); + n->key = key; +#ifdef HAVE_REGEXEC + memcpy(&n->re, &re, sizeof(regex_t)); +#endif /* HAVE_REGEXEC */ + n->val = val; + n->next = *h; + *h = n; +} + + +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); + } +} + + +/* + * Search a map for a key. + * Calls map specific search routine. + * While map is out of date, keep re-syncing. + */ +static int +search_map(mnt_map *m, char *key, char **valp) +{ + int rc; + + do { + rc = (*m->search) (m, m->map_name, key, valp, &m->modify); + if (rc < 0) { + plog(XLOG_MAP, "Re-synchronizing cache for map %s", m->map_name); + mapc_sync(m); + } + } while (rc < 0); + + return rc; +} + + +/* + * Do a wildcard lookup in the map and + * save the result. + */ +static void +mapc_find_wildcard(mnt_map *m) +{ + /* + * Attempt to find the wildcard entry + */ + int rc = search_map(m, wildcard, &m->wildcard); + + if (rc != 0) + m->wildcard = 0; +} + + +/* + * Do a map reload. + * Attempt to reload without losing current data by switching the hashes + * round. + * If reloading was needed and succeeded, return 1; else return 0. + */ +static int +mapc_reload_map(mnt_map *m) +{ + int error, ret = 0; + kv *maphash[NKVHASH], *tmphash[NKVHASH]; + time_t t; + + error = (*m->mtime) (m, m->map_name, &t); + if (error) { + t = m->modify; + } + + /* + * skip reloading maps that have not been modified, unless + * amq -f was used (do_mapc_reload is 0) + */ + if (m->reloads != 0 && do_mapc_reload != 0) { + if (t <= m->modify) { + plog(XLOG_INFO, "reload of map %s is not needed (in sync)", m->map_name); + dlog("map %s last load time is %d, last modify time is %d", + m->map_name, (int) m->modify, (int) t); + return ret; + } + } + + /* copy the old hash and zero the map */ + memcpy((voidp) maphash, (voidp) m->kvhash, sizeof(m->kvhash)); + memset((voidp) m->kvhash, 0, sizeof(m->kvhash)); + + dlog("calling map reload on %s", m->map_name); + error = (*m->reload) (m, m->map_name, mapc_add_kv); + if (error) { + if (m->reloads == 0) + plog(XLOG_FATAL, "first time load of map %s failed!", m->map_name); + else + plog(XLOG_ERROR, "reload of map %s failed - using old values", + m->map_name); + mapc_clear(m); + memcpy((voidp) m->kvhash, (voidp) maphash, sizeof(m->kvhash)); + } else { + if (m->reloads++ == 0) + plog(XLOG_INFO, "first time load of map %s succeeded", m->map_name); + else + plog(XLOG_INFO, "reload #%d of map %s succeeded", + m->reloads, m->map_name); + memcpy((voidp) tmphash, (voidp) m->kvhash, sizeof(m->kvhash)); + memcpy((voidp) m->kvhash, (voidp) maphash, sizeof(m->kvhash)); + mapc_clear(m); + memcpy((voidp) m->kvhash, (voidp) tmphash, sizeof(m->kvhash)); + m->modify = t; + ret = 1; + } + m->wildcard = 0; + + dlog("calling mapc_search for wildcard"); + error = mapc_search(m, wildcard, &m->wildcard); + if (error) + m->wildcard = 0; + return ret; +} + + +/* + * Create a new map + */ +static mnt_map * +mapc_create(char *map, char *opt, const char *type) +{ + mnt_map *m = ALLOC(struct mnt_map); + map_type *mt; + time_t modify = 0; + int alloc = 0; + + cmdoption(opt, mapc_opt, &alloc); + + /* + * If using a configuration file, and the map_type is defined, then look + * for it, in the maptypes array. If found, initialize the map using that + * map_type. If not found, return error. If no map_type was defined, + * default to cycling through all maptypes. + */ + if (use_conf_file && type) { + /* find what type of map this one is */ + for (mt = maptypes; + mt < maptypes + sizeof(maptypes) / sizeof(maptypes[0]); + mt++) { + if (STREQ(type, mt->name)) { + plog(XLOG_INFO, "initializing amd.conf map %s of type %s", map, type); + if ((*mt->init) (m, map, &modify) == 0) { + break; + } else { + plog(XLOG_ERROR, "failed to initialize map %s", map); + error_init(m, map, &modify); + break; + } + } + } /* end of "for (mt =" loop */ + + } else { /* cycle through all known maptypes */ + + /* + * not using amd conf file or using it by w/o specifying map type + */ + for (mt = maptypes; + mt < maptypes + sizeof(maptypes) / sizeof(maptypes[0]); + mt++) { + dlog("trying to initialize map %s of type %s ...", map, mt->name); + if ((*mt->init) (m, map, &modify) == 0) { + break; + } + } + } /* end of "if (use_conf_file && (colpos = strchr ..." statement */ + + /* assert: mt in maptypes */ + + m->flags = alloc & ~MAPC_CACHE_MASK; + alloc &= MAPC_CACHE_MASK; + + if (alloc == MAPC_DFLT) + alloc = mt->def_alloc; + + switch (alloc) { + default: + plog(XLOG_USER, "Ambiguous map cache type \"%s\"; using \"inc\"", opt); + alloc = MAPC_INC; + /* fall-through... */ + case MAPC_NONE: + case MAPC_INC: + case MAPC_ROOT: + break; + + case MAPC_ALL: + /* + * If there is no support for reload and it was requested + * then back off to incremental instead. + */ + if (mt->reload == error_reload) { + plog(XLOG_WARNING, "Map type \"%s\" does not support cache type \"all\"; using \"inc\"", mt->name); + alloc = MAPC_INC; + } + break; + +#ifdef HAVE_REGEXEC + case MAPC_RE: + if (mt->reload == error_reload) { + plog(XLOG_WARNING, "Map type \"%s\" does not support cache type \"re\"", mt->name); + mt = &maptypes[sizeof(maptypes) / sizeof(maptypes[0]) - 1]; + /* assert: mt->name == "error" */ + } + break; +#endif /* HAVE_REGEXEC */ + } + + dlog("Map for %s coming from maptype %s", map, mt->name); + + m->alloc = alloc; + m->reload = mt->reload; + m->isup = mt->isup; + m->modify = modify; + m->search = alloc >= MAPC_ALL ? error_search : mt->search; + m->mtime = mt->mtime; + memset((voidp) m->kvhash, 0, sizeof(m->kvhash)); + m->map_name = strdup(map); + m->refc = 1; + m->wildcard = 0; + m->reloads = 0; + /* Unfortunately with current code structure, this cannot be initialized here */ + m->cfm = NULL; + + /* + * synchronize cache with reality + */ + mapc_sync(m); + + return m; +} + + +/* + * Free the cached data in a map + */ +static void +mapc_clear(mnt_map *m) +{ + int i; + + /* + * For each of the hash slots, chain + * along free'ing the data. + */ + for (i = 0; i < NKVHASH; i++) { + kv *k = m->kvhash[i]; + while (k) { + kv *n = k->next; + XFREE(k->key); + if (k->val) + XFREE(k->val); + XFREE(k); + k = n; + } + } + + /* + * Zero the hash slots + */ + memset((voidp) m->kvhash, 0, sizeof(m->kvhash)); + + /* + * Free the wildcard if it exists + */ + if (m->wildcard) { + XFREE(m->wildcard); + m->wildcard = 0; + } +} + + +/* + * Find a map, or create one if it does not exist + */ +mnt_map * +mapc_find(char *map, char *opt, const char *maptype) +{ + mnt_map *m; + + /* + * Search the list of known maps to see if + * it has already been loaded. If it is found + * then return a duplicate reference to it. + * Otherwise make a new map as required and + * add it to the list of maps + */ + ITER(m, mnt_map, &map_list_head) + if (STREQ(m->map_name, map)) + return mapc_dup(m); + m = mapc_create(map, opt, maptype); + ins_que(&m->hdr, &map_list_head); + + return m; +} + + +/* + * Free a map. + */ +void +mapc_free(opaque_t arg) +{ + mnt_map *m = (mnt_map *) arg; + + /* + * Decrement the reference count. + * If the reference count hits zero + * then throw the map away. + */ + if (m && --m->refc == 0) { + mapc_clear(m); + XFREE(m->map_name); + rem_que(&m->hdr); + XFREE(m); + } +} + + +/* + * Search the map for the key. Put a safe (malloc'ed) copy in *pval or + * return an error code + */ +static 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, m->map_name, &t); + if (error || t > m->modify) { + 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 HAVE_REGEXEC + 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) { + int retval; + + /* XXX: this code was recently ported, and must be tested -Erez */ + retval = regexec(&k->re, key, 0, 0, 0); + if (retval == 0) { /* succeeded */ + break; + } else { /* failed to match, log error */ + char errstr[256]; + + errstr[0] = '\0'; + regerror(retval, &k->re, errstr, 256); + plog(XLOG_USER, "error matching RE \"%s\" against \"%s\": %s", + key, k->key, errstr); + } + k = k->next; + } + if (k) + break; + } + } +#endif /* HAVE_REGEXEC */ + + /* + * 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 / *" + */ + xstrlcpy(wildname, key, sizeof(wildname)); + while (error && (subp = strrchr(wildname, '/'))) { + /* + * sizeof space left in subp is sizeof wildname minus what's left + * after the strchr above returned a pointer inside wildname into + * subp. + */ + xstrlcpy(subp, "/*", sizeof(wildname) - (subp - wildname)); + dlog("mapc recurses on %s", wildname); + 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; +} + + +int +mapc_search(mnt_map *m, char *key, char **pval) +{ + return mapc_meta_search(m, key, pval, MREC_FULL); +} + + +/* + * Get map cache in sync with physical representation + */ +static void +mapc_sync(mnt_map *m) +{ + int need_mtime_update = 0; + + if (m->alloc == MAPC_ROOT) + return; /* nothing to do */ + + /* do not clear map if map service is down */ + if (m->isup) { + if (!((*m->isup)(m, m->map_name))) { + plog(XLOG_ERROR, "mapc_sync: map %s is down: not clearing map", m->map_name); + return; + } + } + + if (m->alloc >= MAPC_ALL) { + /* mapc_reload_map() always works */ + need_mtime_update = mapc_reload_map(m); + } else { + mapc_clear(m); + /* + * Attempt to find the wildcard entry + */ + mapc_find_wildcard(m); + need_mtime_update = 1; /* because mapc_clear always works */ + } + + /* + * To be safe, update the mtime of the mnt_map's own node, so that the + * kernel will flush all of its cached entries. + */ + if (need_mtime_update && m->cfm) { + am_node *mp = find_ap(m->cfm->cfm_dir); + if (mp) { + clocktime(&mp->am_fattr.na_mtime); + } else { + plog(XLOG_ERROR, "cannot find map %s to update its mtime", + m->cfm->cfm_dir); + } + } +} + + +/* + * Reload all the maps + * Called when Amd gets hit by a SIGHUP. + */ +void +mapc_reload(void) +{ + mnt_map *m; + + /* + * For all the maps, + * Throw away the existing information. + * Do a reload + * Find the wildcard + */ + ITER(m, mnt_map, &map_list_head) + mapc_sync(m); +} + + +/* + * Root map. + * The root map is used to bootstrap amd. + * All the require top-level mounts are added + * into the root map and then the map is iterated + * and a lookup is done on all the mount points. + * This causes the top level mounts to be automounted. + */ +static int +root_init(mnt_map *m, char *map, time_t *tp) +{ + *tp = clocktime(NULL); + return STREQ(map, ROOT_MAP) ? 0 : ENOENT; +} + + +/* + * Add a new entry to the root map + * + * dir - directory (key) + * opts - mount options + * map - map name + * cfm - optional amd configuration file map section structure + */ +void +root_newmap(const char *dir, const char *opts, const char *map, const cf_map_t *cfm) +{ + char str[MAXPATHLEN]; + + /* + * First make sure we have a root map to talk about... + */ + if (!root_map) + root_map = mapc_find(ROOT_MAP, "mapdefault", NULL); + + /* + * Then add the entry... + */ + + /* + * Here I plug in the code to process other amd.conf options like + * map_type, search_path, and flags (browsable_dirs, mount_type). + */ + + if (cfm) { + if (map) { + xsnprintf(str, sizeof(str), + "cache:=mapdefault;type:=toplvl;mount_type:=%s;fs:=\"%s\"", + cfm->cfm_flags & CFM_MOUNT_TYPE_AUTOFS ? "autofs" : "nfs", + get_full_path(map, cfm->cfm_search_path, cfm->cfm_type)); + if (opts && opts[0] != '\0') { + xstrlcat(str, ";", sizeof(str)); + xstrlcat(str, opts, sizeof(str)); + } + if (cfm->cfm_flags & CFM_BROWSABLE_DIRS_FULL) + xstrlcat(str, ";opts:=rw,fullybrowsable", sizeof(str)); + if (cfm->cfm_flags & CFM_BROWSABLE_DIRS) + xstrlcat(str, ";opts:=rw,browsable", sizeof(str)); + if (cfm->cfm_type) { + xstrlcat(str, ";maptype:=", sizeof(str)); + xstrlcat(str, cfm->cfm_type, sizeof(str)); + } + } else { + xstrlcpy(str, opts, sizeof(str)); + } + } else { + if (map) + xsnprintf(str, sizeof(str), + "cache:=mapdefault;type:=toplvl;fs:=\"%s\";%s", + map, opts ? opts : ""); + else + xstrlcpy(str, opts, sizeof(str)); + } + mapc_repl_kv(root_map, strdup((char *)dir), strdup(str)); +} + + +int +mapc_keyiter(mnt_map *m, key_fun *fn, opaque_t arg) +{ + int i; + int c = 0; + + for (i = 0; i < NKVHASH; i++) { + kv *k = m->kvhash[i]; + while (k) { + (*fn) (k->key, arg); + k = k->next; + c++; + } + } + + return c; +} + + +/* + * Iterate on the root map and call (*fn)() on the key of all the nodes. + * Returns the number of entries in the root map. + */ +int +root_keyiter(key_fun *fn, opaque_t arg) +{ + if (root_map) { + int c = mapc_keyiter(root_map, fn, arg); + return c; + } + + return 0; +} + + +/* + * Error map + */ +static int +error_init(mnt_map *m, char *map, time_t *tp) +{ + plog(XLOG_USER, "No source data for map %s", map); + *tp = 0; + + return 0; +} + + +static int +error_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp) +{ + return ENOENT; +} + + +static int +error_reload(mnt_map *m, char *map, add_fn *fn) +{ + return ENOENT; +} + + +static int +error_mtime(mnt_map *m, char *map, time_t *tp) +{ + *tp = 0; + + return 0; +} + + +/* + * Return absolute path of map, searched in a type-specific path. + * Note: uses a static buffer for returned data. + */ +static const char * +get_full_path(const char *map, const char *path, const char *type) +{ + char component[MAXPATHLEN], *str; + static char full_path[MAXPATHLEN]; + int len; + + /* for now, only file-type search paths are implemented */ + if (type && !STREQ(type, "file")) + return map; + + /* if null map, return it */ + if (!map) + return map; + + /* if map includes a '/', return it (absolute or relative path) */ + if (strchr(map, '/')) + return map; + + /* if path is empty, return map */ + if (!path) + return map; + + /* now break path into components, and search in each */ + xstrlcpy(component, path, sizeof(component)); + + str = strtok(component, ":"); + do { + xstrlcpy(full_path, str, sizeof(full_path)); + len = strlen(full_path); + if (full_path[len - 1] != '/') /* add trailing "/" if needed */ + xstrlcat(full_path, "/", sizeof(full_path)); + xstrlcat(full_path, map, sizeof(full_path)); + if (access(full_path, R_OK) == 0) + return full_path; + str = strtok(NULL, ":"); + } while (str); + + return map; /* if found nothing, return map */ +} diff --git a/amd/mntfs.c b/amd/mntfs.c new file mode 100644 index 000000000000..6021838425c0 --- /dev/null +++ b/amd/mntfs.c @@ -0,0 +1,366 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/mntfs.c + * + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +qelem mfhead = {&mfhead, &mfhead}; + +int mntfs_allocated; + + +mntfs * +dup_mntfs(mntfs *mf) +{ + if (mf->mf_refc == 0) { + if (mf->mf_cid) + untimeout(mf->mf_cid); + mf->mf_cid = 0; + } + mf->mf_refc++; + + return mf; +} + + +static void +init_mntfs(mntfs *mf, am_ops *ops, am_opts *mo, char *mp, char *info, char *auto_opts, char *mopts, char *remopts) +{ + mf->mf_ops = ops; + mf->mf_fsflags = ops->nfs_fs_flags; + mf->mf_fo = mo; + mf->mf_mount = strdup(mp); + mf->mf_info = strdup(info); + mf->mf_auto = strdup(auto_opts); + mf->mf_mopts = strdup(mopts); + mf->mf_remopts = strdup(remopts); + mf->mf_loopdev = NULL; + mf->mf_refc = 1; + mf->mf_flags = 0; + mf->mf_error = -1; + mf->mf_cid = 0; + mf->mf_private = 0; + mf->mf_prfree = 0; + + if (ops->ffserver) + mf->mf_server = (*ops->ffserver) (mf); + else + mf->mf_server = 0; +} + + +static mntfs * +alloc_mntfs(am_ops *ops, am_opts *mo, char *mp, char *info, char *auto_opts, char *mopts, char *remopts) +{ + mntfs *mf = ALLOC(struct mntfs); + + init_mntfs(mf, ops, mo, mp, info, auto_opts, mopts, remopts); + ins_que(&mf->mf_q, &mfhead); + mntfs_allocated++; + + return mf; +} + + +/* find a matching mntfs in our list */ +mntfs * +locate_mntfs(am_ops *ops, am_opts *mo, char *mp, char *info, char *auto_opts, char *mopts, char *remopts) +{ + mntfs *mf; + + dlog("Locating mntfs reference to (%s,%s)", mp, info); + + ITER(mf, mntfs, &mfhead) { + /* + * For backwards compatibility purposes, we treat already-mounted + * filesystems differently and only require a match of their mount point, + * not of their server info. After all, there is little we can do if + * the user asks us to mount two different things onto the same mount: one + * will always cover the other one. + */ + if (STREQ(mf->mf_mount, mp) && + ((mf->mf_flags & MFF_MOUNTED && !(mf->mf_fsflags & FS_DIRECT)) + || (STREQ(mf->mf_info, info) && mf->mf_ops == ops))) { + /* + * Handle cases where error ops are involved + */ + if (ops == &amfs_error_ops) { + /* + * If the existing ops are not amfs_error_ops + * then continue... + */ + if (mf->mf_ops != &amfs_error_ops) + continue; + return dup_mntfs(mf); + } + + dlog("mf->mf_flags = %#x", mf->mf_flags); + mf->mf_fo = mo; + if ((mf->mf_flags & MFF_RESTART) && amd_state < Finishing) { + /* + * Restart a previously mounted filesystem. + */ + dlog("Restarting filesystem %s", mf->mf_mount); + + /* + * If we are restarting an amd internal filesystem, + * we need to initialize it a bit. + * + * We know it's internal because it is marked as toplvl. + */ + if (mf->mf_ops == &amfs_toplvl_ops) { + mf->mf_ops = ops; + mf->mf_info = strealloc(mf->mf_info, info); + ops->mounted(mf); /* XXX: not right, but will do for now */ + } + + return mf; + } + + if (!(mf->mf_flags & (MFF_MOUNTED | MFF_MOUNTING | MFF_UNMOUNTING))) { + fserver *fs; + mf->mf_flags &= ~MFF_ERROR; + mf->mf_error = -1; + mf->mf_auto = strealloc(mf->mf_auto, auto_opts); + mf->mf_mopts = strealloc(mf->mf_mopts, mopts); + mf->mf_remopts = strealloc(mf->mf_remopts, remopts); + mf->mf_info = strealloc(mf->mf_info, info); + + if (mf->mf_private && mf->mf_prfree) { + mf->mf_prfree(mf->mf_private); + mf->mf_private = 0; + } + + fs = ops->ffserver ? (*ops->ffserver) (mf) : (fserver *) NULL; + if (mf->mf_server) + free_srvr(mf->mf_server); + mf->mf_server = fs; + } + return dup_mntfs(mf); + } /* end of "if (STREQ(mf-> ..." */ + } /* end of ITER */ + + return 0; +} + + +/* find a matching mntfs in our list, create a new one if none is found */ +mntfs * +find_mntfs(am_ops *ops, am_opts *mo, char *mp, char *info, char *auto_opts, char *mopts, char *remopts) +{ + mntfs *mf = locate_mntfs(ops, mo, mp, info, auto_opts, mopts, remopts); + if (mf) + return mf; + + return alloc_mntfs(ops, mo, mp, info, auto_opts, mopts, remopts); +} + + +mntfs * +new_mntfs(void) +{ + return alloc_mntfs(&amfs_error_ops, (am_opts *) 0, "//nil//", ".", "", "", ""); +} + + +static void +uninit_mntfs(mntfs *mf) +{ + if (mf->mf_auto) + XFREE(mf->mf_auto); + if (mf->mf_mopts) + XFREE(mf->mf_mopts); + if (mf->mf_remopts) + XFREE(mf->mf_remopts); + if (mf->mf_info) + XFREE(mf->mf_info); + if (mf->mf_private && mf->mf_prfree) + (*mf->mf_prfree) (mf->mf_private); + + if (mf->mf_mount) + XFREE(mf->mf_mount); + + /* + * Clean up the file server + */ + if (mf->mf_server) + free_srvr(mf->mf_server); + + /* + * Don't do a callback on this mount + */ + if (mf->mf_cid) { + untimeout(mf->mf_cid); + mf->mf_cid = 0; + } +} + + +static void +discard_mntfs(voidp v) +{ + mntfs *mf = v; + + rem_que(&mf->mf_q); + + /* + * Free memory + */ + uninit_mntfs(mf); + XFREE(mf); + + --mntfs_allocated; +} + + +void +flush_mntfs(void) +{ + mntfs *mf; + + mf = AM_FIRST(mntfs, &mfhead); + while (mf != HEAD(mntfs, &mfhead)) { + mntfs *mf2 = mf; + mf = NEXT(mntfs, mf); + if (mf2->mf_refc == 0 && mf2->mf_cid) + discard_mntfs(mf2); + } +} + + +void +free_mntfs(opaque_t arg) +{ + mntfs *mf = (mntfs *) arg; + + dlog("free_mntfs <%s> type %s mf_refc %d flags %x", + mf->mf_mount, mf->mf_ops->fs_type, mf->mf_refc, mf->mf_flags); + + /* + * We shouldn't ever be called to free something that has + * a non-positive refcount. Something is badly wrong if + * we have been! Ignore the request for now... + */ + if (mf->mf_refc <= 0) { + plog(XLOG_ERROR, "IGNORING free_mntfs for <%s>: refc %d, flags %x (bug?)", + mf->mf_mount, mf->mf_refc, mf->mf_flags); + return; + } + + /* don't discard last reference of a restarted/kept mntfs */ + if (mf->mf_refc == 1 && mf->mf_flags & MFF_RSTKEEP) { + plog(XLOG_ERROR, "IGNORING free_mntfs for <%s>: refc %d, flags %x (restarted)", + mf->mf_mount, mf->mf_refc, mf->mf_flags); + return; + } + + if (--mf->mf_refc == 0) { + if (mf->mf_flags & MFF_MOUNTED) { + int quoted; + mf->mf_flags &= ~MFF_MOUNTED; + + /* + * Record for posterity + */ + quoted = strchr(mf->mf_info, ' ') != 0; /* cheap */ + plog(XLOG_INFO, "%s%s%s %sed fstype %s from %s", + quoted ? "\"" : "", + mf->mf_info, + quoted ? "\"" : "", + mf->mf_error ? "discard" : "unmount", + mf->mf_ops->fs_type, mf->mf_mount); + } + + if (mf->mf_fsflags & FS_DISCARD) { + dlog("Immediately discarding mntfs for %s", mf->mf_mount); + discard_mntfs(mf); + + } else { + + if (mf->mf_flags & MFF_RESTART) { + dlog("Discarding remount hook for %s", mf->mf_mount); + } else { + dlog("Discarding last mntfs reference to %s fstype %s", + mf->mf_mount, mf->mf_ops->fs_type); + } + if (mf->mf_flags & (MFF_MOUNTED | MFF_MOUNTING | MFF_UNMOUNTING)) + dlog("mntfs reference for %s still active", mf->mf_mount); + mf->mf_cid = timeout(ALLOWED_MOUNT_TIME, discard_mntfs, (voidp) mf); + } + } +} + + +mntfs * +realloc_mntfs(mntfs *mf, am_ops *ops, am_opts *mo, char *mp, char *info, char *auto_opts, char *mopts, char *remopts) +{ + mntfs *mf2; + + if (mf->mf_refc == 1 && + mf->mf_flags & MFF_RESTART && + STREQ(mf->mf_mount, mp)) { + /* + * If we are inheriting then just return + * the same node... + */ + return mf; + } + + /* + * Re-use the existing mntfs if it is mounted. + * This traps a race in nfsx. + */ + if (mf->mf_ops != &amfs_error_ops && + (mf->mf_flags & MFF_MOUNTED) && + !FSRV_ISDOWN(mf->mf_server)) { + mf->mf_fo = mo; + return mf; + } + + mf2 = find_mntfs(ops, mo, mp, info, auto_opts, mopts, remopts); + free_mntfs(mf); + return mf2; +} diff --git a/amd/nfs_prot_svc.c b/amd/nfs_prot_svc.c new file mode 100644 index 000000000000..0dd6992cf9e7 --- /dev/null +++ b/amd/nfs_prot_svc.c @@ -0,0 +1,301 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * 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. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/nfs_prot_svc.c + * + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* external definitions */ +extern voidp nfsproc_null_2_svc(voidp, struct svc_req *); +extern nfsattrstat *nfsproc_getattr_2_svc(am_nfs_fh *, struct svc_req *); +extern nfsattrstat *nfsproc_setattr_2_svc(nfssattrargs *, struct svc_req *); +extern voidp nfsproc_root_2_svc(voidp, struct svc_req *); +extern nfsdiropres *nfsproc_lookup_2_svc(nfsdiropargs *, struct svc_req *); +extern nfsreadlinkres *nfsproc_readlink_2_svc(am_nfs_fh *, struct svc_req *); +extern nfsreadres *nfsproc_read_2_svc(nfsreadargs *, struct svc_req *); +extern voidp nfsproc_writecache_2_svc(voidp, struct svc_req *); +extern nfsattrstat *nfsproc_write_2_svc(nfswriteargs *, struct svc_req *); +extern nfsdiropres *nfsproc_create_2_svc(nfscreateargs *, struct svc_req *); +extern nfsstat *nfsproc_remove_2_svc(nfsdiropargs *, struct svc_req *); +extern nfsstat *nfsproc_rename_2_svc(nfsrenameargs *, struct svc_req *); +extern nfsstat *nfsproc_link_2_svc(nfslinkargs *, struct svc_req *); +extern nfsstat *nfsproc_symlink_2_svc(nfssymlinkargs *, struct svc_req *); +extern nfsdiropres *nfsproc_mkdir_2_svc(nfscreateargs *, struct svc_req *); +extern nfsstat *nfsproc_rmdir_2_svc(nfsdiropargs *, struct svc_req *); +extern nfsreaddirres *nfsproc_readdir_2_svc(nfsreaddirargs *, struct svc_req *); +extern nfsstatfsres *nfsproc_statfs_2_svc(am_nfs_fh *, struct svc_req *); + +/* global variables */ +SVCXPRT *current_transp; + +/* typedefs */ +typedef char *(*nfssvcproc_t)(voidp, struct svc_req *); + + +void +nfs_program_2(struct svc_req *rqstp, SVCXPRT *transp) +{ + union { + am_nfs_fh nfsproc_getattr_2_arg; + nfssattrargs nfsproc_setattr_2_arg; + nfsdiropargs nfsproc_lookup_2_arg; + am_nfs_fh nfsproc_readlink_2_arg; + nfsreadargs nfsproc_read_2_arg; + nfswriteargs nfsproc_write_2_arg; + nfscreateargs nfsproc_create_2_arg; + nfsdiropargs nfsproc_remove_2_arg; + nfsrenameargs nfsproc_rename_2_arg; + nfslinkargs nfsproc_link_2_arg; + nfssymlinkargs nfsproc_symlink_2_arg; + nfscreateargs nfsproc_mkdir_2_arg; + nfsdiropargs fsproc_rmdir_2_arg; + nfsreaddirargs nfsproc_readdir_2_arg; + am_nfs_fh nfsproc_statfs_2_arg; + } argument; + char *result; + xdrproc_t xdr_argument, xdr_result; + nfssvcproc_t local; + +#ifdef HAVE_TRANSPORT_TYPE_TLI + /* + * On TLI systems we don't use an INET network type, but a "ticlts" (see + * /etc/netconfig and conf/transp_tli.c:create_nfs_service). This means + * that packets could only come from the loopback interface, and we don't + * need to check them and filter possibly spoofed packets. Therefore we + * only need to check if the UID caller is correct. + */ +# ifdef HAVE___RPC_GET_LOCAL_UID + uid_t u; + /* extern definition for an internal libnsl function */ + extern int __rpc_get_local_uid(SVCXPRT *transp, uid_t *uid); + if (__rpc_get_local_uid(transp, &u) >= 0 && u != 0) { + plog(XLOG_WARNING, "ignoring request from UID %ld, must be 0", (long) u); + return; + } +# else /* not HAVE___RPC_GET_LOCAL_UID */ + dlog("cannot verify local uid for rpc request"); +# endif /* HAVE___RPC_GET_LOCAL_UID */ +#else /* not HAVE_TRANPORT_TYPE_TLI */ + struct sockaddr_in *sinp; + char dq[20], dq2[28]; + sinp = amu_svc_getcaller(rqstp->rq_xprt); +# ifdef MNT2_NFS_OPT_RESVPORT + /* Verify that the request comes from a reserved port */ + if (sinp && + ntohs(sinp->sin_port) >= IPPORT_RESERVED && + !(gopt.flags & CFM_NFS_INSECURE_PORT)) { + plog(XLOG_WARNING, "ignoring request from %s:%u, port not reserved", + inet_dquad(dq, sizeof(dq), sinp->sin_addr.s_addr), + ntohs(sinp->sin_port)); + return; + } +# endif /* MNT2_NFS_OPT_RESVPORT */ + /* if the address does not match, ignore the request */ + if (sinp && (sinp->sin_addr.s_addr != myipaddr.s_addr)) { + if (gopt.flags & CFM_NFS_ANY_INTERFACE) { + if (!is_interface_local(sinp->sin_addr.s_addr)) { + plog(XLOG_WARNING, "ignoring request from %s:%u, not a local interface", + inet_dquad(dq, sizeof(dq), sinp->sin_addr.s_addr), + ntohs(sinp->sin_port)); + } + } else { + plog(XLOG_WARNING, "ignoring request from %s:%u, expected %s", + inet_dquad(dq, sizeof(dq), sinp->sin_addr.s_addr), + ntohs(sinp->sin_port), + inet_dquad(dq2, sizeof(dq2), myipaddr.s_addr)); + return; + } + } +#endif /* not HAVE_TRANPORT_TYPE_TLI */ + + current_transp = NULL; + + switch (rqstp->rq_proc) { + + case NFSPROC_NULL: + xdr_argument = (xdrproc_t) xdr_void; + xdr_result = (xdrproc_t) xdr_void; + local = (nfssvcproc_t) nfsproc_null_2_svc; + break; + + case NFSPROC_GETATTR: + xdr_argument = (xdrproc_t) xdr_nfs_fh; + xdr_result = (xdrproc_t) xdr_attrstat; + local = (nfssvcproc_t) nfsproc_getattr_2_svc; + break; + + case NFSPROC_SETATTR: + xdr_argument = (xdrproc_t) xdr_sattrargs; + xdr_result = (xdrproc_t) xdr_attrstat; + local = (nfssvcproc_t) nfsproc_setattr_2_svc; + break; + + case NFSPROC_ROOT: + xdr_argument = (xdrproc_t) xdr_void; + xdr_result = (xdrproc_t) xdr_void; + local = (nfssvcproc_t) nfsproc_root_2_svc; + break; + + case NFSPROC_LOOKUP: + xdr_argument = (xdrproc_t) xdr_diropargs; + xdr_result = (xdrproc_t) xdr_diropres; + local = (nfssvcproc_t) nfsproc_lookup_2_svc; + /* + * Cheap way to pass transp down to amfs_auto_lookuppn so it can + * be stored in the am_node structure and later used for + * quick_reply(). + */ + current_transp = transp; + break; + + case NFSPROC_READLINK: + xdr_argument = (xdrproc_t) xdr_nfs_fh; + xdr_result = (xdrproc_t) xdr_readlinkres; + local = (nfssvcproc_t) nfsproc_readlink_2_svc; + break; + + case NFSPROC_READ: + xdr_argument = (xdrproc_t) xdr_readargs; + xdr_result = (xdrproc_t) xdr_readres; + local = (nfssvcproc_t) nfsproc_read_2_svc; + break; + + case NFSPROC_WRITECACHE: + xdr_argument = (xdrproc_t) xdr_void; + xdr_result = (xdrproc_t) xdr_void; + local = (nfssvcproc_t) nfsproc_writecache_2_svc; + break; + + case NFSPROC_WRITE: + xdr_argument = (xdrproc_t) xdr_writeargs; + xdr_result = (xdrproc_t) xdr_attrstat; + local = (nfssvcproc_t) nfsproc_write_2_svc; + break; + + case NFSPROC_CREATE: + xdr_argument = (xdrproc_t) xdr_createargs; + xdr_result = (xdrproc_t) xdr_diropres; + local = (nfssvcproc_t) nfsproc_create_2_svc; + break; + + case NFSPROC_REMOVE: + xdr_argument = (xdrproc_t) xdr_diropargs; + xdr_result = (xdrproc_t) xdr_nfsstat; + local = (nfssvcproc_t) nfsproc_remove_2_svc; + break; + + case NFSPROC_RENAME: + xdr_argument = (xdrproc_t) xdr_renameargs; + xdr_result = (xdrproc_t) xdr_nfsstat; + local = (nfssvcproc_t) nfsproc_rename_2_svc; + break; + + case NFSPROC_LINK: + xdr_argument = (xdrproc_t) xdr_linkargs; + xdr_result = (xdrproc_t) xdr_nfsstat; + local = (nfssvcproc_t) nfsproc_link_2_svc; + break; + + case NFSPROC_SYMLINK: + xdr_argument = (xdrproc_t) xdr_symlinkargs; + xdr_result = (xdrproc_t) xdr_nfsstat; + local = (nfssvcproc_t) nfsproc_symlink_2_svc; + break; + + case NFSPROC_MKDIR: + xdr_argument = (xdrproc_t) xdr_createargs; + xdr_result = (xdrproc_t) xdr_diropres; + local = (nfssvcproc_t) nfsproc_mkdir_2_svc; + break; + + case NFSPROC_RMDIR: + xdr_argument = (xdrproc_t) xdr_diropargs; + xdr_result = (xdrproc_t) xdr_nfsstat; + local = (nfssvcproc_t) nfsproc_rmdir_2_svc; + break; + + case NFSPROC_READDIR: + xdr_argument = (xdrproc_t) xdr_readdirargs; + xdr_result = (xdrproc_t) xdr_readdirres; + local = (nfssvcproc_t) nfsproc_readdir_2_svc; + break; + + case NFSPROC_STATFS: + xdr_argument = (xdrproc_t) xdr_nfs_fh; + xdr_result = (xdrproc_t) xdr_statfsres; + local = (nfssvcproc_t) nfsproc_statfs_2_svc; + break; + + default: + svcerr_noproc(transp); + return; + } + + memset((char *) &argument, 0, sizeof(argument)); + if (!svc_getargs(transp, + (XDRPROC_T_TYPE) xdr_argument, + (SVC_IN_ARG_TYPE) &argument)) { + plog(XLOG_ERROR, + "NFS xdr decode failed for %d %d %d", + (int) rqstp->rq_prog, (int) rqstp->rq_vers, (int) rqstp->rq_proc); + svcerr_decode(transp); + return; + } + result = (*local) (&argument, rqstp); + + current_transp = NULL; + + if (result != NULL && !svc_sendreply(transp, + (XDRPROC_T_TYPE) xdr_result, + result)) { + svcerr_systemerr(transp); + } + if (!svc_freeargs(transp, + (XDRPROC_T_TYPE) xdr_argument, + (SVC_IN_ARG_TYPE) & argument)) { + plog(XLOG_FATAL, "unable to free rpc arguments in nfs_program_2"); + going_down(1); + } +} diff --git a/amd/nfs_start.c b/amd/nfs_start.c new file mode 100644 index 000000000000..f6aba94e2b6d --- /dev/null +++ b/amd/nfs_start.c @@ -0,0 +1,453 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/nfs_start.c + * + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +#ifndef SELECT_MAXWAIT +# define SELECT_MAXWAIT 16 +#endif /* not SELECT_MAXWAIT */ + +SVCXPRT *nfsxprt = NULL; +u_short nfs_port = 0; + +#ifndef HAVE_SIGACTION +# define MASKED_SIGS (sigmask(SIGINT)|sigmask(SIGTERM)|sigmask(SIGCHLD)|sigmask(SIGHUP)) +#endif /* not HAVE_SIGACTION */ + +#ifdef DEBUG +/* + * Check that we are not burning resources + */ +static void +checkup(void) +{ + + static int max_fd = 0; + static char *max_mem = 0; + + int next_fd = dup(0); + caddr_t next_mem = sbrk(0); + close(next_fd); + + if (max_fd < next_fd) { + dlog("%d new fds allocated; total is %d", + next_fd - max_fd, next_fd); + max_fd = next_fd; + } + if (max_mem < next_mem) { +#ifdef HAVE_GETPAGESIZE + dlog("%#lx bytes of memory allocated; total is %#lx (%ld pages)", + (long) (next_mem - max_mem), (unsigned long) next_mem, + ((long) next_mem + getpagesize() - 1) / (long) getpagesize()); +#else /* not HAVE_GETPAGESIZE */ + dlog("%#lx bytes of memory allocated; total is %#lx", + (long) (next_mem - max_mem), (unsigned long) next_mem); +#endif /* not HAVE_GETPAGESIZE */ + max_mem = next_mem; + + } +} +#else /* not DEBUG */ +#define checkup() +#endif /* not DEBUG */ + + +static int +#ifdef HAVE_SIGACTION +do_select(sigset_t smask, int fds, fd_set *fdp, struct timeval *tvp) +#else /* not HAVE_SIGACTION */ +do_select(int smask, int fds, fd_set *fdp, struct timeval *tvp) +#endif /* not HAVE_SIGACTION */ +{ + + int sig; + int nsel; + + if ((sig = setjmp(select_intr))) { + select_intr_valid = 0; + /* Got a signal */ + switch (sig) { + case SIGINT: + case SIGTERM: + amd_state = Finishing; + reschedule_timeout_mp(); + break; + } + nsel = -1; + errno = EINTR; + } else { + select_intr_valid = 1; + /* + * Allow interrupts. If a signal + * occurs, then it will cause a longjmp + * up above. + */ +#ifdef HAVE_SIGACTION + sigprocmask(SIG_SETMASK, &smask, NULL); +#else /* not HAVE_SIGACTION */ + (void) sigsetmask(smask); +#endif /* not HAVE_SIGACTION */ + + /* + * Wait for input + */ + nsel = select(fds, fdp, (fd_set *) 0, (fd_set *) 0, + tvp->tv_sec ? tvp : (struct timeval *) 0); + } + +#ifdef HAVE_SIGACTION + sigprocmask(SIG_BLOCK, &masked_sigs, NULL); +#else /* not HAVE_SIGACTION */ + (void) sigblock(MASKED_SIGS); +#endif /* not HAVE_SIGACTION */ + + /* + * Perhaps reload the cache? + */ + if (do_mapc_reload < clocktime(NULL)) { + mapc_reload(); + do_mapc_reload = clocktime(NULL) + gopt.map_reload_interval; + } + return nsel; +} + + +/* + * Determine whether anything is left in + * the RPC input queue. + */ +static int +rpc_pending_now(void) +{ + struct timeval tvv; + int nsel; + fd_set readfds; + + FD_ZERO(&readfds); + FD_SET(fwd_sock, &readfds); + + tvv.tv_sec = tvv.tv_usec = 0; + nsel = select(FD_SETSIZE, &readfds, (fd_set *) 0, (fd_set *) 0, &tvv); + if (nsel < 1) + return (0); + if (FD_ISSET(fwd_sock, &readfds)) + return (1); + + return (0); +} + + +static serv_state +run_rpc(void) +{ +#ifdef HAVE_SIGACTION + sigset_t smask; + sigprocmask(SIG_BLOCK, &masked_sigs, &smask); +#else /* not HAVE_SIGACTION */ + int smask = sigblock(MASKED_SIGS); +#endif /* not HAVE_SIGACTION */ + + next_softclock = clocktime(NULL); + + amd_state = Run; + + /* + * Keep on trucking while we are in Run mode. This state + * is switched to Quit after all the file systems have + * been unmounted. + */ + while ((int) amd_state <= (int) Finishing) { + struct timeval tvv; + int nsel; + time_t now; + fd_set readfds; + +#ifdef HAVE_SVC_GETREQSET + memmove(&readfds, &svc_fdset, sizeof(svc_fdset)); +#else /* not HAVE_SVC_GETREQSET */ + FD_ZERO(&readfds); +# ifdef HAVE_FD_SET_FDS_BITS + readfds.fds_bits[0] = svc_fds; +# else /* not HAVE_FD_SET_FDS_BITS */ + readfds = svc_fds; +# endif /* not HAVE_FD_SET_FDS_BITS */ +#endif /* not HAVE_SVC_GETREQSET */ + FD_SET(fwd_sock, &readfds); + + checkup(); + + /* + * If the full timeout code is not called, + * then recompute the time delta manually. + */ + now = clocktime(NULL); + + if (next_softclock <= now) { + if (amd_state == Finishing) + umount_exported(); + tvv.tv_sec = softclock(); + } else { + tvv.tv_sec = next_softclock - now; + } + tvv.tv_usec = 0; + + if (amd_state == Finishing && get_exported_ap(0) == NULL) { + flush_mntfs(); + amd_state = Quit; + break; + } + +#ifdef HAVE_FS_AUTOFS + autofs_add_fdset(&readfds); +#endif /* HAVE_FS_AUTOFS */ + + if (tvv.tv_sec <= 0) + tvv.tv_sec = SELECT_MAXWAIT; + if (tvv.tv_sec) { + dlog("Select waits for %ds", (int) tvv.tv_sec); + } else { + dlog("Select waits for Godot"); + } + + nsel = do_select(smask, FD_SETSIZE, &readfds, &tvv); + + switch (nsel) { + case -1: + if (errno == EINTR) { + dlog("select interrupted"); + continue; + } + plog(XLOG_ERROR, "select: %m"); + break; + + case 0: + break; + + default: + /* + * Read all pending NFS responses at once to avoid having responses + * queue up as a consequence of retransmissions. + */ + if (FD_ISSET(fwd_sock, &readfds)) { + FD_CLR(fwd_sock, &readfds); + --nsel; + do { + fwd_reply(); + } while (rpc_pending_now() > 0); + } + +#ifdef HAVE_FS_AUTOFS + if (nsel) + nsel = autofs_handle_fdset(&readfds, nsel); +#endif /* HAVE_FS_AUTOFS */ + + if (nsel) { + /* + * Anything left must be a normal + * RPC request. + */ +#ifdef HAVE_SVC_GETREQSET + svc_getreqset(&readfds); +#else /* not HAVE_SVC_GETREQSET */ +# ifdef HAVE_FD_SET_FDS_BITS + svc_getreq(readfds.fds_bits[0]); +# else /* not HAVE_FD_SET_FDS_BITS */ + svc_getreq(readfds); +# endif /* not HAVE_FD_SET_FDS_BITS */ +#endif /* not HAVE_SVC_GETREQSET */ + } + break; + } + } + +#ifdef HAVE_SIGACTION + sigprocmask(SIG_SETMASK, &smask, NULL); +#else /* not HAVE_SIGACTION */ + (void) sigsetmask(smask); +#endif /* not HAVE_SIGACTION */ + + if (amd_state == Quit) + amd_state = Done; + + return amd_state; +} + + +int +mount_automounter(int ppid) +{ + /* + * Old code replaced by rpc-trash patch. + * Erez Zadok <ezk@cs.columbia.edu> + int so = socket(AF_INET, SOCK_DGRAM, 0); + */ + SVCXPRT *udp_amqp = NULL, *tcp_amqp = NULL; + int nmount, ret; + int soNFS; + int udp_soAMQ, tcp_soAMQ; + struct netconfig *udp_amqncp, *tcp_amqncp; + + /* + * This must be done first, because it attempts to bind + * to various UDP ports and we don't want anything else + * potentially taking over those ports before we get a chance + * to reserve them. + */ + if (gopt.flags & CFM_RESTART_EXISTING_MOUNTS) + restart_automounter_nodes(); + + /* + * Start RPC forwarding + */ + if (fwd_init() != 0) + return 3; + + /* + * Construct the root automount node + */ + make_root_node(); + + /* + * Pick up the pieces from a previous run + * This is likely to (indirectly) need the rpc_fwd package + * so it *must* come after the call to fwd_init(). + */ + if (gopt.flags & CFM_RESTART_EXISTING_MOUNTS) + restart(); + + /* + * Create the nfs service for amd + * If nfs_port is already initialized, it means we + * already created the service during restart_automounter_nodes(). + */ + if (nfs_port == 0) { + ret = create_nfs_service(&soNFS, &nfs_port, &nfsxprt, nfs_program_2); + if (ret != 0) + return ret; + } + xsnprintf(pid_fsname, sizeof(pid_fsname), "%s:(pid%ld,port%u)", + am_get_hostname(), (long) am_mypid, nfs_port); + + /* security: if user sets -D amq, don't even create listening socket */ + if (!amuDebug(D_AMQ)) { + ret = create_amq_service(&udp_soAMQ, + &udp_amqp, + &udp_amqncp, + &tcp_soAMQ, + &tcp_amqp, + &tcp_amqncp, + gopt.preferred_amq_port); + if (ret != 0) + return ret; + } + +#ifdef HAVE_FS_AUTOFS + if (amd_use_autofs) { + /* + * Create the autofs service for amd. + */ + ret = create_autofs_service(); + /* if autofs service fails it is OK if using a test amd */ + if (ret != 0) { + plog(XLOG_WARNING, "autofs service registration failed, turning off autofs support"); + amd_use_autofs = 0; + } + } +#endif /* HAVE_FS_AUTOFS */ + + /* + * Mount the top-level auto-mountpoints + */ + nmount = mount_exported(); + + /* + * Now safe to tell parent that we are up and running + */ + if (ppid) + kill(ppid, SIGQUIT); + + if (nmount == 0) { + plog(XLOG_FATAL, "No work to do - quitting"); + amd_state = Done; + return 0; + } + + if (!amuDebug(D_AMQ)) { + /* + * Complete registration of amq (first TCP service then UDP) + */ + unregister_amq(); + + ret = amu_svc_register(tcp_amqp, get_amd_program_number(), AMQ_VERSION, + amq_program_1, IPPROTO_TCP, tcp_amqncp); + if (ret != 1) { + plog(XLOG_FATAL, "unable to register (AMQ_PROGRAM=%d, AMQ_VERSION, tcp)", get_amd_program_number()); + return 3; + } + + ret = amu_svc_register(udp_amqp, get_amd_program_number(), AMQ_VERSION, + amq_program_1, IPPROTO_UDP, udp_amqncp); + if (ret != 1) { + plog(XLOG_FATAL, "unable to register (AMQ_PROGRAM=%d, AMQ_VERSION, udp)", get_amd_program_number()); + return 4; + } + } + + /* + * Start timeout_mp rolling + */ + reschedule_timeout_mp(); + + /* + * Start the server + */ + if (run_rpc() != Done) { + plog(XLOG_FATAL, "run_rpc failed"); + amd_state = Done; + } + return 0; +} diff --git a/amd/nfs_subr.c b/amd/nfs_subr.c new file mode 100644 index 000000000000..80d3ca8e311c --- /dev/null +++ b/amd/nfs_subr.c @@ -0,0 +1,828 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/nfs_subr.c + * + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* + * Convert from UN*X to NFS error code. + * Some systems like linux define their own (see + * conf/mount/mount_linux.h). + */ +#ifndef nfs_error +# define nfs_error(e) ((nfsstat)(e)) +#endif /* nfs_error */ + +/* + * File Handle structure + * + * This is interpreted by indexing the exported array + * by fhh_id (for old-style filehandles), or by retrieving + * the node name from fhh_path (for new-style filehandles). + * + * The whole structure is mapped onto a standard fhandle_t + * when transmitted. + */ +struct am_fh { + u_int fhh_gen; /* generation number */ + union { + struct { + int fhh_type; /* old or new am_fh */ + pid_t fhh_pid; /* process id */ + int fhh_id; /* map id */ + } s; + char fhh_path[NFS_FHSIZE-sizeof(u_int)]; /* path to am_node */ + } u; +}; + + +/* forward declarations */ +/* converting am-filehandles to mount-points */ +static am_node *fh_to_mp3(am_nfs_fh *fhp, int *rp, int vop); +static am_node *fh_to_mp(am_nfs_fh *fhp); +static void count_map_entries(const am_node *mp, u_int *out_blocks, u_int *out_bfree, u_int *out_bavail); + + +static char * +do_readlink(am_node *mp, int *error_return) +{ + char *ln; + + /* + * If there is a readlink method then use it, + * otherwise if a link exists use that, + * otherwise use the mount point. + */ + if (mp->am_mnt->mf_ops->readlink) { + int retry = 0; + mp = (*mp->am_mnt->mf_ops->readlink) (mp, &retry); + if (mp == 0) { + *error_return = retry; + return 0; + } + /* reschedule_timeout_mp(); */ + } + + if (mp->am_link) { + ln = mp->am_link; + } else { + ln = mp->am_mnt->mf_mount; + } + + return ln; +} + + +voidp +nfsproc_null_2_svc(voidp argp, struct svc_req *rqstp) +{ + static char res; + + return (voidp) &res; +} + + +nfsattrstat * +nfsproc_getattr_2_svc(am_nfs_fh *argp, struct svc_req *rqstp) +{ + static nfsattrstat res; + am_node *mp; + int retry; + time_t now = clocktime(NULL); + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "getattr:"); + + mp = fh_to_mp3(argp, &retry, VLOOK_CREATE); + if (mp == 0) { + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "\tretry=%d", retry); + + if (retry < 0) { + amd_stats.d_drops++; + return 0; + } + res.ns_status = nfs_error(retry); + return &res; + } + + res = mp->am_attr; + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "\tstat(%s), size = %d, mtime=%ld.%ld", + mp->am_path, + (int) res.ns_u.ns_attr_u.na_size, + (long) res.ns_u.ns_attr_u.na_mtime.nt_seconds, + (long) res.ns_u.ns_attr_u.na_mtime.nt_useconds); + + /* Delay unmount of what was looked up */ + if (mp->am_timeo_w < 4 * gopt.am_timeo_w) + mp->am_timeo_w += gopt.am_timeo_w; + mp->am_ttl = now + mp->am_timeo_w; + + mp->am_stats.s_getattr++; + return &res; +} + + +nfsattrstat * +nfsproc_setattr_2_svc(nfssattrargs *argp, struct svc_req *rqstp) +{ + static nfsattrstat res; + + if (!fh_to_mp(&argp->sag_fhandle)) + res.ns_status = nfs_error(ESTALE); + else + res.ns_status = nfs_error(EROFS); + + return &res; +} + + +voidp +nfsproc_root_2_svc(voidp argp, struct svc_req *rqstp) +{ + static char res; + + return (voidp) &res; +} + + +nfsdiropres * +nfsproc_lookup_2_svc(nfsdiropargs *argp, struct svc_req *rqstp) +{ + static nfsdiropres res; + am_node *mp; + int retry; + uid_t uid; + gid_t gid; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "lookup:"); + + /* finally, find the effective uid/gid from RPC request */ + if (getcreds(rqstp, &uid, &gid, nfsxprt) < 0) + plog(XLOG_ERROR, "cannot get uid/gid from RPC credentials"); + xsnprintf(opt_uid, sizeof(uid_str), "%d", (int) uid); + xsnprintf(opt_gid, sizeof(gid_str), "%d", (int) gid); + + mp = fh_to_mp3(&argp->da_fhandle, &retry, VLOOK_CREATE); + if (mp == 0) { + if (retry < 0) { + amd_stats.d_drops++; + return 0; + } + res.dr_status = nfs_error(retry); + } else { + int error; + am_node *ap; + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "\tlookup(%s, %s)", mp->am_path, argp->da_name); + ap = mp->am_mnt->mf_ops->lookup_child(mp, argp->da_name, &error, VLOOK_CREATE); + if (ap && error < 0) + ap = mp->am_mnt->mf_ops->mount_child(ap, &error); + if (ap == 0) { + if (error < 0) { + amd_stats.d_drops++; + return 0; + } + res.dr_status = nfs_error(error); + } else { + /* + * XXX: EXPERIMENTAL! Delay unmount of what was looked up. This + * should reduce the chance for race condition between unmounting an + * entry synchronously, and re-mounting it asynchronously. + */ + if (ap->am_ttl < mp->am_ttl) + ap->am_ttl = mp->am_ttl; + mp_to_fh(ap, &res.dr_u.dr_drok_u.drok_fhandle); + res.dr_u.dr_drok_u.drok_attributes = ap->am_fattr; + res.dr_status = NFS_OK; + } + mp->am_stats.s_lookup++; + /* reschedule_timeout_mp(); */ + } + + return &res; +} + + +void +nfs_quick_reply(am_node *mp, int error) +{ + SVCXPRT *transp = mp->am_transp; + nfsdiropres res; + xdrproc_t xdr_result = (xdrproc_t) xdr_diropres; + + /* + * If there's a transp structure then we can reply to the client's + * nfs lookup request. + */ + if (transp) { + if (error == 0) { + /* + * Construct a valid reply to a lookup request. Same + * code as in nfsproc_lookup_2_svc() above. + */ + mp_to_fh(mp, &res.dr_u.dr_drok_u.drok_fhandle); + res.dr_u.dr_drok_u.drok_attributes = mp->am_fattr; + res.dr_status = NFS_OK; + } else + /* + * Return the error that was passed to us. + */ + res.dr_status = nfs_error(error); + + /* + * Send off our reply + */ + if (!svc_sendreply(transp, (XDRPROC_T_TYPE) xdr_result, (SVC_IN_ARG_TYPE) & res)) + svcerr_systemerr(transp); + + /* + * Free up transp. It's only used for one reply. + */ + XFREE(mp->am_transp); + dlog("Quick reply sent for %s", mp->am_mnt->mf_mount); + } +} + + +nfsreadlinkres * +nfsproc_readlink_2_svc(am_nfs_fh *argp, struct svc_req *rqstp) +{ + static nfsreadlinkres res; + am_node *mp; + int retry; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "readlink:"); + + mp = fh_to_mp3(argp, &retry, VLOOK_CREATE); + if (mp == 0) { + readlink_retry: + if (retry < 0) { + amd_stats.d_drops++; + return 0; + } + res.rlr_status = nfs_error(retry); + } else { + char *ln = do_readlink(mp, &retry); + if (ln == 0) + goto readlink_retry; + res.rlr_status = NFS_OK; + if (amuDebug(D_TRACE) && ln) + plog(XLOG_DEBUG, "\treadlink(%s) = %s", mp->am_path, ln); + res.rlr_u.rlr_data_u = ln; + mp->am_stats.s_readlink++; + } + + return &res; +} + + +nfsreadres * +nfsproc_read_2_svc(nfsreadargs *argp, struct svc_req *rqstp) +{ + static nfsreadres res; + + memset((char *) &res, 0, sizeof(res)); + res.rr_status = nfs_error(EACCES); + + return &res; +} + + +voidp +nfsproc_writecache_2_svc(voidp argp, struct svc_req *rqstp) +{ + static char res; + + return (voidp) &res; +} + + +nfsattrstat * +nfsproc_write_2_svc(nfswriteargs *argp, struct svc_req *rqstp) +{ + static nfsattrstat res; + + if (!fh_to_mp(&argp->wra_fhandle)) + res.ns_status = nfs_error(ESTALE); + else + res.ns_status = nfs_error(EROFS); + + return &res; +} + + +nfsdiropres * +nfsproc_create_2_svc(nfscreateargs *argp, struct svc_req *rqstp) +{ + static nfsdiropres res; + + if (!fh_to_mp(&argp->ca_where.da_fhandle)) + res.dr_status = nfs_error(ESTALE); + else + res.dr_status = nfs_error(EROFS); + + return &res; +} + + +static nfsstat * +unlink_or_rmdir(nfsdiropargs *argp, struct svc_req *rqstp, int unlinkp) +{ + static nfsstat res; + int retry; + + am_node *mp = fh_to_mp3(&argp->da_fhandle, &retry, VLOOK_DELETE); + if (mp == 0) { + if (retry < 0) { + amd_stats.d_drops++; + return 0; + } + res = nfs_error(retry); + goto out; + } + + if (mp->am_fattr.na_type != NFDIR) { + res = nfs_error(ENOTDIR); + goto out; + } + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "\tremove(%s, %s)", mp->am_path, argp->da_name); + + mp = mp->am_mnt->mf_ops->lookup_child(mp, argp->da_name, &retry, VLOOK_DELETE); + if (mp == 0) { + /* + * Ignore retries... + */ + if (retry < 0) + retry = 0; + /* + * Usual NFS workaround... + */ + else if (retry == ENOENT) + retry = 0; + res = nfs_error(retry); + } else { + forcibly_timeout_mp(mp); + res = NFS_OK; + } + +out: + return &res; +} + + +nfsstat * +nfsproc_remove_2_svc(nfsdiropargs *argp, struct svc_req *rqstp) +{ + return unlink_or_rmdir(argp, rqstp, TRUE); +} + + +nfsstat * +nfsproc_rename_2_svc(nfsrenameargs *argp, struct svc_req *rqstp) +{ + static nfsstat res; + + if (!fh_to_mp(&argp->rna_from.da_fhandle) || !fh_to_mp(&argp->rna_to.da_fhandle)) + res = nfs_error(ESTALE); + /* + * If the kernel is doing clever things with referenced files + * then let it pretend... + */ + else if (NSTREQ(argp->rna_to.da_name, ".nfs", 4)) + res = NFS_OK; + /* + * otherwise a failure + */ + else + res = nfs_error(EROFS); + + return &res; +} + + +nfsstat * +nfsproc_link_2_svc(nfslinkargs *argp, struct svc_req *rqstp) +{ + static nfsstat res; + + if (!fh_to_mp(&argp->la_fhandle) || !fh_to_mp(&argp->la_to.da_fhandle)) + res = nfs_error(ESTALE); + else + res = nfs_error(EROFS); + + return &res; +} + + +nfsstat * +nfsproc_symlink_2_svc(nfssymlinkargs *argp, struct svc_req *rqstp) +{ + static nfsstat res; + + if (!fh_to_mp(&argp->sla_from.da_fhandle)) + res = nfs_error(ESTALE); + else + res = nfs_error(EROFS); + + return &res; +} + + +nfsdiropres * +nfsproc_mkdir_2_svc(nfscreateargs *argp, struct svc_req *rqstp) +{ + static nfsdiropres res; + + if (!fh_to_mp(&argp->ca_where.da_fhandle)) + res.dr_status = nfs_error(ESTALE); + else + res.dr_status = nfs_error(EROFS); + + return &res; +} + + +nfsstat * +nfsproc_rmdir_2_svc(nfsdiropargs *argp, struct svc_req *rqstp) +{ + return unlink_or_rmdir(argp, rqstp, FALSE); +} + + +nfsreaddirres * +nfsproc_readdir_2_svc(nfsreaddirargs *argp, struct svc_req *rqstp) +{ + static nfsreaddirres res; + static nfsentry e_res[MAX_READDIR_ENTRIES]; + am_node *mp; + int retry; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "readdir:"); + + mp = fh_to_mp3(&argp->rda_fhandle, &retry, VLOOK_CREATE); + if (mp == 0) { + if (retry < 0) { + amd_stats.d_drops++; + return 0; + } + res.rdr_status = nfs_error(retry); + } else { + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "\treaddir(%s)", mp->am_path); + res.rdr_status = nfs_error((*mp->am_mnt->mf_ops->readdir) + (mp, argp->rda_cookie, + &res.rdr_u.rdr_reply_u, e_res, argp->rda_count)); + mp->am_stats.s_readdir++; + } + + return &res; +} + + +nfsstatfsres * +nfsproc_statfs_2_svc(am_nfs_fh *argp, struct svc_req *rqstp) +{ + static nfsstatfsres res; + am_node *mp; + int retry; + mntent_t mnt; + + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "statfs:"); + + mp = fh_to_mp3(argp, &retry, VLOOK_CREATE); + if (mp == 0) { + if (retry < 0) { + amd_stats.d_drops++; + return 0; + } + res.sfr_status = nfs_error(retry); + } else { + nfsstatfsokres *fp; + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "\tstat_fs(%s)", mp->am_path); + + /* + * just return faked up file system information + */ + fp = &res.sfr_u.sfr_reply_u; + + fp->sfrok_tsize = 1024; + fp->sfrok_bsize = 1024; + + /* check if map is browsable and show_statfs_entries=yes */ + if ((gopt.flags & CFM_SHOW_STATFS_ENTRIES) && + mp->am_mnt && mp->am_mnt->mf_mopts) { + mnt.mnt_opts = mp->am_mnt->mf_mopts; + if (amu_hasmntopt(&mnt, "browsable")) { + count_map_entries(mp, + &fp->sfrok_blocks, + &fp->sfrok_bfree, + &fp->sfrok_bavail); + } + } else { + fp->sfrok_blocks = 0; /* set to 1 if you don't want empty automounts */ + fp->sfrok_bfree = 0; + fp->sfrok_bavail = 0; + } + + res.sfr_status = NFS_OK; + mp->am_stats.s_statfs++; + } + + return &res; +} + + +/* + * count how many total entries there are in a map, and how many + * of them are in use. + */ +static void +count_map_entries(const am_node *mp, u_int *out_blocks, u_int *out_bfree, u_int *out_bavail) +{ + u_int blocks, bfree, bavail, i; + mntfs *mf; + mnt_map *mmp; + kv *k; + + blocks = bfree = bavail = 0; + if (!mp) + goto out; + mf = mp->am_mnt; + if (!mf) + goto out; + mmp = (mnt_map *) mf->mf_private; + if (!mmp) + goto out; + + /* iterate over keys */ + for (i = 0; i < NKVHASH; i++) { + for (k = mmp->kvhash[i]; k ; k = k->next) { + if (!k->key) + continue; + blocks++; + /* + * XXX: Need to count how many are actively in use and recompute + * bfree and bavail based on it. + */ + } + } + +out: + *out_blocks = blocks; + *out_bfree = bfree; + *out_bavail = bavail; +} + + +/* + * Convert from file handle to automount node. + */ +static am_node * +fh_to_mp3(am_nfs_fh *fhp, int *rp, int vop) +{ + struct am_fh *fp = (struct am_fh *) fhp; + am_node *ap = 0; + + if (fp->u.s.fhh_type != 0) { + /* New filehandle type */ + int len = sizeof(*fhp) - sizeof(fp->fhh_gen); + char *path = xmalloc(len+1); + /* + * Because fhp is treated as a filehandle we use memcpy + * instead of xstrlcpy. + */ + memcpy(path, (char *) fp->u.fhh_path, len); + path[len] = '\0'; + /* dlog("fh_to_mp3: new filehandle: %s", path); */ + + ap = path_to_exported_ap(path); + XFREE(path); + } else { + /* dlog("fh_to_mp3: old filehandle: %d", fp->u.s.fhh_id); */ + /* + * Check process id matches + * If it doesn't then it is probably + * from an old kernel-cached filehandle + * which is now out of date. + */ + if (fp->u.s.fhh_pid != get_server_pid()) { + dlog("fh_to_mp3: wrong pid %ld != my pid %ld", + (long) fp->u.s.fhh_pid, get_server_pid()); + goto drop; + } + + /* + * Get hold of the supposed mount node + */ + ap = get_exported_ap(fp->u.s.fhh_id); + } + + /* + * Check the generation number in the node + * matches the one from the kernel. If not + * then the old node has been timed out and + * a new one allocated. + */ + if (ap != NULL && ap->am_gen != fp->fhh_gen) + ap = 0; + + /* + * If it doesn't exists then drop the request + */ + if (!ap) + goto drop; + +#if 0 + /* + * If the node is hung then locate a new node + * for it. This implements the replicated filesystem + * retries. + */ + if (ap->am_mnt && FSRV_ISDOWN(ap->am_mnt->mf_server) && ap->am_parent) { + int error; + am_node *orig_ap = ap; + + dlog("fh_to_mp3: %s (%s) is hung: lookup alternative file server", + orig_ap->am_path, orig_ap->am_mnt->mf_info); + + /* + * Update modify time of parent node. + * With any luck the kernel will re-stat + * the child node and get new information. + */ + clocktime(&orig_ap->am_fattr.na_mtime); + + /* + * Call the parent's lookup routine for an object + * with the same name. This may return -1 in error + * if a mount is in progress. In any case, if no + * mount node is returned the error code is propagated + * to the caller. + */ + if (vop == VLOOK_CREATE) { + ap = orig_ap->am_parent->am_mnt->mf_ops->lookup_child(orig_ap->am_parent, orig_ap->am_name, &error, vop); + if (ap && error < 0) + ap = orig_ap->am_parent->am_mnt->mf_ops->mount_child(ap, &error); + } else { + ap = 0; + error = ESTALE; + } + if (ap == 0) { + if (error < 0 && amd_state == Finishing) + error = ENOENT; + *rp = error; + return 0; + } + + /* + * Update last access to original node. This + * avoids timing it out and so sending ESTALE + * back to the kernel. + * XXX - Not sure we need this anymore (jsp, 90/10/6). + */ + new_ttl(orig_ap); + + } +#endif + + /* + * Disallow references to objects being unmounted, unless + * they are automount points. + */ + if (ap->am_mnt && (ap->am_mnt->mf_flags & MFF_UNMOUNTING) && + !(ap->am_flags & AMF_ROOT)) { + if (amd_state == Finishing) + *rp = ENOENT; + else + *rp = -1; + return 0; + } + new_ttl(ap); + +drop: + if (!ap || !ap->am_mnt) { + /* + * If we are shutting down then it is likely + * that this node has disappeared because of + * a fast timeout. To avoid things thrashing + * just pretend it doesn't exist at all. If + * ESTALE is returned, some NFS clients just + * keep retrying (stupid or what - if it's + * stale now, what's it going to be in 5 minutes?) + */ + if (amd_state == Finishing) + *rp = ENOENT; + else + *rp = ESTALE; + amd_stats.d_stale++; + } + + return ap; +} + + +static am_node * +fh_to_mp(am_nfs_fh *fhp) +{ + int dummy; + + return fh_to_mp3(fhp, &dummy, VLOOK_CREATE); +} + + +/* + * Convert from automount node to file handle. + */ +void +mp_to_fh(am_node *mp, am_nfs_fh *fhp) +{ + u_int pathlen; + struct am_fh *fp = (struct am_fh *) fhp; + + memset((char *) fhp, 0, sizeof(am_nfs_fh)); + + /* Store the generation number */ + fp->fhh_gen = mp->am_gen; + + pathlen = strlen(mp->am_path); + if (pathlen <= sizeof(*fhp) - sizeof(fp->fhh_gen)) { + /* dlog("mp_to_fh: new filehandle: %s", mp->am_path); */ + + /* + * Because fhp is treated as a filehandle we use memcpy instead of + * xstrlcpy. + */ + memcpy(fp->u.fhh_path, mp->am_path, pathlen); /* making a filehandle */ + } else { + /* + * Take the process id + */ + fp->u.s.fhh_pid = get_server_pid(); + + /* + * ... the map number + */ + fp->u.s.fhh_id = mp->am_mapno; + + /* + * ... and the generation number (previously stored) + * to make a "unique" triple that will never + * be reallocated except across reboots (which doesn't matter) + * or if we are unlucky enough to be given the same + * pid as a previous amd (very unlikely). + */ + /* dlog("mp_to_fh: old filehandle: %d", fp->u.s.fhh_id); */ + } +} diff --git a/amd/ops_TEMPLATE.c b/amd/ops_TEMPLATE.c new file mode 100644 index 000000000000..3f7421984412 --- /dev/null +++ b/amd/ops_TEMPLATE.c @@ -0,0 +1,305 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/ops_TEMPLATE.c + * + */ + +/* + * An empty template for an amd pseudo filesystem "foofs". + */ + +/* + * NOTE: if this is an Amd file system, prepend "amfs_" to all foofs symbols + * and renamed the file name to amfs_foofs.c. If it is a native file system + * (such as pcfs, isofs, or ffs), then you can keep the names as is, and + * just rename the file to ops_foofs.c. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* forward declarations */ +static char *foofs_match(am_opts *fo); +static int foofs_init(mntfs *mf); +static int foofs_mount(am_node *mp, mntfs *mf); +static int foofs_umount(am_node *mp, mntfs *mf); +static am_node *foofs_lookuppn(am_node *mp, char *fname, int *error_return, int op); +static int foofs_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, u_int count); +static am_node *foofs_readlink(am_node *mp, int *error_return); +static void foofs_mounted(am_node *am, mntfs *mf); +static void foofs_umounted(am_node *mp, mntfs *mf); +static fserver *foofs_ffserver(mntfs *mf); + + +/* + * Foofs operations. + * Define only those you need, others set to 0 (NULL) + */ +am_ops foofs_ops = +{ + "foofs", /* name of file system */ + foofs_match, /* match */ + foofs_init, /* initialize */ + foofs_mount, /* mount vnode */ + foofs_umount, /* unmount vnode */ + foofs_lookup_child, /* lookup path-name */ + foofs_mount_child, /* mount path-name */ + foofs_readdir, /* read directory */ + foofs_readlink, /* read link */ + foofs_mounted, /* after-mount extra actions */ + foofs_umounted, /* after-umount extra actions */ + foofs_ffserver, /* find a file server */ + foofs_get_wchan, /* return the waiting channel */ + FS_MKMNT | FS_BACKGROUND | FS_AMQINFO, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_TEMPLATE_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ +}; + + +/* + * Check that f/s has all needed fields. + * Returns: matched string if found, NULL otherwise. + */ +static char * +foofs_match(am_opts *fo) +{ + char *cp = "fill this with a way to find the match"; + + plog(XLOG_INFO, "entering foofs_match..."); + + if (cp) + return cp; /* OK */ + + return NULL; /* not OK */ +} + + +/* + * Initialize. + * Returns: 0 if OK, non-zero (errno) if failed. + */ +static int +foofs_init(mntfs *mf) +{ + int error = 0; + + plog(XLOG_INFO, "entering foofs_init..."); + + error = EPERM; /* XXX: fixme */ + return error; +} + + +/* + * Mount vnode. + * Returns: 0 if OK, non-zero (errno) if failed. + */ +static int +foofs_mount(am_node *mp) +{ + int error = 0; + + plog(XLOG_INFO, "entering foofs_mount..."); + + error = EPERM; /* XXX: fixme */ + return error; +} + + +/* + * Mount vfs. + * Returns: 0 if OK, non-zero (errno) if failed. + */ +static int +foofs_fmount(mntfs *mf) +{ + int error = 0; + + plog(XLOG_INFO, "entering foofs_fmount..."); + + error = EPERM; /* XXX: fixme */ + return error; +} + + +/* + * Unmount vnode. + * Returns: 0 if OK, non-zero (errno) if failed. + */ +static int +foofs_umount(am_node *mp) +{ + int error = 0; + + plog(XLOG_INFO, "entering foofs_umount..."); + + error = EPERM; /* XXX: fixme */ + return error; +} + + +/* + * Unmount VFS. + * Returns: 0 if OK, non-zero (errno) if failed. + */ +static int +foofs_fumount(mntfs *mf) +{ + int error = 0; + + plog(XLOG_INFO, "entering foofs_fumount..."); + + error = EPERM; /* XXX: fixme */ + return error; +} + + +/* + * Lookup path-name. + * Returns: the am_node that was found, or NULL if failed. + * If failed, also fills in errno in error_return. + */ +static am_node * +foofs_lookuppn(am_node *mp, char *fname, int *error_return, int op) +{ + int error = 0; + + plog(XLOG_INFO, "entering foofs_lookuppn..."); + + error = EPERM; /* XXX: fixme */ + + *error_return = error; + return NULL; +} + + +/* + * Read directory. + * Returns: 0 if OK, non-zero (errno) if failed. + * If OK, fills in ep with chain of directory entries. + */ +static int +foofs_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, u_int count) +{ + int error = 0; + + plog(XLOG_INFO, "entering foofs_readdir..."); + + error = EPERM; /* XXX: fixme */ + return error; +} + + +/* + * Read link. + * Returns: am_node found, or NULL if not found. + * If failed, fills in errno in error_return. + */ +static am_node * +foofs_readlink(am_node *mp, int *error_return) +{ + int error = 0; + + plog(XLOG_INFO, "entering foofs_readlink..."); + + error = EPERM; /* XXX: fixme */ + + *error_return = error; + return NULL; +} + + +/* + * Async mount callback function. + * After the base mount went OK, sometimes + * there are additional actions that are needed. See union_mounted() and + * toplvl_mounted(). + */ +static void +foofs_mounted(mntfs *mf) +{ + plog(XLOG_INFO, "entering foofs_mounted..."); + + return; +} + + +/* + * Async unmount callback function. + * After the base umount() succeeds, we may want to take extra actions, + * such as informing remote mount daemons that we've unmounted them. + * See amfs_auto_umounted(), host_umounted(), nfs_umounted(). + */ +static void +foofs_umounted(am_node *mp) +{ + plog(XLOG_INFO, "entering foofs_umounted..."); + + return; +} + + +/* + * Find a file server. + * Returns: fserver of found server, or NULL if not found. + */ +static fserver * +foofs_ffserver(mntfs *mf) +{ + plog(XLOG_INFO, "entering foofs_ffserver..."); + + return NULL; +} + + +/* + * Normally just return mf. Only inherit needs to do special tricks. + */ +static wchan_t * +foofs_get_wchan(mntfs *mf) +{ + plog(XLOG_INFO, "entering foofs_get_wchan..."); + + return mf; +} diff --git a/amd/ops_autofs.c b/amd/ops_autofs.c new file mode 100644 index 000000000000..bd0bb122fe03 --- /dev/null +++ b/amd/ops_autofs.c @@ -0,0 +1,1279 @@ +/* + * Copyright (c) 1997-2001 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * %W% (Berkeley) %G% + * + * $Id: ops_autofs.c,v 1.7.2.4 2001/04/24 06:17:40 ib42 Exp $ + * + */ + +/* + * Automounter filesystem + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* + * KLUDGE: wrap whole file in HAVE_FS_AUTOFS, because + * not all systems with an automounter file system are supported + * by am-utils yet... + */ + +#ifdef HAVE_FS_AUTOFS + +/* + * MACROS: + */ +#ifndef AUTOFS_NULL +# define AUTOFS_NULL ((u_long)0) +#endif /* not AUTOFS_NULL */ + +/* + * VARIABLES: + */ + +/* forward declarations */ +static int mount_autofs(char *dir, char *opts); +static int autofs_mount_1_svc(struct mntrequest *mr, struct mntres *result, struct authunix_parms *cred); +static int autofs_unmount_1_svc(struct umntrequest *ur, struct umntres *result, struct authunix_parms *cred); + +/* external declarations */ +extern bool_t xdr_mntrequest(XDR *, mntrequest *); +extern bool_t xdr_mntres(XDR *, mntres *); +extern bool_t xdr_umntrequest(XDR *, umntrequest *); +extern bool_t xdr_umntres(XDR *, umntres *); + +/* + * STRUCTURES: + */ + +/* Sun's kernel-based automounter-supporting file system */ +am_ops autofs_ops = +{ + "autofs", + amfs_auto_match, + 0, /* amfs_auto_init */ + autofs_mount, + 0, + autofs_umount, + 0, + amfs_auto_lookuppn, + amfs_auto_readdir, /* browsable version of readdir() */ + 0, /* autofs_readlink */ + autofs_mounted, + 0, /* autofs_umounted */ + find_amfs_auto_srvr, + FS_MKMNT | FS_NOTIMEOUT | FS_BACKGROUND | FS_AMQINFO | FS_DIRECTORY +}; + + +/**************************************************************************** + *** FUNCTIONS *** + ****************************************************************************/ + +/* + * Mount the top-level using autofs + */ +int +autofs_mount(am_node *mp) +{ + mntfs *mf = mp->am_mnt; + struct stat stb; + char opts[256], preopts[256]; + int error; + char *mnttype; + + /* + * Mounting the automounter. + * Make sure the mount directory exists, construct + * the mount options and call the mount_autofs routine. + */ + + if (stat(mp->am_path, &stb) < 0) { + return errno; + } else if ((stb.st_mode & S_IFMT) != S_IFDIR) { + plog(XLOG_WARNING, "%s is not a directory", mp->am_path); + return ENOTDIR; + } + if (mf->mf_ops == &autofs_ops) + mnttype = "indirect"; + else if (mf->mf_ops == &amfs_direct_ops) + mnttype = "direct"; +#ifdef HAVE_AMU_FS_UNION + else if (mf->mf_ops == &amfs_union_ops) + mnttype = "union"; +#endif /* HAVE_AMU_FS_UNION */ + else + mnttype = "auto"; + + /* + * Construct some mount options: + * + * Tack on magic map=<mapname> option in mtab to emulate + * SunOS automounter behavior. + */ + preopts[0] = '\0'; +#ifdef MNTTAB_OPT_INTR + strcat(preopts, MNTTAB_OPT_INTR); + strcat(preopts, ","); +#endif /* MNTTAB_OPT_INTR */ +#ifdef MNTTAB_OPT_IGNORE + strcat(preopts, MNTTAB_OPT_IGNORE); + strcat(preopts, ","); +#endif /* MNTTAB_OPT_IGNORE */ + sprintf(opts, "%s%s,%s=%d,%s=%d,%s=%d,%s,map=%s", + preopts, + MNTTAB_OPT_RW, + MNTTAB_OPT_PORT, nfs_port, + MNTTAB_OPT_TIMEO, gopt.amfs_auto_timeo, + MNTTAB_OPT_RETRANS, gopt.amfs_auto_retrans, + mnttype, mf->mf_info); + + /* now do the mount */ + error = mount_autofs(mf->mf_mount, opts); + if (error) { + errno = error; + plog(XLOG_FATAL, "mount_autofs: %m"); + return error; + } + return 0; +} + + +void +autofs_mounted(mntfs *mf) +{ + amfs_auto_mkcacheref(mf); +} + + +/* + * Unmount a top-level automount node + */ +int +autofs_umount(am_node *mp) +{ + int error; + struct stat stb; + + /* + * The lstat is needed if this mount is type=direct. When that happens, + * the kernel cache gets confused between the underlying type (dir) and + * the mounted type (link) and so needs to be re-synced before the + * unmount. This is all because the unmount system call follows links and + * so can't actually unmount a link (stupid!). It was noted that doing an + * ls -ld of the mount point to see why things were not working actually + * fixed the problem - so simulate an ls -ld here. + */ + if (lstat(mp->am_path, &stb) < 0) { +#ifdef DEBUG + dlog("lstat(%s): %m", mp->am_path); +#endif /* DEBUG */ + } + error = UMOUNT_FS(mp->am_path, mnttab_file_name); + if (error == EBUSY && mp->am_flags & AMF_AUTOFS) { + plog(XLOG_WARNING, "autofs_unmount of %s busy (autofs). exit", mp->am_path); + error = 0; /* fake unmount was ok */ + } + return error; +} + + +/* + * Mount an automounter directory. + * The automounter is connected into the system + * as a user-level NFS server. mount_autofs constructs + * the necessary NFS parameters to be given to the + * kernel so that it will talk back to us. + */ +static int +mount_autofs(char *dir, char *opts) +{ + char fs_hostname[MAXHOSTNAMELEN + MAXPATHLEN + 1]; + char *map_opt, buf[MAXHOSTNAMELEN]; + int retry, error, flags; + struct utsname utsname; + mntent_t mnt; + autofs_args_t autofs_args; + MTYPE_TYPE type = MOUNT_TYPE_AUTOFS; + + memset((voidp) &autofs_args, 0, sizeof(autofs_args)); /* Paranoid */ + + memset((voidp) &mnt, 0, sizeof(mnt)); + mnt.mnt_dir = dir; + mnt.mnt_fsname = pid_fsname; + mnt.mnt_opts = opts; + mnt.mnt_type = type; + + retry = hasmntval(&mnt, "retry"); + if (retry <= 0) + retry = 2; /* XXX */ + + /* + * SET MOUNT ARGS + */ + if (uname(&utsname) < 0) { + strcpy(buf, "localhost.autofs"); + } else { + strcpy(buf, utsname.nodename); + strcat(buf, ".autofs"); + } +#ifdef HAVE_FIELD_AUTOFS_ARGS_T_ADDR + autofs_args.addr.buf = buf; + autofs_args.addr.len = strlen(autofs_args.addr.buf); + autofs_args.addr.maxlen = autofs_args.addr.len; +#endif /* HAVE_FIELD_AUTOFS_ARGS_T_ADDR */ + + autofs_args.path = dir; + autofs_args.opts = opts; + + map_opt = hasmntopt(&mnt, "map"); + if (map_opt) { + map_opt += sizeof("map="); /* skip the "map=" */ + if (map_opt == NULL) { + plog(XLOG_WARNING, "map= has a null map name. reset to amd.unknown"); + map_opt = "amd.unknown"; + } + } + autofs_args.map = map_opt; + + /* XXX: these I set arbitrarily... */ + autofs_args.mount_to = 300; + autofs_args.rpc_to = 60; + autofs_args.direct = 0; + + /* + * Make a ``hostname'' string for the kernel + */ + sprintf(fs_hostname, "pid%ld@%s:%s", + (long) (foreground ? am_mypid : getppid()), + am_get_hostname(), dir); + + /* + * Most kernels have a name length restriction. + */ + if (strlen(fs_hostname) >= MAXHOSTNAMELEN) + strcpy(fs_hostname + MAXHOSTNAMELEN - 3, ".."); + + /* + * Finally we can compute the mount flags set above. + */ + flags = compute_mount_flags(&mnt); + + /* + * This is it! Here we try to mount amd on its mount points. + */ + error = mount_fs(&mnt, flags, (caddr_t) &autofs_args, retry, type, 0, NULL, mnttab_file_name); + return error; +} + + +/****************************************************************************/ +/* autofs program dispatcher */ +void +autofs_program_1(struct svc_req *rqstp, SVCXPRT *transp) +{ + int ret; + union { + mntrequest autofs_mount_1_arg; + umntrequest autofs_umount_1_arg; + } argument; + union { + mntres mount_res; + umntres umount_res; + } result; + + bool_t (*xdr_argument)(), (*xdr_result)(); + int (*local)(); + + switch (rqstp->rq_proc) { + + case AUTOFS_NULL: + svc_sendreply(transp, + (XDRPROC_T_TYPE) xdr_void, + (SVC_IN_ARG_TYPE) NULL); + return; + + case AUTOFS_MOUNT: + xdr_argument = xdr_mntrequest; + xdr_result = xdr_mntres; + local = (int (*)()) autofs_mount_1_svc; + break; + + case AUTOFS_UNMOUNT: + xdr_argument = xdr_umntrequest; + xdr_result = xdr_umntres; + local = (int (*)()) autofs_unmount_1_svc; + break; + + default: + svcerr_noproc(transp); + return; + } + + memset((char *) &argument, 0, sizeof(argument)); + if (!svc_getargs(transp, + (XDRPROC_T_TYPE) xdr_argument, + (SVC_IN_ARG_TYPE) &argument)) { + plog(XLOG_ERROR, + "AUTOFS xdr decode failed for %d %d %d", + (int) rqstp->rq_prog, (int) rqstp->rq_vers, (int) rqstp->rq_proc); + svcerr_decode(transp); + return; + } + + ret = (*local) (&argument, &result, rqstp); + if (!svc_sendreply(transp, + (XDRPROC_T_TYPE) xdr_result, + (SVC_IN_ARG_TYPE) &result)) { + svcerr_systemerr(transp); + } + + if (!svc_freeargs(transp, + (XDRPROC_T_TYPE) xdr_argument, + (SVC_IN_ARG_TYPE) &argument)) { + plog(XLOG_FATAL, "unable to free rpc arguments in autofs_program_1"); + going_down(1); + } +} + + +static int +autofs_mount_1_svc(struct mntrequest *mr, struct mntres *result, struct authunix_parms *cred) +{ + int err = 0; + am_node *anp, *anp2; + + plog(XLOG_INFO, "XXX: autofs_mount_1_svc: %s:%s:%s:%s", + mr->map, mr->name, mr->opts, mr->path); + + /* look for map (eg. "/home") */ + anp = find_ap(mr->path); + if (!anp) { + plog(XLOG_ERROR, "map %s not found", mr->path); + err = ENOENT; + goto out; + } + /* turn on autofs in map flags */ + if (!(anp->am_flags & AMF_AUTOFS)) { + plog(XLOG_INFO, "turning on AMF_AUTOFS for node %s", mr->path); + anp->am_flags |= AMF_AUTOFS; + } + + /* + * Look for (and create if needed) the new node. + * + * If an error occurred, return it. If a -1 was returned, that indicates + * that a mount is in progress, so sleep a while (while the backgrounded + * mount is happening), and then signal the autofs to retry the mount. + * + * There's something I don't understand. I was thinking that this code + * here is the one which will succeed eventually and will send an RPC + * reply to the kernel, but apparently that happens somewhere else, not + * here. It works though, just that I don't know how. Arg. -Erez. + * */ + err = 0; + anp2 = autofs_lookuppn(anp, mr->name, &err, VLOOK_CREATE); + if (!anp2) { + if (err == -1) { /* then tell autofs to retry */ + sleep(1); + err = EAGAIN; + } + goto out; + } + +out: + result->status = err; + return err; +} + + +static int +autofs_unmount_1_svc(struct umntrequest *ur, struct umntres *result, struct authunix_parms *cred) +{ + int err = 0; + +#ifdef HAVE_FIELD_UMNTREQUEST_RDEVID + plog(XLOG_INFO, "XXX: autofs_unmount_1_svc: %d:%lu:%lu:0x%lx", + ur->isdirect, (unsigned long) ur->devid, (unsigned long) ur->rdevid, + (unsigned long) ur->next); +#else /* HAVE_FIELD_UMNTREQUEST_RDEVID */ + plog(XLOG_INFO, "XXX: autofs_unmount_1_svc: %d:%lu:0x%lx", + ur->isdirect, (unsigned long) ur->devid, + (unsigned long) ur->next); +#endif /* HAVE_FIELD_UMNTREQUEST_RDEVID */ + + err = EINVAL; /* XXX: not implemented yet */ + goto out; + +out: + result->status = err; + return err; +} + + +/* + * Pick a file system to try mounting and + * do that in the background if necessary + * + For each location: + if it is new -defaults then + extract and process + continue; + fi + if it is a cut then + if a location has been tried then + break; + fi + continue; + fi + parse mount location + discard previous mount location if required + find matching mounted filesystem + if not applicable then + this_error = No such file or directory + continue + fi + if the filesystem failed to be mounted then + this_error = error from filesystem + elif the filesystem is mounting or unmounting then + this_error = -1 + elif the fileserver is down then + this_error = -1 + elif the filesystem is already mounted + this_error = 0 + break + fi + if no error on this mount then + this_error = initialize mount point + fi + if no error on this mount and mount is delayed then + this_error = -1 + fi + if this_error < 0 then + retry = true + fi + if no error on this mount then + make mount point if required + fi + if no error on this mount then + if mount in background then + run mount in background + return -1 + else + this_error = mount in foreground + fi + fi + if an error occurred on this mount then + update stats + save error in mount point + fi + endfor + */ +static int +autofs_bgmount(struct continuation *cp, int mpe) +{ + mntfs *mf = cp->mp->am_mnt; /* Current mntfs */ + mntfs *mf_retry = 0; /* First mntfs which needed retrying */ + int this_error = -1; /* Per-mount error */ + int hard_error = -1; + int mp_error = mpe; + + /* + * Try to mount each location. + * At the end: + * hard_error == 0 indicates something was mounted. + * hard_error > 0 indicates everything failed with a hard error + * hard_error < 0 indicates nothing could be mounted now + */ + for (; this_error && *cp->ivec; cp->ivec++) { + am_ops *p; + am_node *mp = cp->mp; + char *link_dir; + int dont_retry; + + if (hard_error < 0) + hard_error = this_error; + + this_error = -1; + + if (**cp->ivec == '-') { + /* + * Pick up new defaults + */ + if (cp->auto_opts && *cp->auto_opts) + cp->def_opts = str3cat(cp->def_opts, cp->auto_opts, ";", *cp->ivec + 1); + else + cp->def_opts = strealloc(cp->def_opts, *cp->ivec + 1); +#ifdef DEBUG + dlog("Setting def_opts to \"%s\"", cp->def_opts); +#endif /* DEBUG */ + continue; + } + /* + * If a mount has been attempted, and we find + * a cut then don't try any more locations. + */ + if (STREQ(*cp->ivec, "/") || STREQ(*cp->ivec, "||")) { + if (cp->tried) { +#ifdef DEBUG + dlog("Cut: not trying any more locations for %s", + mp->am_path); +#endif /* DEBUG */ + break; + } + continue; + } + + /* match the operators */ + p = ops_match(&cp->fs_opts, *cp->ivec, cp->def_opts, mp->am_path, cp->key, mp->am_parent->am_mnt->mf_info); + + /* + * Find a mounted filesystem for this node. + */ + mp->am_mnt = mf = realloc_mntfs(mf, p, &cp->fs_opts, + cp->fs_opts.opt_fs, + cp->fs_opts.fs_mtab, + cp->auto_opts, + cp->fs_opts.opt_opts, + cp->fs_opts.opt_remopts); + + p = mf->mf_ops; +#ifdef DEBUG + dlog("Got a hit with %s", p->fs_type); +#endif /* DEBUG */ + + /* + * Note whether this is a real mount attempt + */ + if (p == &amfs_error_ops) { + plog(XLOG_MAP, "Map entry %s for %s did not match", *cp->ivec, mp->am_path); + if (this_error <= 0) + this_error = ENOENT; + continue; + } else { + if (cp->fs_opts.fs_mtab) { + plog(XLOG_MAP, "Trying mount of %s on \"%s\" fstype %s", + cp->fs_opts.fs_mtab, mp->am_path, p->fs_type); + } + cp->tried = TRUE; + } + + this_error = 0; + dont_retry = FALSE; + + if (mp->am_link) { + XFREE(mp->am_link); + mp->am_link = 0; + } + link_dir = mf->mf_fo->opt_sublink; + + if (link_dir && *link_dir) { + if (*link_dir == '/') { + mp->am_link = strdup(link_dir); + } else { + /* + * try getting fs option from continuation, not mountpoint! + * Don't try logging the string from mf, since it may be bad! + */ + if (cp->fs_opts.opt_fs != mf->mf_fo->opt_fs) + plog(XLOG_ERROR, "use %s instead of 0x%lx", + cp->fs_opts.opt_fs, (unsigned long) mf->mf_fo->opt_fs); + + mp->am_link = str3cat((char *) 0, + cp->fs_opts.opt_fs, "/", link_dir); + + normalize_slash(mp->am_link); + } + } + + if (mf->mf_error > 0) { + this_error = mf->mf_error; + } else if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) { + /* + * Still mounting - retry later + */ +#ifdef DEBUG + dlog("Duplicate pending mount fstype %s", p->fs_type); +#endif /* DEBUG */ + this_error = -1; + } else if (FSRV_ISDOWN(mf->mf_server)) { + /* + * Would just mount from the same place + * as a hung mount - so give up + */ +#ifdef DEBUG + dlog("%s is already hung - giving up", mf->mf_mount); +#endif /* DEBUG */ + mp_error = EWOULDBLOCK; + dont_retry = TRUE; + this_error = -1; + } else if (mf->mf_flags & MFF_MOUNTED) { +#ifdef DEBUG + dlog("duplicate mount of \"%s\" ...", mf->mf_info); +#endif /* DEBUG */ + + /* + * Just call mounted() + */ + am_mounted(mp); + + this_error = 0; + break; + } + + /* + * Will usually need to play around with the mount nodes + * file attribute structure. This must be done here. + * Try and get things initialized, even if the fileserver + * is not known to be up. In the common case this will + * progress things faster. + */ + if (!this_error) { + /* + * Fill in attribute fields. + */ + if (mf->mf_ops->fs_flags & FS_DIRECTORY) + mk_fattr(mp, NFDIR); + else + mk_fattr(mp, NFLNK); + + if (p->fs_init) + this_error = (*p->fs_init) (mf); + } + + /* + * Make sure the fileserver is UP before doing any more work + */ + if (!FSRV_ISUP(mf->mf_server)) { +#ifdef DEBUG + dlog("waiting for server %s to become available", mf->mf_server->fs_host); +#endif /* DEBUG */ + this_error = -1; + } + + if (!this_error && mf->mf_fo->opt_delay) { + /* + * If there is a delay timer on the mount + * then don't try to mount if the timer + * has not expired. + */ + int i = atoi(mf->mf_fo->opt_delay); + if (i > 0 && clocktime() < (cp->start + i)) { +#ifdef DEBUG + dlog("Mount of %s delayed by %lds", mf->mf_mount, i - clocktime() + cp->start); +#endif /* DEBUG */ + this_error = -1; + } + } + + if (this_error < 0 && !dont_retry) { + if (!mf_retry) + mf_retry = dup_mntfs(mf); + cp->retry = TRUE; + } + + if (!this_error) { + if (p->fs_flags & FS_MBACKGROUND) { + mf->mf_flags |= MFF_MOUNTING; /* XXX */ +#ifdef DEBUG + dlog("backgrounding mount of \"%s\"", mf->mf_mount); +#endif /* DEBUG */ + if (cp->callout) { + untimeout(cp->callout); + cp->callout = 0; + } + run_task(try_mount, (voidp) mp, amfs_auto_cont, (voidp) cp); + mf->mf_flags |= MFF_MKMNT; /* XXX */ + if (mf_retry) + free_mntfs(mf_retry); + return -1; + } else { +#ifdef DEBUG + dlog("foreground mount of \"%s\" ...", mf->mf_info); +#endif /* DEBUG */ + this_error = try_mount((voidp) mp); + if (this_error < 0) { + if (!mf_retry) + mf_retry = dup_mntfs(mf); + cp->retry = TRUE; + } + } + } + + if (this_error >= 0) { + if (this_error > 0) { + amd_stats.d_merr++; + if (mf != mf_retry) { + mf->mf_error = this_error; + mf->mf_flags |= MFF_ERROR; + } + } + + /* + * Wakeup anything waiting for this mount + */ + wakeup((voidp) mf); + } + } + + if (this_error && cp->retry) { + free_mntfs(mf); + mf = cp->mp->am_mnt = mf_retry; + /* + * Not retrying again (so far) + */ + cp->retry = FALSE; + cp->tried = FALSE; + /* + * Start at the beginning. + * Rewind the location vector and + * reset the default options. + */ + cp->ivec = cp->xivec; + cp->def_opts = strealloc(cp->def_opts, cp->auto_opts); + /* + * Arrange that autofs_bgmount is called + * after anything else happens. + */ +#ifdef DEBUG + dlog("Arranging to retry mount of %s", cp->mp->am_path); +#endif /* DEBUG */ + sched_task(amfs_auto_retry, (voidp) cp, (voidp) mf); + if (cp->callout) + untimeout(cp->callout); + cp->callout = timeout(RETRY_INTERVAL, wakeup, (voidp) mf); + + cp->mp->am_ttl = clocktime() + RETRY_INTERVAL; + + /* + * Not done yet - so don't return anything + */ + return -1; + } + + if (hard_error < 0 || this_error == 0) + hard_error = this_error; + + /* + * Discard handle on duff filesystem. + * This should never happen since it + * should be caught by the case above. + */ + if (mf_retry) { + if (hard_error) + plog(XLOG_ERROR, "discarding a retry mntfs for %s", mf_retry->mf_mount); + free_mntfs(mf_retry); + } + + /* + * If we get here, then either the mount succeeded or + * there is no more mount information available. + */ + if (hard_error < 0 && mp_error) + hard_error = cp->mp->am_error = mp_error; + if (hard_error > 0) { + /* + * Set a small(ish) timeout on an error node if + * the error was not a time out. + */ + switch (hard_error) { + case ETIMEDOUT: + case EWOULDBLOCK: + cp->mp->am_timeo = 17; + break; + default: + cp->mp->am_timeo = 5; + break; + } + new_ttl(cp->mp); + } + + /* + * Make sure that the error value in the mntfs has a + * reasonable value. + */ + if (mf->mf_error < 0) { + mf->mf_error = hard_error; + if (hard_error) + mf->mf_flags |= MFF_ERROR; + } + + /* + * In any case we don't need the continuation any more + */ + free_continuation(cp); + + return hard_error; +} + + +/* + * Automount interface to RPC lookup routine + * Find the corresponding entry and return + * the file handle for it. + */ +am_node * +autofs_lookuppn(am_node *mp, char *fname, int *error_return, int op) +{ + am_node *ap, *new_mp, *ap_hung; + char *info; /* Mount info - where to get the file system */ + char **ivec, **xivec; /* Split version of info */ + char *auto_opts; /* Automount options */ + int error = 0; /* Error so far */ + char path_name[MAXPATHLEN]; /* General path name buffer */ + char apath[MAXPATHLEN]; /* autofs path (added space) */ + char *pfname; /* Path for database lookup */ + struct continuation *cp; /* Continuation structure if need to mount */ + int in_progress = 0; /* # of (un)mount in progress */ + char *dflts; + mntfs *mf; + +#ifdef DEBUG + dlog("in autofs_lookuppn"); +#endif /* DEBUG */ + + /* + * If the server is shutting down + * then don't return information + * about the mount point. + */ + if (amd_state == Finishing) { +#ifdef DEBUG + if ((mf = mp->am_mnt) == 0 || mf->mf_ops == &amfs_direct_ops) { + dlog("%s mount ignored - going down", fname); + } else { + dlog("%s/%s mount ignored - going down", mp->am_path, fname); + } +#endif /* DEBUG */ + 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) { +#ifdef DEBUG + dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path); +#endif /* DEBUG */ + 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. + * fname is now a private copy. + */ + fname = expand_key(fname); + + for (ap_hung = 0, ap = mp->am_child; ap; ap = ap->am_osib) { + /* + * Otherwise search children of this node + */ + if (FSTREQ(ap->am_name, fname)) { + mf = ap->am_mnt; + if (ap->am_error) { + error = ap->am_error; + continue; + } + /* + * If the error code is undefined then it must be + * in progress. + */ + if (mf->mf_error < 0) + goto in_progrss; + + /* + * Check for a hung node + */ + if (FSRV_ISDOWN(mf->mf_server)) { +#ifdef DEBUG + dlog("server hung"); +#endif /* DEBUG */ + error = ap->am_error; + ap_hung = ap; + continue; + } + /* + * 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. + */ +#ifdef DEBUG + dlog("ignoring mount of %s in %s -- flags (%x) in progress", + fname, mf->mf_mount, mf->mf_flags); +#endif /* DEBUG */ + in_progress++; + continue; + } + + /* + * Otherwise we have a hit: return the current mount point. + */ +#ifdef DEBUG + dlog("matched %s in %s", fname, ap->am_path); +#endif /* DEBUG */ + XFREE(fname); + return ap; + } + } + + if (in_progress) { +#ifdef DEBUG + dlog("Waiting while %d mount(s) in progress", in_progress); +#endif /* DEBUG */ + XFREE(fname); + ereturn(-1); + } + + /* + * If an error occurred then return it. + */ + if (error) { +#ifdef DEBUG + errno = error; /* XXX */ + dlog("Returning error: %m"); +#endif /* DEBUG */ + XFREE(fname); + ereturn(error); + } + + /* + * If doing a delete then don't create again! + */ + switch (op) { + case VLOOK_DELETE: + ereturn(ENOENT); + + case VLOOK_CREATE: + break; + + default: + plog(XLOG_FATAL, "Unknown op to autofs_lookuppn: 0x%x", op); + ereturn(EINVAL); + } + + /* + * If the server is going down then just return, + * don't try to mount any more file systems + */ + if ((int) amd_state >= (int) Finishing) { +#ifdef DEBUG + dlog("not found - server going down anyway"); +#endif /* DEBUG */ + XFREE(fname); + ereturn(ENOENT); + } + + /* + * If we get there then this is a reference to an, + * as yet, unknown name so we need to search the mount + * map for it. + */ + if (mp->am_pref) { + sprintf(path_name, "%s%s", mp->am_pref, fname); + pfname = path_name; + } else { + pfname = fname; + } + + mf = mp->am_mnt; + +#ifdef DEBUG + dlog("will search map info in %s to find %s", mf->mf_info, pfname); +#endif /* DEBUG */ + /* + * Consult the oracle for some mount information. + * info is malloc'ed and belongs to this routine. + * It ends up being free'd in free_continuation(). + * + * Note that this may return -1 indicating that information + * is not yet available. + */ + error = mapc_search((mnt_map *) mf->mf_private, pfname, &info); + if (error) { + if (error > 0) + plog(XLOG_MAP, "No map entry for %s", pfname); + else + plog(XLOG_MAP, "Waiting on map entry for %s", pfname); + XFREE(fname); + ereturn(error); + } +#ifdef DEBUG + dlog("mount info is %s", info); +#endif /* DEBUG */ + + /* + * Split info into an argument vector. + * The vector is malloc'ed and belongs to + * this routine. It is free'd in free_continuation() + */ + xivec = ivec = strsplit(info, ' ', '\"'); + + /* + * Default error code... + */ + if (ap_hung) + error = EWOULDBLOCK; + else + error = ENOENT; + + /* + * Allocate a new map + */ + new_mp = exported_ap_alloc(); + if (new_mp == 0) { + XFREE(xivec); + XFREE(info); + XFREE(fname); + ereturn(ENOSPC); + } + if (mf->mf_auto) + auto_opts = mf->mf_auto; + else + auto_opts = ""; + + auto_opts = strdup(auto_opts); + +#ifdef DEBUG + dlog("searching for /defaults entry"); +#endif /* DEBUG */ + if (mapc_search((mnt_map *) mf->mf_private, "/defaults", &dflts) == 0) { + char *dfl; + char **rvec; +#ifdef DEBUG + dlog("/defaults gave %s", dflts); +#endif /* DEBUG */ + if (*dflts == '-') + dfl = dflts + 1; + else + dfl = dflts; + + /* + * Chop the defaults up + */ + rvec = strsplit(dfl, ' ', '\"'); + + if (gopt.flags & CFM_SELECTORS_IN_DEFAULTS) { + /* + * Pick whichever first entry matched the list of selectors. + * Strip the selectors from the string, and assign to dfl the + * rest of the string. + */ + if (rvec) { + am_opts ap; + am_ops *pt; + char **sp = rvec; + while (*sp) { /* loop until you find something, if any */ + memset((char *) &ap, 0, sizeof(am_opts)); + pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults", + mp->am_parent->am_mnt->mf_info); + free_opts(&ap); /* don't leak */ + if (pt == &amfs_error_ops) { + plog(XLOG_MAP, "did not match defaults for \"%s\"", *sp); + } else { + dfl = strip_selectors(*sp, "/defaults"); + plog(XLOG_MAP, "matched default selectors \"%s\"", dfl); + break; + } + ++sp; + } + } + } else { /* not selectors_in_defaults */ + /* + * Extract first value + */ + dfl = rvec[0]; + } + + /* + * If there were any values at all... + */ + if (dfl) { + /* + * Log error if there were other values + */ + if (!(gopt.flags & CFM_SELECTORS_IN_DEFAULTS) && rvec[1]) { +# ifdef DEBUG + dlog("/defaults chopped into %s", dfl); +# endif /* DEBUG */ + plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info); + } + + /* + * Prepend to existing defaults if they exist, + * otherwise just use these defaults. + */ + if (*auto_opts && *dfl) { + char *nopts = (char *) xmalloc(strlen(auto_opts) + strlen(dfl) + 2); + sprintf(nopts, "%s;%s", dfl, auto_opts); + XFREE(auto_opts); + auto_opts = nopts; + } else if (*dfl) { + auto_opts = strealloc(auto_opts, dfl); + } + } + XFREE(dflts); + /* + * Don't need info vector any more + */ + XFREE(rvec); + } + + /* + * Fill it in + */ + init_map(new_mp, fname); + + /* + * Turn on autofs flag if needed. + */ + if (mp->am_flags & AMF_AUTOFS) { + new_mp->am_flags |= AMF_AUTOFS; + } + + /* + * Put it in the table + */ + insert_am(new_mp, mp); + + /* + * Fill in some other fields, + * path and mount point. + * + * bugfix: do not prepend old am_path if direct map + * <wls@astro.umd.edu> William Sebok + */ + + strcpy(apath, fname); + strcat(apath, " "); + new_mp->am_path = str3cat(new_mp->am_path, + mf->mf_ops == &amfs_direct_ops ? "" : mp->am_path, + *fname == '/' ? "" : "/", + apath); + +#ifdef DEBUG + dlog("setting path to \"%s\"", new_mp->am_path); +#endif /* DEBUG */ + + /* + * Take private copy of pfname + */ + pfname = strdup(pfname); + + /* + * Construct a continuation + */ + cp = ALLOC(struct continuation); + cp->callout = 0; + cp->mp = new_mp; + cp->xivec = xivec; + cp->ivec = ivec; + cp->info = info; + cp->key = pfname; + cp->auto_opts = auto_opts; + cp->retry = FALSE; + cp->tried = FALSE; + cp->start = clocktime(); + cp->def_opts = strdup(auto_opts); + memset((voidp) &cp->fs_opts, 0, sizeof(cp->fs_opts)); + + /* + * Try and mount the file system. If this succeeds immediately (possible + * for a ufs file system) then return the attributes, otherwise just + * return an error. + */ + error = autofs_bgmount(cp, error); + reschedule_timeout_mp(); + if (!error) { + XFREE(fname); + return new_mp; + } + + /* + * Code for quick reply. If nfs_program_2_transp is set, then + * its the transp that's been passed down from nfs_program_2(). + * If new_mp->am_transp is not already set, set it by copying in + * nfs_program_2_transp. Once am_transp is set, quick_reply() can + * use it to send a reply to the client that requested this mount. + */ + if (nfs_program_2_transp && !new_mp->am_transp) { + new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT)); + *(new_mp->am_transp) = *nfs_program_2_transp; + } + if (error && (new_mp->am_mnt->mf_ops == &amfs_error_ops)) + new_mp->am_error = error; + + assign_error_mntfs(new_mp); + + XFREE(fname); + + ereturn(error); +} +#endif /* HAVE_FS_AUTOFS */ diff --git a/amd/ops_cachefs.c b/amd/ops_cachefs.c new file mode 100644 index 000000000000..d3e303bc2bf9 --- /dev/null +++ b/amd/ops_cachefs.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/ops_cachefs.c + * + */ + +/* + * Caching filesystem (Solaris 2.x) + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* forward declarations */ +static char *cachefs_match(am_opts *fo); +static int cachefs_init(mntfs *mf); +static int cachefs_mount(am_node *am, mntfs *mf); +static int cachefs_umount(am_node *am, mntfs *mf); + + +/* + * Ops structure + */ +am_ops cachefs_ops = +{ + "cachefs", + cachefs_match, + cachefs_init, + cachefs_mount, + cachefs_umount, + amfs_error_lookup_child, + amfs_error_mount_child, + amfs_error_readdir, + 0, /* cachefs_readlink */ + 0, /* cachefs_mounted */ + 0, /* cachefs_umounted */ + amfs_generic_find_srvr, + 0, /* cachefs_get_wchan */ + FS_MKMNT | FS_NOTIMEOUT | FS_UBACKGROUND | FS_AMQINFO, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_CACHEFS_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ +}; + + +/* + * Check that f/s has all needed fields. + * Returns: matched string if found, NULL otherwise. + */ +static char * +cachefs_match(am_opts *fo) +{ + /* sanity check */ + if (!fo->opt_rfs || !fo->opt_fs || !fo->opt_cachedir) { + plog(XLOG_USER, "cachefs: must specify cachedir, rfs, and fs"); + return NULL; + } + + dlog("CACHEFS: using cache directory \"%s\"", fo->opt_cachedir); + + /* determine magic cookie to put in mtab */ + return strdup(fo->opt_cachedir); +} + + +/* + * Initialize. + * Returns: 0 if OK, non-zero (errno) if failed. + */ +static int +cachefs_init(mntfs *mf) +{ + /* + * Save cache directory name + */ + if (!mf->mf_private) { + mf->mf_private = (voidp) strdup(mf->mf_fo->opt_cachedir); + mf->mf_prfree = (void (*)(voidp)) free; + } + + return 0; +} + + +/* + * mntpt is the mount point ($fs) [XXX: was 'dir'] + * backdir is the mounted pathname ($rfs) [XXX: was 'fs_name'] + * cachedir is the cache directory ($cachedir) + */ +static int +mount_cachefs(char *mntdir, char *backdir, char *cachedir, + char *opts, int on_autofs) +{ + cachefs_args_t ca; + mntent_t mnt; + int flags; + char *cp; + MTYPE_TYPE type = MOUNT_TYPE_CACHEFS; /* F/S mount type */ + + memset((voidp) &ca, 0, sizeof(ca)); /* Paranoid */ + + /* + * Fill in the mount structure + */ + memset((voidp) &mnt, 0, sizeof(mnt)); + mnt.mnt_dir = mntdir; + mnt.mnt_fsname = backdir; + mnt.mnt_type = MNTTAB_TYPE_CACHEFS; + mnt.mnt_opts = opts; + + flags = compute_mount_flags(&mnt); +#ifdef HAVE_FS_AUTOFS + if (on_autofs) + flags |= autofs_compute_mount_flags(&mnt); +#endif /* HAVE_FS_AUTOFS */ + + /* Fill in cachefs mount arguments */ + + /* + * XXX: Caveats + * (1) cache directory is NOT checked for sanity beforehand, nor is it + * purged. Maybe it should be purged first? + * (2) cache directory is NOT locked. Should we? + */ + + /* mount flags */ + ca.cfs_options.opt_flags = CFS_WRITE_AROUND | CFS_ACCESS_BACKFS; + /* cache population size */ + ca.cfs_options.opt_popsize = DEF_POP_SIZE; /* default: 64K */ + /* filegrp size */ + ca.cfs_options.opt_fgsize = DEF_FILEGRP_SIZE; /* default: 256 */ + + /* CFS ID for file system (must be unique) */ + ca.cfs_fsid = cachedir; + + /* CFS fscdir name */ + memset(ca.cfs_cacheid, 0, sizeof(ca.cfs_cacheid)); + /* + * Append cacheid and mountpoint. + * sizeof(cfs_cacheid) should be C_MAX_MOUNT_FSCDIRNAME as per + * <sys/fs/cachefs_fs.h> (checked on Solaris 8). + */ + xsnprintf(ca.cfs_cacheid, sizeof(ca.cfs_cacheid), + "%s:%s", ca.cfs_fsid, mntdir); + /* convert '/' to '_' (Solaris does that...) */ + cp = ca.cfs_cacheid; + while ((cp = strpbrk(cp, "/")) != NULL) + *cp = '_'; + + /* path for this cache dir */ + ca.cfs_cachedir = cachedir; + + /* back filesystem dir */ + ca.cfs_backfs = backdir; + + /* same as nfs values (XXX: need to handle these options) */ + ca.cfs_acregmin = 0; + ca.cfs_acregmax = 0; + ca.cfs_acdirmin = 0; + ca.cfs_acdirmax = 0; + + /* + * Call generic mount routine + */ + return mount_fs(&mnt, flags, (caddr_t) &ca, 0, type, 0, NULL, mnttab_file_name, on_autofs); +} + + +static int +cachefs_mount(am_node *am, mntfs *mf) +{ + int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; + int error; + + error = mount_cachefs(mf->mf_mount, + mf->mf_fo->opt_rfs, + mf->mf_fo->opt_cachedir, + mf->mf_mopts, + on_autofs); + if (error) { + errno = error; + /* according to Solaris, if errno==ESRCH, "options to not match" */ + if (error == ESRCH) + plog(XLOG_ERROR, "mount_cachefs: options to no match: %m"); + else + plog(XLOG_ERROR, "mount_cachefs: %m"); + return error; + } + + return 0; +} + + +static int +cachefs_umount(am_node *am, mntfs *mf) +{ + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + int error; + + error = UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); + + /* + * In the case of cachefs, we must fsck the cache directory. Otherwise, + * it will remain inconsistent, and the next cachefs mount will fail + * with the error "no space left on device" (ENOSPC). + * + * XXX: this is hacky! use fork/exec/wait instead... + */ + if (!error) { + char *cachedir = NULL; + char cmd[128]; + + cachedir = (char *) mf->mf_private; + plog(XLOG_INFO, "running fsck on cache directory \"%s\"", cachedir); + xsnprintf(cmd, sizeof(cmd), "fsck -F cachefs %s", cachedir); + system(cmd); + } + + return error; +} diff --git a/amd/ops_cdfs.c b/amd/ops_cdfs.c new file mode 100644 index 000000000000..40355ac5c078 --- /dev/null +++ b/amd/ops_cdfs.c @@ -0,0 +1,228 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/ops_cdfs.c + * + */ + +/* + * High Sierra (CD-ROM) file system + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* forward declarations */ +static char *cdfs_match(am_opts *fo); +static int cdfs_mount(am_node *am, mntfs *mf); +static int cdfs_umount(am_node *am, mntfs *mf); + +/* + * Ops structure + */ +am_ops cdfs_ops = +{ + "cdfs", + cdfs_match, + 0, /* cdfs_init */ + cdfs_mount, + cdfs_umount, + amfs_error_lookup_child, + amfs_error_mount_child, + amfs_error_readdir, + 0, /* cdfs_readlink */ + 0, /* cdfs_mounted */ + 0, /* cdfs_umounted */ + amfs_generic_find_srvr, + 0, /* cdfs_get_wchan */ + FS_MKMNT | FS_UBACKGROUND | FS_AMQINFO, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_CDFS_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ +}; + + +/* + * CDFS needs remote filesystem. + */ +static char * +cdfs_match(am_opts *fo) +{ + if (!fo->opt_dev) { + plog(XLOG_USER, "cdfs: no source device specified"); + return 0; + } + dlog("CDFS: mounting device \"%s\" on \"%s\"", + fo->opt_dev, fo->opt_fs); + + /* + * Determine magic cookie to put in mtab + */ + return strdup(fo->opt_dev); +} + + +static int +mount_cdfs(char *mntdir, char *fs_name, char *opts, int on_autofs) +{ + cdfs_args_t cdfs_args; + mntent_t mnt; + int genflags, cdfs_flags, retval; + + /* + * Figure out the name of the file system type. + */ + MTYPE_TYPE type = MOUNT_TYPE_CDFS; + + memset((voidp) &cdfs_args, 0, sizeof(cdfs_args)); /* Paranoid */ + cdfs_flags = 0; + + /* + * Fill in the mount structure + */ + memset((voidp) &mnt, 0, sizeof(mnt)); + mnt.mnt_dir = mntdir; + mnt.mnt_fsname = fs_name; + mnt.mnt_type = MNTTAB_TYPE_CDFS; + mnt.mnt_opts = opts; + +#if defined(MNT2_CDFS_OPT_DEFPERM) && defined(MNTTAB_OPT_DEFPERM) + if (amu_hasmntopt(&mnt, MNTTAB_OPT_DEFPERM)) +# ifdef MNT2_CDFS_OPT_DEFPERM + cdfs_flags |= MNT2_CDFS_OPT_DEFPERM; +# else /* not MNT2_CDFS_OPT_DEFPERM */ + cdfs_flags &= ~MNT2_CDFS_OPT_NODEFPERM; +# endif /* not MNT2_CDFS_OPT_DEFPERM */ +#endif /* defined(MNT2_CDFS_OPT_DEFPERM) && defined(MNTTAB_OPT_DEFPERM) */ + +#if defined(MNT2_CDFS_OPT_NODEFPERM) && defined(MNTTAB_OPT_NODEFPERM) + if (amu_hasmntopt(&mnt, MNTTAB_OPT_NODEFPERM)) + cdfs_flags |= MNT2_CDFS_OPT_NODEFPERM; +#endif /* MNTTAB_OPT_NODEFPERM */ + +#if defined(MNT2_CDFS_OPT_NOVERSION) && defined(MNTTAB_OPT_NOVERSION) + if (amu_hasmntopt(&mnt, MNTTAB_OPT_NOVERSION)) + cdfs_flags |= MNT2_CDFS_OPT_NOVERSION; +#endif /* defined(MNT2_CDFS_OPT_NOVERSION) && defined(MNTTAB_OPT_NOVERSION) */ + +#if defined(MNT2_CDFS_OPT_RRIP) && defined(MNTTAB_OPT_RRIP) + if (amu_hasmntopt(&mnt, MNTTAB_OPT_RRIP)) + cdfs_flags |= MNT2_CDFS_OPT_RRIP; +#endif /* defined(MNT2_CDFS_OPT_RRIP) && defined(MNTTAB_OPT_RRIP) */ +#if defined(MNT2_CDFS_OPT_NORRIP) && defined(MNTTAB_OPT_NORRIP) + if (amu_hasmntopt(&mnt, MNTTAB_OPT_NORRIP)) + cdfs_flags |= MNT2_CDFS_OPT_NORRIP; +#endif /* defined(MNT2_CDFS_OPT_NORRIP) && defined(MNTTAB_OPT_NORRIP) */ + +#if defined(MNT2_CDFS_OPT_GENS) && defined(MNTTAB_OPT_GENS) + if (amu_hasmntopt(&mnt, MNTTAB_OPT_GENS)) + cdfs_flags |= MNT2_CDFS_OPT_GENS; +#endif /* defined(MNT2_CDFS_OPT_GENS) && defined(MNTTAB_OPT_GENS) */ +#if defined(MNT2_CDFS_OPT_EXTATT) && defined(MNTTAB_OPT_EXTATT) + if (amu_hasmntopt(&mnt, MNTTAB_OPT_EXTATT)) + cdfs_flags |= MNT2_CDFS_OPT_EXTATT; +#endif /* defined(MNT2_CDFS_OPT_EXTATT) && defined(MNTTAB_OPT_EXTATT) */ + + genflags = compute_mount_flags(&mnt); +#ifdef HAVE_FS_AUTOFS + if (on_autofs) + genflags |= autofs_compute_mount_flags(&mnt); +#endif /* HAVE_FS_AUTOFS */ + +#ifdef HAVE_CDFS_ARGS_T_FLAGS + cdfs_args.flags = cdfs_flags; +#endif /* HAVE_CDFS_ARGS_T_FLAGS */ + +#ifdef HAVE_CDFS_ARGS_T_ISO_FLAGS + cdfs_args.iso_flags = genflags | cdfs_flags; +#endif /* HAVE_CDFS_ARGS_T_ISO_FLAGS */ + +#ifdef HAVE_CDFS_ARGS_T_ISO_PGTHRESH + cdfs_args.iso_pgthresh = hasmntval(&mnt, MNTTAB_OPT_PGTHRESH); +#endif /* HAVE_CDFS_ARGS_T_ISO_PGTHRESH */ + +#ifdef HAVE_CDFS_ARGS_T_NORRIP + /* XXX: need to provide norrip mount opt */ + cdfs_args.norrip = 0; /* use Rock-Ridge Protocol extensions */ +#endif /* HAVE_CDFS_ARGS_T_NORRIP */ + +#ifdef HAVE_CDFS_ARGS_T_SSECTOR + /* XXX: need to provide ssector mount option */ + cdfs_args.ssector = 0; /* use 1st session on disk */ +#endif /* HAVE_CDFS_ARGS_T_SSECTOR */ + +#ifdef HAVE_CDFS_ARGS_T_FSPEC + cdfs_args.fspec = fs_name; +#endif /* HAVE_CDFS_ARGS_T_FSPEC */ + + /* + * Call generic mount routine + */ + retval = mount_fs(&mnt, genflags, (caddr_t) &cdfs_args, 0, type, 0, NULL, mnttab_file_name, on_autofs); + + return retval; +} + + +static int +cdfs_mount(am_node *am, mntfs *mf) +{ + int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; + int error; + + error = mount_cdfs(mf->mf_mount, mf->mf_info, mf->mf_mopts, on_autofs); + if (error) { + errno = error; + plog(XLOG_ERROR, "mount_cdfs: %m"); + return error; + } + return 0; +} + + +static int +cdfs_umount(am_node *am, mntfs *mf) +{ + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + + return UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); +} diff --git a/amd/ops_efs.c b/amd/ops_efs.c new file mode 100644 index 000000000000..047fe1e4690c --- /dev/null +++ b/amd/ops_efs.c @@ -0,0 +1,172 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/ops_efs.c + * + */ + +/* + * Irix UN*X file system: EFS (Extent File System) + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* forward declarations */ +static char *efs_match(am_opts *fo); +static int efs_mount(am_node *am, mntfs *mf); +static int efs_umount(am_node *am, mntfs *mf); + +/* + * Ops structure + */ +am_ops efs_ops = +{ + "efs", + efs_match, + 0, /* efs_init */ + efs_mount, + efs_umount, + amfs_error_lookup_child, + amfs_error_mount_child, + amfs_error_readdir, + 0, /* efs_readlink */ + 0, /* efs_mounted */ + 0, /* efs_umounted */ + amfs_generic_find_srvr, + 0, /* efs_get_wchan */ + FS_MKMNT | FS_NOTIMEOUT | FS_UBACKGROUND | FS_AMQINFO, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_EFS_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ +}; + + +/* + * EFS needs local filesystem and device. + */ +static char * +efs_match(am_opts *fo) +{ + + if (!fo->opt_dev) { + plog(XLOG_USER, "efs: no device specified"); + return 0; + } + + dlog("EFS: mounting device \"%s\" on \"%s\"", fo->opt_dev, fo->opt_fs); + + /* + * Determine magic cookie to put in mtab + */ + return strdup(fo->opt_dev); +} + + +static int +mount_efs(char *mntdir, char *fs_name, char *opts, int on_autofs) +{ + efs_args_t efs_args; + mntent_t mnt; + int flags; + + /* + * Figure out the name of the file system type. + */ + MTYPE_TYPE type = MOUNT_TYPE_EFS; + + memset((voidp) &efs_args, 0, sizeof(efs_args)); /* Paranoid */ + + /* + * Fill in the mount structure + */ + memset((voidp) &mnt, 0, sizeof(mnt)); + mnt.mnt_dir = mntdir; + mnt.mnt_fsname = fs_name; + mnt.mnt_type = MNTTAB_TYPE_EFS; + mnt.mnt_opts = opts; + + flags = compute_mount_flags(&mnt); +#ifdef HAVE_FS_AUTOFS + if (on_autofs) + flags |= autofs_compute_mount_flags(&mnt); +#endif /* HAVE_FS_AUTOFS */ + +#ifdef HAVE_EFS_ARGS_T_FLAGS + efs_args.flags = 0; /* XXX: fix this to correct flags */ +#endif /* HAVE_EFS_ARGS_T_FLAGS */ +#ifdef HAVE_EFS_ARGS_T_FSPEC + efs_args.fspec = fs_name; +#endif /* HAVE_EFS_ARGS_T_FSPEC */ + + /* + * Call generic mount routine + */ + return mount_fs(&mnt, flags, (caddr_t) &efs_args, 0, type, 0, NULL, mnttab_file_name, on_autofs); +} + + +static int +efs_mount(am_node *am, mntfs *mf) +{ + int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; + int error; + + error = mount_efs(mf->mf_mount, mf->mf_info, mf->mf_mopts, on_autofs); + if (error) { + errno = error; + plog(XLOG_ERROR, "mount_efs: %m"); + return error; + } + + return 0; +} + + +static int +efs_umount(am_node *am, mntfs *mf) +{ + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + + return UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); +} + diff --git a/amd/ops_lofs.c b/amd/ops_lofs.c new file mode 100644 index 000000000000..26fdc9f9b626 --- /dev/null +++ b/amd/ops_lofs.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/ops_lofs.c + * + */ + +/* + * Loopback file system + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* forward definitions */ +static char *lofs_match(am_opts *fo); +static int lofs_mount(am_node *am, mntfs *mf); +static int lofs_umount(am_node *am, mntfs *mf); + +/* + * Ops structure + */ +am_ops lofs_ops = +{ + "lofs", + lofs_match, + 0, /* lofs_init */ + lofs_mount, + lofs_umount, + amfs_error_lookup_child, + amfs_error_mount_child, + amfs_error_readdir, + 0, /* lofs_readlink */ + 0, /* lofs_mounted */ + 0, /* lofs_umounted */ + amfs_generic_find_srvr, + 0, /* lofs_get_wchan */ + FS_MKMNT | FS_NOTIMEOUT | FS_UBACKGROUND | FS_AMQINFO, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_LOFS_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ +}; + + +/* + * LOFS needs remote filesystem. + */ +static char * +lofs_match(am_opts *fo) +{ + if (!fo->opt_rfs) { + plog(XLOG_USER, "lofs: no source filesystem specified"); + return 0; + } + dlog("LOFS: mounting fs \"%s\" on \"%s\"", + fo->opt_rfs, fo->opt_fs); + + /* + * Determine magic cookie to put in mtab + */ + return strdup(fo->opt_rfs); +} + + +int +mount_lofs(char *mntdir, char *fs_name, char *opts, int on_autofs) +{ + mntent_t mnt; + int flags; + + /* + * Figure out the name of the file system type. + */ + MTYPE_TYPE type = MOUNT_TYPE_LOFS; + + /* + * Fill in the mount structure + */ + memset((voidp) &mnt, 0, sizeof(mnt)); + mnt.mnt_dir = mntdir; + mnt.mnt_fsname = fs_name; + mnt.mnt_type = MNTTAB_TYPE_LOFS; + mnt.mnt_opts = opts; + + flags = compute_mount_flags(&mnt); +#ifdef HAVE_FS_AUTOFS + if (on_autofs) + flags |= autofs_compute_mount_flags(&mnt); +#endif /* HAVE_FS_AUTOFS */ + + /* + * Call generic mount routine + */ + return mount_fs(&mnt, flags, NULL, 0, type, 0, NULL, mnttab_file_name, on_autofs); +} + + +static int +lofs_mount(am_node *am, mntfs *mf) +{ + int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; + int error; + + error = mount_lofs(mf->mf_mount, mf->mf_info, mf->mf_mopts, on_autofs); + if (error) { + errno = error; + plog(XLOG_ERROR, "mount_lofs: %m"); + return error; + } + return 0; +} + + +static int +lofs_umount(am_node *am, mntfs *mf) +{ + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + + return UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); +} diff --git a/amd/ops_mfs.c b/amd/ops_mfs.c new file mode 100644 index 000000000000..ccaa49c569cd --- /dev/null +++ b/amd/ops_mfs.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/ops_mfs.c + * + */ + +/* + * Memory file system (RAM filesystem) + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* FEEL FREE TO IMPLEMENT THIS... :-) */ diff --git a/amd/ops_nfs.c b/amd/ops_nfs.c new file mode 100644 index 000000000000..a6a85858d484 --- /dev/null +++ b/amd/ops_nfs.c @@ -0,0 +1,1042 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/ops_nfs.c + * + */ + +/* + * Network file system + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* + * Convert from nfsstat to UN*X error code + */ +#define unx_error(e) ((int)(e)) + +/* + * FH_TTL is the time a file handle will remain in the cache since + * last being used. If the file handle becomes invalid, then it + * will be flushed anyway. + */ +#define FH_TTL (5 * 60) /* five minutes */ +#define FH_TTL_ERROR (30) /* 30 seconds */ +#define FHID_ALLOC() (++fh_id) + +/* + * The NFS layer maintains a cache of file handles. + * This is *fundamental* to the implementation and + * also allows quick remounting when a filesystem + * is accessed soon after timing out. + * + * The NFS server layer knows to flush this cache + * when a server goes down so avoiding stale handles. + * + * Each cache entry keeps a hard reference to + * the corresponding server. This ensures that + * the server keepalive information is maintained. + * + * The copy of the sockaddr_in here is taken so + * that the port can be twiddled to talk to mountd + * instead of portmap or the NFS server as used + * elsewhere. + * The port# is flushed if a server goes down. + * The IP address is never flushed - we assume + * that the address of a mounted machine never + * changes. If it does, then you have other + * problems... + */ +typedef struct fh_cache fh_cache; +struct fh_cache { + qelem fh_q; /* List header */ + wchan_t fh_wchan; /* Wait channel */ + int fh_error; /* Valid data? */ + int fh_id; /* Unique id */ + int fh_cid; /* Callout id */ + u_long fh_nfs_version; /* highest NFS version on host */ + am_nfs_handle_t fh_nfs_handle; /* Handle on filesystem */ + int fh_status; /* Status of last rpc */ + struct sockaddr_in fh_sin; /* Address of mountd */ + fserver *fh_fs; /* Server holding filesystem */ + char *fh_path; /* Filesystem on host */ +}; + +/* forward definitions */ +static int nfs_init(mntfs *mf); +static char *nfs_match(am_opts *fo); +static int nfs_mount(am_node *am, mntfs *mf); +static int nfs_umount(am_node *am, mntfs *mf); +static void nfs_umounted(mntfs *mf); +static int call_mountd(fh_cache *fp, u_long proc, fwd_fun f, wchan_t wchan); +static int webnfs_lookup(fh_cache *fp, fwd_fun f, wchan_t wchan); +static int fh_id = 0; + +/* globals */ +AUTH *nfs_auth; +qelem fh_head = {&fh_head, &fh_head}; + +/* + * Network file system operations + */ +am_ops nfs_ops = +{ + "nfs", + nfs_match, + nfs_init, + nfs_mount, + nfs_umount, + amfs_error_lookup_child, + amfs_error_mount_child, + amfs_error_readdir, + 0, /* nfs_readlink */ + 0, /* nfs_mounted */ + nfs_umounted, + find_nfs_srvr, + 0, /* nfs_get_wchan */ + FS_MKMNT | FS_BACKGROUND | FS_AMQINFO, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_NFS_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ +}; + + +static fh_cache * +find_nfs_fhandle_cache(opaque_t arg, int done) +{ + fh_cache *fp, *fp2 = 0; + int id = (long) arg; /* for 64-bit archs */ + + ITER(fp, fh_cache, &fh_head) { + if (fp->fh_id == id) { + fp2 = fp; + break; + } + } + + if (fp2) { + dlog("fh cache gives fp %#lx, fs %s", (unsigned long) fp2, fp2->fh_path); + } else { + dlog("fh cache search failed"); + } + + if (fp2 && !done) { + fp2->fh_error = ETIMEDOUT; + return 0; + } + + return fp2; +} + + +/* + * Called when a filehandle appears via the mount protocol + */ +static void +got_nfs_fh_mount(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, opaque_t arg, int done) +{ + fh_cache *fp; + struct fhstatus res; +#ifdef HAVE_FS_NFS3 + struct am_mountres3 res3; +#endif /* HAVE_FS_NFS3 */ + + fp = find_nfs_fhandle_cache(arg, done); + if (!fp) + return; + + /* + * retrieve the correct RPC reply for the file handle, based on the + * NFS protocol version. + */ +#ifdef HAVE_FS_NFS3 + if (fp->fh_nfs_version == NFS_VERSION3) { + memset(&res3, 0, sizeof(res3)); + fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &res3, + (XDRPROC_T_TYPE) xdr_am_mountres3); + fp->fh_status = unx_error(res3.fhs_status); + memset(&fp->fh_nfs_handle.v3, 0, sizeof(am_nfs_fh3)); + fp->fh_nfs_handle.v3.am_fh3_length = res3.mountres3_u.mountinfo.fhandle.fhandle3_len; + memmove(fp->fh_nfs_handle.v3.am_fh3_data, + res3.mountres3_u.mountinfo.fhandle.fhandle3_val, + fp->fh_nfs_handle.v3.am_fh3_length); + } else { +#endif /* HAVE_FS_NFS3 */ + memset(&res, 0, sizeof(res)); + fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &res, + (XDRPROC_T_TYPE) xdr_fhstatus); + fp->fh_status = unx_error(res.fhs_status); + memmove(&fp->fh_nfs_handle.v2, &res.fhs_fh, NFS_FHSIZE); +#ifdef HAVE_FS_NFS3 + } +#endif /* HAVE_FS_NFS3 */ + + if (!fp->fh_error) { + dlog("got filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path); + } else { + plog(XLOG_USER, "filehandle denied for %s:%s", fp->fh_fs->fs_host, fp->fh_path); + /* + * Force the error to be EACCES. It's debatable whether it should be + * ENOENT instead, but the server really doesn't give us any clues, and + * EACCES is more in line with the "filehandle denied" message. + */ + fp->fh_error = EACCES; + } + + /* + * Wakeup anything sleeping on this filehandle + */ + if (fp->fh_wchan) { + dlog("Calling wakeup on %#lx", (unsigned long) fp->fh_wchan); + wakeup(fp->fh_wchan); + } +} + + +/* + * Called when a filehandle appears via WebNFS + */ +static void +got_nfs_fh_webnfs(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, opaque_t arg, int done) +{ + fh_cache *fp; + nfsdiropres res; +#ifdef HAVE_FS_NFS3 + am_LOOKUP3res res3; +#endif /* HAVE_FS_NFS3 */ + + fp = find_nfs_fhandle_cache(arg, done); + if (!fp) + return; + + /* + * retrieve the correct RPC reply for the file handle, based on the + * NFS protocol version. + */ +#ifdef HAVE_FS_NFS3 + if (fp->fh_nfs_version == NFS_VERSION3) { + memset(&res3, 0, sizeof(res3)); + fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &res3, + (XDRPROC_T_TYPE) xdr_am_LOOKUP3res); + fp->fh_status = unx_error(res3.status); + memset(&fp->fh_nfs_handle.v3, 0, sizeof(am_nfs_fh3)); + fp->fh_nfs_handle.v3.am_fh3_length = res3.res_u.ok.object.am_fh3_length; + memmove(fp->fh_nfs_handle.v3.am_fh3_data, + res3.res_u.ok.object.am_fh3_data, + fp->fh_nfs_handle.v3.am_fh3_length); + } else { +#endif /* HAVE_FS_NFS3 */ + memset(&res, 0, sizeof(res)); + fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &res, + (XDRPROC_T_TYPE) xdr_diropres); + fp->fh_status = unx_error(res.dr_status); + memmove(&fp->fh_nfs_handle.v2, &res.dr_u.dr_drok_u.drok_fhandle, NFS_FHSIZE); +#ifdef HAVE_FS_NFS3 + } +#endif /* HAVE_FS_NFS3 */ + + if (!fp->fh_error) { + dlog("got filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path); + } else { + plog(XLOG_USER, "filehandle denied for %s:%s", fp->fh_fs->fs_host, fp->fh_path); + /* + * Force the error to be EACCES. It's debatable whether it should be + * ENOENT instead, but the server really doesn't give us any clues, and + * EACCES is more in line with the "filehandle denied" message. + */ + fp->fh_error = EACCES; + } + + /* + * Wakeup anything sleeping on this filehandle + */ + if (fp->fh_wchan) { + dlog("Calling wakeup on %#lx", (unsigned long) fp->fh_wchan); + wakeup(fp->fh_wchan); + } +} + + +void +flush_nfs_fhandle_cache(fserver *fs) +{ + fh_cache *fp; + + ITER(fp, fh_cache, &fh_head) { + if (fp->fh_fs == fs || fs == NULL) { + /* + * Only invalidate port info for non-WebNFS servers + */ + if (!(fp->fh_fs->fs_flags & FSF_WEBNFS)) + fp->fh_sin.sin_port = (u_short) 0; + fp->fh_error = -1; + } + } +} + + +static void +discard_fh(opaque_t arg) +{ + fh_cache *fp = (fh_cache *) arg; + + rem_que(&fp->fh_q); + if (fp->fh_fs) { + dlog("Discarding filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path); + free_srvr(fp->fh_fs); + } + if (fp->fh_path) + XFREE(fp->fh_path); + XFREE(fp); +} + + +/* + * Determine the file handle for a node + */ +static int +prime_nfs_fhandle_cache(char *path, fserver *fs, am_nfs_handle_t *fhbuf, mntfs *mf) +{ + fh_cache *fp, *fp_save = 0; + int error; + int reuse_id = FALSE; + + dlog("Searching cache for %s:%s", fs->fs_host, path); + + /* + * First search the cache + */ + ITER(fp, fh_cache, &fh_head) { + if (fs != fp->fh_fs || !STREQ(path, fp->fh_path)) + continue; /* skip to next ITER item */ + /* else we got a match */ + switch (fp->fh_error) { + case 0: + plog(XLOG_INFO, "prime_nfs_fhandle_cache: NFS version %d", (int) fp->fh_nfs_version); + + error = fp->fh_error = fp->fh_status; + + if (error == 0) { + if (mf->mf_flags & MFF_NFS_SCALEDOWN) { + fp_save = fp; + /* XXX: why reuse the ID? */ + reuse_id = TRUE; + break; + } + + if (fhbuf) { +#ifdef HAVE_FS_NFS3 + if (fp->fh_nfs_version == NFS_VERSION3) { + memmove((voidp) &(fhbuf->v3), (voidp) &(fp->fh_nfs_handle.v3), + sizeof(fp->fh_nfs_handle.v3)); + } else +#endif /* HAVE_FS_NFS3 */ + { + memmove((voidp) &(fhbuf->v2), (voidp) &(fp->fh_nfs_handle.v2), + sizeof(fp->fh_nfs_handle.v2)); + } + } + if (fp->fh_cid) + untimeout(fp->fh_cid); + fp->fh_cid = timeout(FH_TTL, discard_fh, (opaque_t) fp); + } else if (error == EACCES) { + /* + * Now decode the file handle return code. + */ + plog(XLOG_INFO, "Filehandle denied for \"%s:%s\"", + fs->fs_host, path); + } else { + errno = error; /* XXX */ + plog(XLOG_INFO, "Filehandle error for \"%s:%s\": %m", + fs->fs_host, path); + } + + /* + * The error was returned from the remote mount daemon. + * Policy: this error will be cached for now... + */ + return error; + + case -1: + /* + * Still thinking about it, but we can re-use. + */ + fp_save = fp; + reuse_id = TRUE; + break; + + default: + /* + * Return the error. + * Policy: make sure we recompute if required again + * in case this was caused by a network failure. + * This can thrash mountd's though... If you find + * your mountd going slowly then: + * 1. Add a fork() loop to main. + * 2. Remove the call to innetgr() and don't use + * netgroups, especially if you don't use YP. + */ + error = fp->fh_error; + fp->fh_error = -1; + return error; + } /* end of switch statement */ + } /* end of ITER loop */ + + /* + * Not in cache + */ + if (fp_save) { + fp = fp_save; + /* + * Re-use existing slot + */ + untimeout(fp->fh_cid); + free_srvr(fp->fh_fs); + XFREE(fp->fh_path); + } else { + fp = ALLOC(struct fh_cache); + memset((voidp) fp, 0, sizeof(struct fh_cache)); + ins_que(&fp->fh_q, &fh_head); + } + if (!reuse_id) + fp->fh_id = FHID_ALLOC(); + fp->fh_wchan = get_mntfs_wchan(mf); + fp->fh_error = -1; + fp->fh_cid = timeout(FH_TTL, discard_fh, (opaque_t) fp); + + /* + * If fs->fs_ip is null, remote server is probably down. + */ + if (!fs->fs_ip) { + /* Mark the fileserver down and invalid again */ + fs->fs_flags &= ~FSF_VALID; + fs->fs_flags |= FSF_DOWN; + error = AM_ERRNO_HOST_DOWN; + return error; + } + + /* + * Either fp has been freshly allocated or the address has changed. + * Initialize address and nfs version. Don't try to re-use the port + * information unless using WebNFS where the port is fixed either by + * the spec or the "port" mount option. + */ + if (fp->fh_sin.sin_addr.s_addr != fs->fs_ip->sin_addr.s_addr) { + fp->fh_sin = *fs->fs_ip; + if (!(mf->mf_flags & MFF_WEBNFS)) + fp->fh_sin.sin_port = 0; + fp->fh_nfs_version = fs->fs_version; + } + + fp->fh_fs = dup_srvr(fs); + fp->fh_path = strdup(path); + + if (mf->mf_flags & MFF_WEBNFS) + error = webnfs_lookup(fp, got_nfs_fh_webnfs, get_mntfs_wchan(mf)); + else + error = call_mountd(fp, MOUNTPROC_MNT, got_nfs_fh_mount, get_mntfs_wchan(mf)); + if (error) { + /* + * Local error - cache for a short period + * just to prevent thrashing. + */ + untimeout(fp->fh_cid); + fp->fh_cid = timeout(error < 0 ? 2 * ALLOWED_MOUNT_TIME : FH_TTL_ERROR, + discard_fh, (opaque_t) fp); + fp->fh_error = error; + } else { + error = fp->fh_error; + } + + return error; +} + + +int +make_nfs_auth(void) +{ + AUTH_CREATE_GIDLIST_TYPE group_wheel = 0; + + /* Some NFS mounts (particularly cross-domain) require FQDNs to succeed */ + +#ifdef HAVE_TRANSPORT_TYPE_TLI + if (gopt.flags & CFM_FULLY_QUALIFIED_HOSTS) { + plog(XLOG_INFO, "Using NFS auth for FQHN \"%s\"", hostd); + nfs_auth = authsys_create(hostd, 0, 0, 1, &group_wheel); + } else { + nfs_auth = authsys_create_default(); + } +#else /* not HAVE_TRANSPORT_TYPE_TLI */ + if (gopt.flags & CFM_FULLY_QUALIFIED_HOSTS) { + plog(XLOG_INFO, "Using NFS auth for FQHN \"%s\"", hostd); + nfs_auth = authunix_create(hostd, 0, 0, 1, &group_wheel); + } else { + nfs_auth = authunix_create_default(); + } +#endif /* not HAVE_TRANSPORT_TYPE_TLI */ + + if (!nfs_auth) + return ENOBUFS; + + return 0; +} + + +static int +call_mountd(fh_cache *fp, u_long proc, fwd_fun fun, wchan_t wchan) +{ + struct rpc_msg mnt_msg; + int len; + char iobuf[UDPMSGSIZE]; + int error; + u_long mnt_version; + + if (!nfs_auth) { + error = make_nfs_auth(); + if (error) + return error; + } + + if (fp->fh_sin.sin_port == 0) { + u_short mountd_port; + error = get_mountd_port(fp->fh_fs, &mountd_port, wchan); + if (error) + return error; + fp->fh_sin.sin_port = mountd_port; + } + + /* find the right version of the mount protocol */ +#ifdef HAVE_FS_NFS3 + if (fp->fh_nfs_version == NFS_VERSION3) + mnt_version = AM_MOUNTVERS3; + else +#endif /* HAVE_FS_NFS3 */ + mnt_version = MOUNTVERS; + plog(XLOG_INFO, "call_mountd: NFS version %d, mount version %d", + (int) fp->fh_nfs_version, (int) mnt_version); + + rpc_msg_init(&mnt_msg, MOUNTPROG, mnt_version, MOUNTPROC_NULL); + len = make_rpc_packet(iobuf, + sizeof(iobuf), + proc, + &mnt_msg, + (voidp) &fp->fh_path, + (XDRPROC_T_TYPE) xdr_nfspath, + nfs_auth); + + if (len > 0) { + error = fwd_packet(MK_RPC_XID(RPC_XID_MOUNTD, fp->fh_id), + iobuf, + len, + &fp->fh_sin, + &fp->fh_sin, + (opaque_t) ((long) fp->fh_id), /* cast to long needed for 64-bit archs */ + fun); + } else { + error = -len; + } + + /* + * It may be the case that we're sending to the wrong MOUNTD port. This + * occurs if mountd is restarted on the server after the port has been + * looked up and stored in the filehandle cache somewhere. The correct + * solution, if we're going to cache port numbers is to catch the ICMP + * port unreachable reply from the server and cause the portmap request + * to be redone. The quick solution here is to invalidate the MOUNTD + * port. + */ + fp->fh_sin.sin_port = 0; + + return error; +} + + +static int +webnfs_lookup(fh_cache *fp, fwd_fun fun, wchan_t wchan) +{ + struct rpc_msg wnfs_msg; + int len; + char iobuf[UDPMSGSIZE]; + int error; + u_long proc; + XDRPROC_T_TYPE xdr_fn; + voidp argp; + nfsdiropargs args; +#ifdef HAVE_FS_NFS3 + am_LOOKUP3args args3; +#endif + char *wnfs_path; + size_t l; + + if (!nfs_auth) { + error = make_nfs_auth(); + if (error) + return error; + } + + if (fp->fh_sin.sin_port == 0) { + /* FIXME: wrong, don't discard sin_port in the first place for WebNFS. */ + plog(XLOG_WARNING, "webnfs_lookup: port == 0 for nfs on %s, fixed", + fp->fh_fs->fs_host); + fp->fh_sin.sin_port = htons(NFS_PORT); + } + + /* + * Use native path like the rest of amd (cf. RFC 2054, 6.1). + */ + l = strlen(fp->fh_path) + 2; + wnfs_path = (char *) xmalloc(l); + wnfs_path[0] = 0x80; + xstrlcpy(wnfs_path + 1, fp->fh_path, l - 1); + + /* find the right program and lookup procedure */ +#ifdef HAVE_FS_NFS3 + if (fp->fh_nfs_version == NFS_VERSION3) { + proc = AM_NFSPROC3_LOOKUP; + xdr_fn = (XDRPROC_T_TYPE) xdr_am_LOOKUP3args; + argp = &args3; + /* WebNFS public file handle */ + args3.what.dir.am_fh3_length = 0; + args3.what.name = wnfs_path; + } else { +#endif /* HAVE_FS_NFS3 */ + proc = NFSPROC_LOOKUP; + xdr_fn = (XDRPROC_T_TYPE) xdr_diropargs; + argp = &args; + /* WebNFS public file handle */ + memset(&args.da_fhandle, 0, NFS_FHSIZE); + args.da_name = wnfs_path; +#ifdef HAVE_FS_NFS3 + } +#endif /* HAVE_FS_NFS3 */ + + plog(XLOG_INFO, "webnfs_lookup: NFS version %d", (int) fp->fh_nfs_version); + + rpc_msg_init(&wnfs_msg, NFS_PROGRAM, fp->fh_nfs_version, proc); + len = make_rpc_packet(iobuf, + sizeof(iobuf), + proc, + &wnfs_msg, + argp, + (XDRPROC_T_TYPE) xdr_fn, + nfs_auth); + + if (len > 0) { + error = fwd_packet(MK_RPC_XID(RPC_XID_WEBNFS, fp->fh_id), + iobuf, + len, + &fp->fh_sin, + &fp->fh_sin, + (opaque_t) ((long) fp->fh_id), /* cast to long needed for 64-bit archs */ + fun); + } else { + error = -len; + } + + XFREE(wnfs_path); + return error; +} + + +/* + * NFS needs the local filesystem, remote filesystem + * remote hostname. + * Local filesystem defaults to remote and vice-versa. + */ +static char * +nfs_match(am_opts *fo) +{ + char *xmtab; + size_t l; + + if (fo->opt_fs && !fo->opt_rfs) + fo->opt_rfs = fo->opt_fs; + if (!fo->opt_rfs) { + plog(XLOG_USER, "nfs: no remote filesystem specified"); + return NULL; + } + if (!fo->opt_rhost) { + plog(XLOG_USER, "nfs: no remote host specified"); + return NULL; + } + + /* + * Determine magic cookie to put in mtab + */ + l = strlen(fo->opt_rhost) + strlen(fo->opt_rfs) + 2; + xmtab = (char *) xmalloc(l); + xsnprintf(xmtab, l, "%s:%s", fo->opt_rhost, fo->opt_rfs); + dlog("NFS: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"", + fo->opt_rhost, fo->opt_rfs, fo->opt_fs); + + return xmtab; +} + + +/* + * Initialize am structure for nfs + */ +static int +nfs_init(mntfs *mf) +{ + int error; + am_nfs_handle_t fhs; + char *colon; + + if (mf->mf_private) { + if (mf->mf_flags & MFF_NFS_SCALEDOWN) { + fserver *fs; + + /* tell remote mountd that we're done with this filehandle */ + mf->mf_ops->umounted(mf); + + mf->mf_prfree(mf->mf_private); + fs = mf->mf_ops->ffserver(mf); + free_srvr(mf->mf_server); + mf->mf_server = fs; + } else + return 0; + } + + colon = strchr(mf->mf_info, ':'); + if (colon == 0) + return ENOENT; + + error = prime_nfs_fhandle_cache(colon + 1, mf->mf_server, &fhs, mf); + if (!error) { + mf->mf_private = (opaque_t) ALLOC(am_nfs_handle_t); + mf->mf_prfree = (void (*)(opaque_t)) free; + memmove(mf->mf_private, (voidp) &fhs, sizeof(fhs)); + } + return error; +} + + +int +mount_nfs_fh(am_nfs_handle_t *fhp, char *mntdir, char *fs_name, mntfs *mf) +{ + MTYPE_TYPE type; + char *colon; + char *xopts=NULL, transp_timeo_opts[40], transp_retrans_opts[40]; + char host[MAXHOSTNAMELEN + MAXPATHLEN + 2]; + fserver *fs = mf->mf_server; + u_long nfs_version = fs->fs_version; + char *nfs_proto = fs->fs_proto; /* "tcp" or "udp" */ + int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; + int error; + int genflags; + int retry; + int proto = AMU_TYPE_NONE; + mntent_t mnt; + nfs_args_t nfs_args; + + /* + * Extract HOST name to give to kernel. + * Some systems like osf1/aix3/bsd44 variants may need old code + * for NFS_ARGS_NEEDS_PATH. + */ + if (!(colon = strchr(fs_name, ':'))) + return ENOENT; +#ifdef MOUNT_TABLE_ON_FILE + *colon = '\0'; +#endif /* MOUNT_TABLE_ON_FILE */ + xstrlcpy(host, fs_name, sizeof(host)); +#ifdef MOUNT_TABLE_ON_FILE + *colon = ':'; +#endif /* MOUNT_TABLE_ON_FILE */ +#ifdef MAXHOSTNAMELEN + /* most kernels have a name length restriction */ + if (strlen(host) >= MAXHOSTNAMELEN) + xstrlcpy(host + MAXHOSTNAMELEN - 3, "..", + sizeof(host) - MAXHOSTNAMELEN + 3); +#endif /* MAXHOSTNAMELEN */ + + /* + * Create option=VAL for udp/tcp specific timeouts and retrans values, but + * only if these options were specified. + */ + + transp_timeo_opts[0] = transp_retrans_opts[0] = '\0'; /* initialize */ + if (STREQ(nfs_proto, "udp")) + proto = AMU_TYPE_UDP; + else if (STREQ(nfs_proto, "tcp")) + proto = AMU_TYPE_TCP; + if (proto != AMU_TYPE_NONE) { + if (gopt.amfs_auto_timeo[proto] > 0) + xsnprintf(transp_timeo_opts, sizeof(transp_timeo_opts), "%s=%d,", + MNTTAB_OPT_TIMEO, gopt.amfs_auto_timeo[proto]); + if (gopt.amfs_auto_retrans[proto] > 0) + xsnprintf(transp_retrans_opts, sizeof(transp_retrans_opts), "%s=%d,", + MNTTAB_OPT_RETRANS, gopt.amfs_auto_retrans[proto]); + } + + if (mf->mf_remopts && *mf->mf_remopts && + !islocalnet(fs->fs_ip->sin_addr.s_addr)) { + plog(XLOG_INFO, "Using remopts=\"%s\"", mf->mf_remopts); + /* use transp_opts first, so map-specific opts will override */ + xopts = str3cat(xopts, transp_timeo_opts, transp_retrans_opts, mf->mf_remopts); + } else { + /* use transp_opts first, so map-specific opts will override */ + xopts = str3cat(xopts, transp_timeo_opts, transp_retrans_opts, mf->mf_mopts); + } + + memset((voidp) &mnt, 0, sizeof(mnt)); + mnt.mnt_dir = mntdir; + mnt.mnt_fsname = fs_name; + mnt.mnt_opts = xopts; + + /* + * Set mount types accordingly + */ +#ifndef HAVE_FS_NFS3 + type = MOUNT_TYPE_NFS; + mnt.mnt_type = MNTTAB_TYPE_NFS; +#else /* HAVE_FS_NFS3 */ + if (nfs_version == NFS_VERSION3) { + type = MOUNT_TYPE_NFS3; + /* + * Systems that include the mount table "vers" option generally do not + * set the mnttab entry to "nfs3", but to "nfs" and then they set + * "vers=3". Setting it to "nfs3" works, but it may break some things + * like "df -t nfs" and the "quota" program (esp. on Solaris and Irix). + * So on those systems, set it to "nfs". + * Note: MNTTAB_OPT_VERS is always set for NFS3 (see am_compat.h). + */ +# if defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE) + mnt.mnt_type = MNTTAB_TYPE_NFS; +# else /* defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE) */ + mnt.mnt_type = MNTTAB_TYPE_NFS3; +# endif /* defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE) */ + } else { + type = MOUNT_TYPE_NFS; + mnt.mnt_type = MNTTAB_TYPE_NFS; + } +#endif /* HAVE_FS_NFS3 */ + plog(XLOG_INFO, "mount_nfs_fh: NFS version %d", (int) nfs_version); + plog(XLOG_INFO, "mount_nfs_fh: using NFS transport %s", nfs_proto); + + retry = hasmntval(&mnt, MNTTAB_OPT_RETRY); + if (retry <= 0) + retry = 1; /* XXX */ + + genflags = compute_mount_flags(&mnt); +#ifdef HAVE_FS_AUTOFS + if (on_autofs) + genflags |= autofs_compute_mount_flags(&mnt); +#endif /* HAVE_FS_AUTOFS */ + + /* setup the many fields and flags within nfs_args */ + compute_nfs_args(&nfs_args, + &mnt, + genflags, + NULL, /* struct netconfig *nfsncp */ + fs->fs_ip, + nfs_version, + nfs_proto, + fhp, + host, + fs_name); + + /* finally call the mounting function */ + if (amuDebug(D_TRACE)) { + print_nfs_args(&nfs_args, nfs_version); + plog(XLOG_DEBUG, "Generic mount flags 0x%x used for NFS mount", genflags); + } + error = mount_fs(&mnt, genflags, (caddr_t) &nfs_args, retry, type, + nfs_version, nfs_proto, mnttab_file_name, on_autofs); + XFREE(xopts); + +#ifdef HAVE_TRANSPORT_TYPE_TLI + free_knetconfig(nfs_args.knconf); + if (nfs_args.addr) + XFREE(nfs_args.addr); /* allocated in compute_nfs_args() */ +#endif /* HAVE_TRANSPORT_TYPE_TLI */ + + return error; +} + + +static int +nfs_mount(am_node *am, mntfs *mf) +{ + int error = 0; + mntent_t mnt; + + if (!mf->mf_private) { + plog(XLOG_ERROR, "Missing filehandle for %s", mf->mf_info); + return EINVAL; + } + + mnt.mnt_opts = mf->mf_mopts; + if (amu_hasmntopt(&mnt, "softlookup") || + (amu_hasmntopt(&mnt, "soft") && !amu_hasmntopt(&mnt, "nosoftlookup"))) + am->am_flags |= AMF_SOFTLOOKUP; + + error = mount_nfs_fh((am_nfs_handle_t *) mf->mf_private, + mf->mf_mount, + mf->mf_info, + mf); + + if (error) { + errno = error; + dlog("mount_nfs: %m"); + } + + return error; +} + + +static int +nfs_umount(am_node *am, mntfs *mf) +{ + int unmount_flags, new_unmount_flags, error; + + unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + error = UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); + +#if defined(HAVE_UMOUNT2) && (defined(MNT2_GEN_OPT_FORCE) || defined(MNT2_GEN_OPT_DETACH)) + /* + * If the attempt to unmount failed with EBUSY, and this fserver was + * marked for forced unmounts, then use forced/lazy unmounts. + */ + if (error == EBUSY && + gopt.flags & CFM_FORCED_UNMOUNTS && + mf->mf_server->fs_flags & FSF_FORCE_UNMOUNT) { + plog(XLOG_INFO, "EZK: nfs_umount: trying forced/lazy unmounts"); + /* + * XXX: turning off the FSF_FORCE_UNMOUNT may not be perfectly + * incorrect. Multiple nodes may need to be timed out and restarted for + * a single hung fserver. + */ + mf->mf_server->fs_flags &= ~FSF_FORCE_UNMOUNT; + new_unmount_flags = unmount_flags | AMU_UMOUNT_FORCE | AMU_UMOUNT_DETACH; + error = UMOUNT_FS(mf->mf_mount, mnttab_file_name, new_unmount_flags); + } +#endif /* HAVE_UMOUNT2 && (MNT2_GEN_OPT_FORCE || MNT2_GEN_OPT_DETACH) */ + + /* + * Here is some code to unmount 'restarted' file systems. + * The restarted file systems are marked as 'nfs', not + * 'host', so we only have the map information for the + * the top-level mount. The unmount will fail (EBUSY) + * if there are anything else from the NFS server mounted + * below the mount-point. This code checks to see if there + * is anything mounted with the same prefix as the + * file system to be unmounted ("/a/b/c" when unmounting "/a/b"). + * If there is, and it is a 'restarted' file system, we unmount + * it. + * Added by Mike Mitchell, mcm@unx.sas.com, 09/08/93 + */ + if (error == EBUSY) { + mntfs *new_mf; + int len = strlen(mf->mf_mount); + int didsome = 0; + + ITER(new_mf, mntfs, &mfhead) { + if (new_mf->mf_ops != mf->mf_ops || + new_mf->mf_refc > 1 || + mf == new_mf || + ((new_mf->mf_flags & (MFF_MOUNTED | MFF_UNMOUNTING | MFF_RESTART)) == (MFF_MOUNTED | MFF_RESTART))) + continue; + + if (NSTREQ(mf->mf_mount, new_mf->mf_mount, len) && + new_mf->mf_mount[len] == '/') { + new_unmount_flags = + (new_mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + UMOUNT_FS(new_mf->mf_mount, mnttab_file_name, new_unmount_flags); + didsome = 1; + } + } + if (didsome) + error = UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); + } + if (error) + return error; + + return 0; +} + + +static void +nfs_umounted(mntfs *mf) +{ + fserver *fs; + char *colon, *path; + + if (mf->mf_error || mf->mf_refc > 1) + return; + + /* + * No need to inform mountd when WebNFS is in use. + */ + if (mf->mf_flags & MFF_WEBNFS) + return; + + /* + * Call the mount daemon on the server to announce that we are not using + * the fs any more. + * + * XXX: This is *wrong*. The mountd should be called when the fhandle is + * flushed from the cache, and a reference held to the cached entry while + * the fs is mounted... + */ + fs = mf->mf_server; + colon = path = strchr(mf->mf_info, ':'); + if (fs && colon) { + fh_cache f; + + dlog("calling mountd for %s", mf->mf_info); + *path++ = '\0'; + f.fh_path = path; + f.fh_sin = *fs->fs_ip; + f.fh_sin.sin_port = (u_short) 0; + f.fh_nfs_version = fs->fs_version; + f.fh_fs = fs; + f.fh_id = 0; + f.fh_error = 0; + prime_nfs_fhandle_cache(colon + 1, mf->mf_server, (am_nfs_handle_t *) 0, mf); + call_mountd(&f, MOUNTPROC_UMNT, (fwd_fun *) 0, (wchan_t) 0); + *colon = ':'; + } +} diff --git a/amd/ops_nfs3.c b/amd/ops_nfs3.c new file mode 100644 index 000000000000..db3c7f9d265d --- /dev/null +++ b/amd/ops_nfs3.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/ops_nfs3.c + * + */ + +/* + * Network file system version 3.0 + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* FEEL FREE TO IMPLEMENT THIS... :-) */ diff --git a/amd/ops_nullfs.c b/amd/ops_nullfs.c new file mode 100644 index 000000000000..cf621ec33ff5 --- /dev/null +++ b/amd/ops_nullfs.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/ops_nullfs.c + * + */ + +/* + * The null filesystem in BSD-4.4 is similar to the loopback one. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* FEEL FREE TO IMPLEMENT THIS... :-) */ diff --git a/amd/ops_pcfs.c b/amd/ops_pcfs.c new file mode 100644 index 000000000000..527dc926ca77 --- /dev/null +++ b/amd/ops_pcfs.c @@ -0,0 +1,226 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/ops_pcfs.c + * + */ + +/* + * PC (MS-DOS) file system + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* forward definitions */ +static char *pcfs_match(am_opts *fo); +static int pcfs_mount(am_node *am, mntfs *mf); +static int pcfs_umount(am_node *am, mntfs *mf); + +/* + * Ops structure + */ +am_ops pcfs_ops = +{ + "pcfs", + pcfs_match, + 0, /* pcfs_init */ + pcfs_mount, + pcfs_umount, + amfs_error_lookup_child, + amfs_error_mount_child, + amfs_error_readdir, + 0, /* pcfs_readlink */ + 0, /* pcfs_mounted */ + 0, /* pcfs_umounted */ + amfs_generic_find_srvr, + 0, /* pcfs_get_wchan */ + FS_MKMNT | FS_UBACKGROUND | FS_AMQINFO, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_PCFS_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ +}; + + + +/* + * PCFS needs remote filesystem. + */ +static char * +pcfs_match(am_opts *fo) +{ + if (!fo->opt_dev) { + plog(XLOG_USER, "pcfs: no source device specified"); + return 0; + } + dlog("PCFS: mounting device \"%s\" on \"%s\"", fo->opt_dev, fo->opt_fs); + + /* + * Determine magic cookie to put in mtab + */ + return strdup(fo->opt_dev); +} + + +static int +mount_pcfs(char *mntdir, char *fs_name, char *opts, int on_autofs) +{ + pcfs_args_t pcfs_args; + mntent_t mnt; + int flags; +#if defined(HAVE_PCFS_ARGS_T_MASK) || defined(HAVE_PCFS_ARGS_T_DIRMASK) + int mask; +#endif /* defined(HAVE_PCFS_ARGS_T_MASK) || defined(HAVE_PCFS_ARGS_T_DIRMASK) */ +#if defined(HAVE_PCFS_ARGS_T_UID) || defined(HAVE_PCFS_ARGS_T_UID) + char *str; +#endif /* defined(HAVE_PCFS_ARGS_T_UID) || defined(HAVE_PCFS_ARGS_T_UID) */ + + /* + * Figure out the name of the file system type. + */ + MTYPE_TYPE type = MOUNT_TYPE_PCFS; + + memset((voidp) &pcfs_args, 0, sizeof(pcfs_args)); /* Paranoid */ + + /* + * Fill in the mount structure + */ + memset((voidp) &mnt, 0, sizeof(mnt)); + mnt.mnt_dir = mntdir; + mnt.mnt_fsname = fs_name; + mnt.mnt_type = MNTTAB_TYPE_PCFS; + mnt.mnt_opts = opts; + + flags = compute_mount_flags(&mnt); +#ifdef HAVE_FS_AUTOFS + if (on_autofs) + flags |= autofs_compute_mount_flags(&mnt); +#endif /* HAVE_FS_AUTOFS */ + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "mount_pcfs: flags=0x%x", (u_int) flags); + +#ifdef HAVE_PCFS_ARGS_T_FSPEC + pcfs_args.fspec = fs_name; +#endif /* HAVE_PCFS_ARGS_T_FSPEC */ + +#ifdef HAVE_PCFS_ARGS_T_MASK + pcfs_args.mask = 0777; /* this may be the msdos file modes */ + if ((mask = hasmntval(&mnt, MNTTAB_OPT_MASK)) > 0) + pcfs_args.mask = mask; + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "mount_pcfs: mask=%o (octal)", (u_int) pcfs_args.mask); +#endif /* HAVE_PCFS_ARGS_T_MASK */ + +#ifdef HAVE_PCFS_ARGS_T_DIRMASK + pcfs_args.dirmask = 0777; /* this may be the msdos dir modes */ + if ((mask = hasmntval(&mnt, MNTTAB_OPT_DIRMASK)) > 0) + pcfs_args.dirmask = mask; + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "mount_pcfs: dirmask=%o (octal)", (u_int) pcfs_args.dirmask); +#endif /* HAVE_PCFS_ARGS_T_DIRMASK */ + +#ifdef HAVE_PCFS_ARGS_T_UID + pcfs_args.uid = 0; /* default to root */ + if ((str = hasmntstr(&mnt, MNTTAB_OPT_USER)) != NULL) { + struct passwd *pw; + if ((pw = getpwnam(str)) != NULL) + pcfs_args.uid = pw->pw_uid; + else /* maybe used passed a UID number, not user name */ + pcfs_args.uid = atoi(str); /* atoi returns '0' if it failed */ + XFREE(str); + } + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "mount_pcfs: uid=%d", (int) pcfs_args.uid); +#endif /* HAVE_PCFS_ARGS_T_UID */ + +#ifdef HAVE_PCFS_ARGS_T_GID + pcfs_args.gid = 0; /* default to wheel/root group */ + if ((str = hasmntstr(&mnt, MNTTAB_OPT_GROUP)) != NULL) { + struct group *gr; + if ((gr = getgrnam(str)) != NULL) + pcfs_args.gid = gr->gr_gid; + else /* maybe used passed a GID number, not group name */ + pcfs_args.gid = atoi(str); /* atoi returns '0' if it failed */ + XFREE(str); + } + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "mount_pcfs: gid=%d", (int) pcfs_args.gid); +#endif /* HAVE_PCFS_ARGS_T_GID */ + +#ifdef HAVE_PCFS_ARGS_T_SECONDSWEST + pcfs_args.secondswest = 0; /* XXX: fill in correct values */ +#endif /* HAVE_PCFS_ARGS_T_SECONDSWEST */ +#ifdef HAVE_PCFS_ARGS_T_DSTTIME + pcfs_args.dsttime = 0; /* XXX: fill in correct values */ +#endif /* HAVE_PCFS_ARGS_T_DSTTIME */ + + /* + * Call generic mount routine + */ + return mount_fs(&mnt, flags, (caddr_t) & pcfs_args, 0, type, 0, NULL, mnttab_file_name, on_autofs); +} + + +static int +pcfs_mount(am_node *am, mntfs *mf) +{ + int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; + int error; + + error = mount_pcfs(mf->mf_mount, mf->mf_info, mf->mf_mopts, on_autofs); + if (error) { + errno = error; + plog(XLOG_ERROR, "mount_pcfs: %m"); + return error; + } + + return 0; +} + + +static int +pcfs_umount(am_node *am, mntfs *mf) +{ + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + + return UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); +} diff --git a/amd/ops_tfs.c b/amd/ops_tfs.c new file mode 100644 index 000000000000..142c32022937 --- /dev/null +++ b/amd/ops_tfs.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/ops_tfs.c + * + */ + +/* + * Translucent file system + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* FEEL FREE TO IMPLEMENT THIS... :-) */ diff --git a/amd/ops_tmpfs.c b/amd/ops_tmpfs.c new file mode 100644 index 000000000000..eddd75569e9e --- /dev/null +++ b/amd/ops_tmpfs.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/ops_tmpfs.c + * + */ + +/* + * TMPFS file system (combines RAM-fs and swap-fs) + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* FEEL FREE TO IMPLEMENT THIS... :-) */ diff --git a/amd/ops_ufs.c b/amd/ops_ufs.c new file mode 100644 index 000000000000..fd82a5cd2228 --- /dev/null +++ b/amd/ops_ufs.c @@ -0,0 +1,180 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/ops_ufs.c + * + */ + +/* + * UN*X file system + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* forward declarations */ +static char *ufs_match(am_opts *fo); +static int ufs_mount(am_node *am, mntfs *mf); +static int ufs_umount(am_node *am, mntfs *mf); + +/* + * Ops structure + */ +am_ops ufs_ops = +{ + "ufs", + ufs_match, + 0, /* ufs_init */ + ufs_mount, + ufs_umount, + amfs_error_lookup_child, + amfs_error_mount_child, + amfs_error_readdir, + 0, /* ufs_readlink */ + 0, /* ufs_mounted */ + 0, /* ufs_umounted */ + amfs_generic_find_srvr, + 0, /* ufs_get_wchan */ + FS_MKMNT | FS_NOTIMEOUT | FS_UBACKGROUND | FS_AMQINFO, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_UFS_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ +}; + + +/* + * UFS needs local filesystem and device. + */ +static char * +ufs_match(am_opts *fo) +{ + + if (!fo->opt_dev) { + plog(XLOG_USER, "ufs: no device specified"); + return 0; + } + + dlog("UFS: mounting device \"%s\" on \"%s\"", fo->opt_dev, fo->opt_fs); + + /* + * Determine magic cookie to put in mtab + */ + return strdup(fo->opt_dev); +} + + +static int +mount_ufs(char *mntdir, char *fs_name, char *opts, int on_autofs) +{ + ufs_args_t ufs_args; + mntent_t mnt; + int genflags; + + /* + * Figure out the name of the file system type. + */ + MTYPE_TYPE type = MOUNT_TYPE_UFS; + + memset((voidp) &ufs_args, 0, sizeof(ufs_args)); /* Paranoid */ + + /* + * Fill in the mount structure + */ + memset((voidp) &mnt, 0, sizeof(mnt)); + mnt.mnt_dir = mntdir; + mnt.mnt_fsname = fs_name; + mnt.mnt_type = MNTTAB_TYPE_UFS; + mnt.mnt_opts = opts; + + genflags = compute_mount_flags(&mnt); +#ifdef HAVE_FS_AUTOFS + if (on_autofs) + genflags |= autofs_compute_mount_flags(&mnt); +#endif /* HAVE_FS_AUTOFS */ + +#ifdef HAVE_UFS_ARGS_T_FLAGS + ufs_args.flags = genflags; /* XXX: is this correct? */ +#endif /* HAVE_UFS_ARGS_T_FLAGS */ + +#ifdef HAVE_UFS_ARGS_T_UFS_FLAGS + ufs_args.ufs_flags = genflags; +#endif /* HAVE_UFS_ARGS_T_UFS_FLAGS */ + +#ifdef HAVE_UFS_ARGS_T_FSPEC + ufs_args.fspec = fs_name; +#endif /* HAVE_UFS_ARGS_T_FSPEC */ + +#ifdef HAVE_UFS_ARGS_T_UFS_PGTHRESH + ufs_args.ufs_pgthresh = hasmntval(&mnt, MNTTAB_OPT_PGTHRESH); +#endif /* HAVE_UFS_ARGS_T_UFS_PGTHRESH */ + + /* + * Call generic mount routine + */ + return mount_fs(&mnt, genflags, (caddr_t) &ufs_args, 0, type, 0, NULL, mnttab_file_name, on_autofs); +} + + +static int +ufs_mount(am_node *am, mntfs *mf) +{ + int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; + int error; + + error = mount_ufs(mf->mf_mount, mf->mf_info, mf->mf_mopts, on_autofs); + if (error) { + errno = error; + plog(XLOG_ERROR, "mount_ufs: %m"); + return error; + } + + return 0; +} + + +static int +ufs_umount(am_node *am, mntfs *mf) +{ + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + + return UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); +} diff --git a/amd/ops_umapfs.c b/amd/ops_umapfs.c new file mode 100644 index 000000000000..5ba735e39c61 --- /dev/null +++ b/amd/ops_umapfs.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/ops_umapfs.c + * + */ + +/* + * uid/gid mapping filesystem. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* FEEL FREE TO IMPLEMENT THIS... :-) */ diff --git a/amd/ops_unionfs.c b/amd/ops_unionfs.c new file mode 100644 index 000000000000..73f8d6676203 --- /dev/null +++ b/amd/ops_unionfs.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/ops_unionfs.c + * + */ + +/* + * Union filesystem (ala BSD-4.4) + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* FEEL FREE TO IMPLEMENT THIS... :-) */ diff --git a/amd/ops_xfs.c b/amd/ops_xfs.c new file mode 100644 index 000000000000..e0e740bc0a0e --- /dev/null +++ b/amd/ops_xfs.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/ops_xfs.c + * + */ + +/* + * Irix UN*X file system: XFS (Extended File System) + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* forward declarations */ +static char *xfs_match(am_opts *fo); +static int xfs_mount(am_node *am, mntfs *mf); +static int xfs_umount(am_node *am, mntfs *mf); + +/* + * Ops structure + */ +am_ops xfs_ops = +{ + "xfs", + xfs_match, + 0, /* xfs_init */ + xfs_mount, + xfs_umount, + amfs_error_lookup_child, + amfs_error_mount_child, + amfs_error_readdir, + 0, /* xfs_readlink */ + 0, /* xfs_mounted */ + 0, /* xfs_umounted */ + amfs_generic_find_srvr, + 0, /* xfs_get_wchan */ + FS_MKMNT | FS_NOTIMEOUT | FS_UBACKGROUND | FS_AMQINFO, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_XFS_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ +}; + + +/* + * XFS needs local filesystem and device. + */ +static char * +xfs_match(am_opts *fo) +{ + + if (!fo->opt_dev) { + plog(XLOG_USER, "xfs: no device specified"); + return 0; + } + + dlog("XFS: mounting device \"%s\" on \"%s\"", fo->opt_dev, fo->opt_fs); + + /* + * Determine magic cookie to put in mtab + */ + return strdup(fo->opt_dev); +} + + +static int +mount_xfs(char *mntdir, char *fs_name, char *opts, int on_autofs) +{ + xfs_args_t xfs_args; + mntent_t mnt; + int flags; + + /* + * Figure out the name of the file system type. + */ + MTYPE_TYPE type = MOUNT_TYPE_XFS; + + memset((voidp) &xfs_args, 0, sizeof(xfs_args)); /* Paranoid */ + + /* + * Fill in the mount structure + */ + memset((voidp) &mnt, 0, sizeof(mnt)); + mnt.mnt_dir = mntdir; + mnt.mnt_fsname = fs_name; + mnt.mnt_type = MNTTAB_TYPE_XFS; + mnt.mnt_opts = opts; + + flags = compute_mount_flags(&mnt); +#ifdef HAVE_FS_AUTOFS + if (on_autofs) + flags |= autofs_compute_mount_flags(&mnt); +#endif /* HAVE_FS_AUTOFS */ + +#ifdef HAVE_XFS_ARGS_T_FLAGS + xfs_args.flags = 0; /* XXX: fix this to correct flags */ +#endif /* HAVE_XFS_ARGS_T_FLAGS */ +#ifdef HAVE_XFS_ARGS_T_FSPEC + xfs_args.fspec = fs_name; +#endif /* HAVE_XFS_ARGS_T_FSPEC */ + + /* + * Call generic mount routine + */ + return mount_fs(&mnt, flags, (caddr_t) &xfs_args, 0, type, 0, NULL, mnttab_file_name, on_autofs); +} + + +static int +xfs_mount(am_node *am, mntfs *mf) +{ + int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; + int error; + + error = mount_xfs(mf->mf_mount, mf->mf_info, mf->mf_mopts, on_autofs); + if (error) { + errno = error; + plog(XLOG_ERROR, "mount_xfs: %m"); + return error; + } + + return 0; +} + + +static int +xfs_umount(am_node *am, mntfs *mf) +{ + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + + return UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); +} diff --git a/amd/opts.c b/amd/opts.c new file mode 100644 index 000000000000..a3d35343adc0 --- /dev/null +++ b/amd/opts.c @@ -0,0 +1,1519 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * 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. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/opts.c + * + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* + * MACROS: + */ +#define NLEN 16 /* Length of longest option name (conservative) */ +#define S(x) (x) , (sizeof(x)-1) +/* + * 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) + +/* + * TYPEDEFS: + */ +typedef int (*IntFuncPtr) (char *); +typedef struct opt_apply opt_apply; +enum vs_opt { SelEQ, SelNE, VarAss }; + +/* + * STRUCTURES + */ +struct opt { + 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 */ + int (*fxn_p)(char *); /* Pointer to boolean function */ + int case_insensitive; /* How to do selector comparisons */ +}; + +struct opt_apply { + char **opt; + char *val; +}; + +struct functable { + char *name; + IntFuncPtr func; +}; + +/* + * FORWARD DEFINITION: + */ +static int f_in_network(char *); +static int f_xhost(char *); +static int f_netgrp(char *); +static int f_netgrpd(char *); +static int f_exists(char *); +static int f_false(char *); +static int f_true(char *); +static inline char *expand_options(char *key); + +/* + * STATICS: + */ +static char NullStr[] = "<NULL>"; +static char nullstr[] = ""; +static char *opt_dkey = NullStr; +static char *opt_host = nullstr; /* XXX: was the global hostname */ +static char *opt_hostd = hostd; +static char *opt_key = nullstr; +static char *opt_keyd = nullstr; +static char *opt_map = nullstr; +static char *opt_path = nullstr; +char uid_str[SIZEOF_UID_STR], gid_str[SIZEOF_GID_STR]; +char *opt_uid = uid_str; +char *opt_gid = gid_str; +static char *vars[8]; +static char *literal_dollar = "$"; /* ${dollar}: a literal '$' in maps */ + +/* + * GLOBALS + */ +static struct am_opts fs_static; /* copy of the options to play with */ + + +/* + * Options in some order corresponding to frequency of use so that + * first-match algorithm is sped up. + */ +static struct opt opt_fields[] = { + /* Name and length. + Option str. Selector str. boolean fxn. case sensitive */ + { S("opts"), + &fs_static.opt_opts, 0, 0, FALSE }, + { S("host"), + 0, &opt_host, 0, TRUE }, + { S("hostd"), + 0, &opt_hostd, 0, TRUE }, + { S("type"), + &fs_static.opt_type, 0, 0, FALSE }, + { S("rhost"), + &fs_static.opt_rhost, 0, 0, TRUE }, + { S("rfs"), + &fs_static.opt_rfs, 0, 0, FALSE }, + { S("fs"), + &fs_static.opt_fs, 0, 0, FALSE }, + { S("key"), + 0, &opt_key, 0, FALSE }, + { S("map"), + 0, &opt_map, 0, FALSE }, + { S("sublink"), + &fs_static.opt_sublink, 0, 0, FALSE }, + { S("arch"), + 0, &gopt.arch, 0, TRUE }, + { S("dev"), + &fs_static.opt_dev, 0, 0, FALSE }, + { S("pref"), + &fs_static.opt_pref, 0, 0, FALSE }, + { S("path"), + 0, &opt_path, 0, FALSE }, + { S("autodir"), + 0, &gopt.auto_dir, 0, FALSE }, + { S("delay"), + &fs_static.opt_delay, 0, 0, FALSE }, + { S("domain"), + 0, &hostdomain, 0, TRUE }, + { S("karch"), + 0, &gopt.karch, 0, TRUE }, + { S("cluster"), + 0, &gopt.cluster, 0, TRUE }, + { S("wire"), + 0, 0, f_in_network, TRUE }, + { S("network"), + 0, 0, f_in_network, TRUE }, + { S("netnumber"), + 0, 0, f_in_network, TRUE }, + { S("byte"), + 0, &endian, 0, TRUE }, + { S("os"), + 0, &gopt.op_sys, 0, TRUE }, + { S("osver"), + 0, &gopt.op_sys_ver, 0, TRUE }, + { S("full_os"), + 0, &gopt.op_sys_full, 0, TRUE }, + { S("vendor"), + 0, &gopt.op_sys_vendor, 0, TRUE }, + { S("remopts"), + &fs_static.opt_remopts, 0, 0, FALSE }, + { S("mount"), + &fs_static.opt_mount, 0, 0, FALSE }, + { S("unmount"), + &fs_static.opt_unmount, 0, 0, FALSE }, + { S("umount"), + &fs_static.opt_umount, 0, 0, FALSE }, + { S("cache"), + &fs_static.opt_cache, 0, 0, FALSE }, + { S("user"), + &fs_static.opt_user, 0, 0, FALSE }, + { S("group"), + &fs_static.opt_group, 0, 0, FALSE }, + { S(".key"), + 0, &opt_dkey, 0, FALSE }, + { S("key."), + 0, &opt_keyd, 0, FALSE }, + { S("maptype"), + &fs_static.opt_maptype, 0, 0, FALSE }, + { S("cachedir"), + &fs_static.opt_cachedir, 0, 0, FALSE }, + { S("addopts"), + &fs_static.opt_addopts, 0, 0, FALSE }, + { S("uid"), + 0, &opt_uid, 0, FALSE }, + { S("gid"), + 0, &opt_gid, 0, FALSE }, + { S("mount_type"), + &fs_static.opt_mount_type, 0, 0, FALSE }, + { S("dollar"), + &literal_dollar, 0, 0, FALSE }, + { S("var0"), + &vars[0], 0, 0, FALSE }, + { S("var1"), + &vars[1], 0, 0, FALSE }, + { S("var2"), + &vars[2], 0, 0, FALSE }, + { S("var3"), + &vars[3], 0, 0, FALSE }, + { S("var4"), + &vars[4], 0, 0, FALSE }, + { S("var5"), + &vars[5], 0, 0, FALSE }, + { S("var6"), + &vars[6], 0, 0, FALSE }, + { S("var7"), + &vars[7], 0, 0, FALSE }, + { 0, 0, 0, 0, 0, FALSE }, +}; + +static struct functable functable[] = { + { "in_network", f_in_network }, + { "xhost", f_xhost }, + { "netgrp", f_netgrp }, + { "netgrpd", f_netgrpd }, + { "exists", f_exists }, + { "false", f_false }, + { "true", f_true }, + { 0, 0 }, +}; + +/* + * Specially expand the remote host name first + */ +static opt_apply rhost_expansion[] = +{ + {&fs_static.opt_rhost, "${host}"}, + {0, 0}, +}; + +/* + * List of options which need to be expanded + * Note that the order here _may_ be important. + */ +static opt_apply expansions[] = +{ + {&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}, + {&fs_static.opt_umount, 0}, + {&fs_static.opt_cachedir, 0}, + {&fs_static.opt_addopts, 0}, + {0, 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_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}, + {&fs_static.opt_umount, 0}, + {&fs_static.opt_cachedir, 0}, + {&fs_static.opt_addopts, 0}, + {&vars[0], 0}, + {&vars[1], 0}, + {&vars[2], 0}, + {&vars[3], 0}, + {&vars[4], 0}, + {&vars[5], 0}, + {&vars[6], 0}, + {&vars[7], 0}, + {0, 0}, +}; + + +/* + * expand backslash escape sequences + * (escaped slash is handled separately in normalize_slash) + */ +static char +backslash(char **p) +{ + char c; + + if ((*p)[1] == '\0') { + plog(XLOG_USER, "Empty backslash escape"); + return **p; + } + + if (**p == '\\') { + (*p)++; + switch (**p) { + case 'g': + c = '\007'; /* Bell */ + break; + case 'b': + c = '\010'; /* Backspace */ + break; + case 't': + c = '\011'; /* Horizontal Tab */ + break; + case 'n': + c = '\012'; /* New Line */ + break; + case 'v': + c = '\013'; /* Vertical Tab */ + break; + case 'f': + c = '\014'; /* Form Feed */ + break; + case 'r': + c = '\015'; /* Carriage Return */ + break; + case 'e': + c = '\033'; /* Escape */ + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + { + int cnt, val, ch; + + for (cnt = 0, val = 0; cnt < 3; cnt++) { + ch = *(*p)++; + if (ch < '0' || ch > '7') { + (*p)--; + break; + } + val = (val << 3) | (ch - '0'); + } + + if ((val & 0xffffff00) != 0) + plog(XLOG_USER, + "Too large character constant %u\n", + val); + c = (char) val; + --(*p); + } + break; + + default: + c = **p; + break; + } + } else + c = **p; + + return c; +} + + +/* + * Skip to next option in the string + */ +static char * +opt(char **p) +{ + char *cp = *p; + char *dp = cp; + char *s = cp; + +top: + while (*cp && *cp != ';') { + if (*cp == '"') { + /* + * Skip past string + */ + for (cp++; *cp && *cp != '"'; cp++) + if (*cp == '\\') + *dp++ = backslash(&cp); + else + *dp++ = *cp; + if (*cp) + cp++; + } else { + *dp++ = *cp++; + } + } + + /* + * Skip past any remaining ';'s + */ + while (*cp == ';') + cp++; + + /* + * If we have a zero length string + * and there are more fields, then + * parse the next one. This allows + * sequences of empty fields. + */ + if (*cp && dp == s) + goto top; + + *dp = '\0'; + + *p = cp; + return s; +} + + +/* + * 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 <rens@imsi.com> + */ +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; +} + + +/* + * 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; +} + + +/* + * Skip to next option in the string, but don't scribble over the string. + * However, *p gets repointed to the start of the next string past ';'. + */ +static char * +opt_no_scribble(char **p) +{ + char *cp = *p; + char *dp = cp; + char *s = cp; + +top: + while (*cp && *cp != ';') { + if (*cp == '\"') { + /* + * Skip past string + */ + cp++; + while (*cp && *cp != '\"') + *dp++ = *cp++; + if (*cp) + cp++; + } else { + *dp++ = *cp++; + } + } + + /* + * Skip past any remaining ';'s + */ + while (*cp == ';') + cp++; + + /* + * If we have a zero length string + * and there are more fields, then + * parse the next one. This allows + * sequences of empty fields. + */ + if (*cp && dp == s) + goto top; + + *p = cp; + return s; +} + + +/* + * Strip any selectors from a string. Selectors are all assumed to be + * first in the string. This is used for the new /defaults method which will + * use selectors as well. + */ +char * +strip_selectors(char *opts, char *mapkey) +{ + /* + * Fill in the global structure fs_static by + * cracking the string opts. opts may be + * scribbled on at will. + */ + char *o = opts; + char *oo = opts; + char *f; + + /* + * Scan options. Note that the opt() function scribbles on the opt string. + */ + while (*(f = opt_no_scribble(&o))) { + enum vs_opt vs_opt = VarAss; + char *eq = strchr(f, '='); + + if (!eq || eq[1] == '\0' || eq == f) { + /* + * No option or assignment? Return as is. + */ + plog(XLOG_USER, "key %s: No option or assignment in \"%s\"", mapkey, f); + return o; + } + /* + * Check what type of operation is happening + * !=, =! is SelNE + * == is SelEQ + * := is VarAss + */ + if (*(eq-1) == '!') { /* != */ + vs_opt = SelNE; + } else if (*(eq-1) == ':') { /* := */ + vs_opt = VarAss; + } else if (eq[1] == '=') { /* == */ + vs_opt = SelEQ; + } else if (eq[1] == '!') { /* =! */ + vs_opt = SelNE; + } + switch (vs_opt) { + case SelEQ: + case SelNE: + /* Skip this selector, maybe there's another one following it */ + plog(XLOG_USER, "skipping selector to \"%s\"", o); + /* store previous match. it may have been the first assignment */ + oo = o; + break; + + case VarAss: + /* found the first assignment, return the string starting with it */ + dlog("found first assignment past selectors \"%s\"", o); + return oo; + } + } + + /* return the same string by default. should not happen. */ + return oo; +} + + +/***************************************************************************** + *** BOOLEAN FUNCTIONS (return 0 if false, 1 if true): *** + *****************************************************************************/ + +/* test if arg is any of this host's network names or numbers */ +static int +f_in_network(char *arg) +{ + int status; + + if (!arg) + return 0; + + status = is_network_member(arg); + dlog("%s is %son a local network", arg, (status ? "" : "not ")); + return status; +} + + +/* + * Test if arg is any of this host's names or aliases (CNAMES). + * Note: this function compares against the fully expanded host name (hostd). + * XXX: maybe we also need to compare against the stripped host name? + */ +static int +f_xhost(char *arg) +{ + struct hostent *hp; + char **cp; + + if (!arg) + return 0; + + /* simple test: does it match main host name? */ + if (STREQ(arg, opt_hostd)) + return 1; + + /* now find all of the names of "arg" and compare against opt_hostd */ + hp = gethostbyname(arg); + if (hp == NULL) { +#ifdef HAVE_HSTRERROR + plog(XLOG_ERROR, "gethostbyname xhost(%s): %s", arg, hstrerror(h_errno)); +#else /* not HAVE_HSTRERROR */ + plog(XLOG_ERROR, "gethostbyname xhost(%s): h_errno %d", arg, h_errno); +#endif /* not HAVE_HSTRERROR */ + return 0; + } + /* check primary name */ + if (hp->h_name) { + dlog("xhost: compare %s==%s", hp->h_name, opt_hostd); + if (STREQ(hp->h_name, opt_hostd)) { + plog(XLOG_INFO, "xhost(%s): matched h_name %s", arg, hp->h_name); + return 1; + } + } + /* check all aliases, if any */ + if (hp->h_aliases == NULL) { + dlog("gethostbyname(%s) has no aliases", arg); + return 0; + } + cp = hp->h_aliases; + while (*cp) { + dlog("xhost: compare alias %s==%s", *cp, opt_hostd); + if (STREQ(*cp, opt_hostd)) { + plog(XLOG_INFO, "xhost(%s): matched alias %s", arg, *cp); + return 1; + } + cp++; + } + /* nothing matched */ + return 0; +} + + +/* test if this host (short hostname form) is in netgroup (arg) */ +static int +f_netgrp(char *arg) +{ + int status; + char *ptr, *nhost; + + if ((ptr = strchr(arg, ',')) != NULL) { + *ptr = '\0'; + nhost = ptr + 1; + } else { + nhost = opt_host; + } + status = innetgr(arg, nhost, NULL, NULL); + dlog("netgrp = %s status = %d host = %s", arg, status, nhost); + if (ptr) + *ptr = ','; + return status; +} + + +/* test if this host (fully-qualified name) is in netgroup (arg) */ +static int +f_netgrpd(char *arg) +{ + int status; + char *ptr, *nhost; + + if ((ptr = strchr(arg, ',')) != NULL) { + *ptr = '\0'; + nhost = ptr + 1; + } else { + nhost = opt_hostd; + } + status = innetgr(arg, nhost, NULL, NULL); + dlog("netgrp = %s status = %d hostd = %s", arg, status, nhost); + if (ptr) + *ptr = ','; + return status; +} + + +/* test if file (arg) exists via lstat */ +static int +f_exists(char *arg) +{ + struct stat buf; + + if (lstat(arg, &buf) < 0) + return (0); + else + return (1); +} + + +/* always false */ +static int +f_false(char *arg) +{ + return (0); +} + + +/* always true */ +static int +f_true(char *arg) +{ + return (1); +} + + +/* + * Free an option + */ +static void +free_op(opt_apply *p, int b) +{ + if (*p->opt) { + XFREE(*p->opt); + } +} + + +/* + * Normalize slashes in the string. + */ +void +normalize_slash(char *p) +{ + char *f, *f0; + + if (!(gopt.flags & CFM_NORMALIZE_SLASHES)) + return; + + f0 = f = strchr(p, '/'); + if (f) { + char *t = f; + do { + /* assert(*f == '/'); */ + if (f == f0 && f[0] == '/' && f[1] == '/') { + /* copy double slash iff first */ + *t++ = *f++; + *t++ = *f++; + } else { + /* copy a single / across */ + *t++ = *f++; + } + + /* assert(f[-1] == '/'); */ + /* skip past more /'s */ + while (*f == '/') + f++; + + /* assert(*f != '/'); */ + /* keep copying up to next / */ + while (*f && *f != '/') { + /* support escaped slashes '\/' */ + if (f[0] == '\\' && f[1] == '/') + f++; /* skip backslash */ + *t++ = *f++; + } + + /* assert(*f == 0 || *f == '/'); */ + + } while (*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_p is true then old expand selectors, otherwise + * don't expand selectors. + */ +static char * +expand_op(char *opt, int sel_p) +{ +#define EXPAND_ERROR "No space to expand \"%s\"" + char expbuf[MAXPATHLEN + 1]; + char nbuf[NLEN + 1]; + char *ep = expbuf; + char *cp = opt; + char *dp; + struct opt *op; + char *cp_orig = opt; + + while ((dp = strchr(cp, '$'))) { + char ch; + /* + * First copy up to the $ + */ + { + int len = dp - cp; + + if (len > 0) { + if (BUFSPACE(ep, len)) { + /* + * We use strncpy (not xstrlcpy) because 'ep' relies on its + * semantics. BUFSPACE guarantees that ep can hold len. + */ + strncpy(ep, cp, len); + ep += len; + } else { + plog(XLOG_ERROR, EXPAND_ERROR, opt); + goto out; + } + } + } + + cp = dp + 1; + ch = *cp++; + if (ch == '$') { + if (BUFSPACE(ep, 1)) { + *ep++ = '$'; + } else { + plog(XLOG_ERROR, EXPAND_ERROR, opt); + goto out; + } + } else if (ch == '{') { + /* Expansion... */ + enum { + E_All, E_Dir, E_File, E_Domain, E_Host + } todo; + /* + * Find closing brace + */ + char *br_p = strchr(cp, '}'); + int len; + + /* + * Check we found it + */ + if (!br_p) { + /* + * Just give up + */ + plog(XLOG_USER, "No closing '}' in \"%s\"", opt); + goto out; + } + len = br_p - cp; + + /* + * Figure out which part of the variable to grab. + */ + if (*cp == '/') { + /* + * Just take the last component + */ + todo = E_File; + cp++; + --len; + } else if (*(br_p-1) == '/') { + /* + * Take all but the last component + */ + todo = E_Dir; + --len; + } else if (*cp == '.') { + /* + * Take domain name + */ + todo = E_Domain; + cp++; + --len; + } else if (*(br_p-1) == '.') { + /* + * Take host name + */ + todo = E_Host; + --len; + } else { + /* + * Take the whole lot + */ + todo = E_All; + } + + /* + * Truncate if too long. Since it won't + * match anyway it doesn't matter that + * it has been cut short. + */ + if (len > NLEN) + len = NLEN; + + /* + * Put the string into another buffer so + * we can do comparisons. + * + * We use strncpy here (not xstrlcpy) because the dest is meant + * to be truncated and we don't want to log it as an error. The + * use of the BUFSPACE macro above guarantees the safe use of + * strncpy with nbuf. + */ + strncpy(nbuf, cp, len); + nbuf[len] = '\0'; + + /* + * Advance cp + */ + cp = br_p + 1; + + /* + * Search the option array + */ + for (op = opt_fields; op->name; op++) { + /* + * Check for match + */ + if (len == op->nlen && STREQ(op->name, nbuf)) { + char xbuf[NLEN + 3]; + char *val; + /* + * Found expansion. Copy + * the correct value field. + */ + if (!(!op->sel_p == !sel_p)) { + /* + * Copy the string across unexpanded + */ + xsnprintf(xbuf, sizeof(xbuf), "${%s%s%s}", + todo == E_File ? "/" : + todo == E_Domain ? "." : "", + nbuf, + todo == E_Dir ? "/" : + todo == E_Host ? "." : ""); + val = xbuf; + /* + * Make sure expansion doesn't + * munge the value! + */ + todo = E_All; + } else if (op->sel_p) { + val = *op->sel_p; + } else { + val = *op->optp; + } + + if (val) { + /* + * Do expansion: + * ${/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 + */ + int vlen = strlen(val); + char *vptr = val; + switch (todo) { + case E_Dir: + vptr = strrchr(val, '/'); + if (vptr) + vlen = vptr - val; + vptr = val; + break; + case E_File: + vptr = strrchr(val, '/'); + if (vptr) { + vptr++; + vlen = strlen(vptr); + } else + vptr = val; + break; + case E_Domain: + vptr = strchr(val, '.'); + if (vptr) { + vptr++; + vlen = strlen(vptr); + } else { + vptr = ""; + vlen = 0; + } + break; + case E_Host: + vptr = strchr(val, '.'); + if (vptr) + vlen = vptr - val; + vptr = val; + break; + case E_All: + break; + } + + if (BUFSPACE(ep, vlen+1)) { + xstrlcpy(ep, vptr, vlen+1); + ep += vlen; + } else { + plog(XLOG_ERROR, EXPAND_ERROR, opt); + goto out; + } + } + /* + * Done with this variable + */ + break; + } + } + + /* + * Check that the search was successful + */ + if (!op->name) { + /* + * If it wasn't then scan the + * environment for that name + * and use any value found + */ + char *env = getenv(nbuf); + + if (env) { + int vlen = strlen(env); + + if (BUFSPACE(ep, vlen+1)) { + xstrlcpy(ep, env, vlen+1); + ep += vlen; + } else { + plog(XLOG_ERROR, EXPAND_ERROR, opt); + goto out; + } + if (amuDebug(D_STR)) + plog(XLOG_DEBUG, "Environment gave \"%s\" -> \"%s\"", nbuf, env); + } else { + plog(XLOG_USER, "Unknown sequence \"${%s}\"", nbuf); + } + } + } else { + /* + * Error, error + */ + plog(XLOG_USER, "Unknown $ sequence in \"%s\"", opt); + } + } + +out: + /* + * Handle common case - no expansion + */ + if (cp == opt) { + opt = strdup(cp); + } else { + /* + * Finish off the expansion + */ + int vlen = strlen(cp); + if (BUFSPACE(ep, vlen+1)) { + xstrlcpy(ep, cp, vlen+1); + /* ep += vlen; */ + } else { + plog(XLOG_ERROR, EXPAND_ERROR, opt); + } + + /* + * Save the expansion + */ + opt = strdup(expbuf); + } + + normalize_slash(opt); + + if (amuDebug(D_STR)) { + plog(XLOG_DEBUG, "Expansion of \"%s\"...", cp_orig); + plog(XLOG_DEBUG, "......... is \"%s\"", opt); + } + return opt; +} + + +/* + * Wrapper for expand_op + */ +static void +expand_opts(opt_apply *p, int sel_p) +{ + if (*p->opt) { + *p->opt = expand_op(*p->opt, sel_p); + } else if (p->val) { + /* + * Do double expansion, remembering + * to free the string from the first + * expansion... + */ + char *s = expand_op(p->val, TRUE); + *p->opt = expand_op(s, sel_p); + XFREE(s); + } +} + + +/* + * Apply a function to a list of options + */ +static void +apply_opts(void (*op) (opt_apply *, int), opt_apply ppp[], int b) +{ + opt_apply *pp; + + for (pp = ppp; pp->opt; pp++) + (*op) (pp, b); +} + + +/* + * Free the option table + */ +void +free_opts(am_opts *fo) +{ + /* + * Copy in the structure we are playing with + */ + fs_static = *fo; + + /* + * Free previously allocated memory + */ + apply_opts(free_op, to_free, FALSE); +} + + +/* + * Expand selectors (variables that cannot be assigned to or overridden) + */ +char * +expand_selectors(char *key) +{ + return expand_op(key, TRUE); +} + + +/* + * Expand options (i.e. non-selectors, see above for definition) + */ +static inline char * +expand_options(char *key) +{ + return expand_op(key, FALSE); +} + + +/* + * Remove trailing /'s from a string + * unless the string is a single / (Steven Glassman) + * or unless it is two slashes // (Kevin D. Bond) + * or unless amd.conf says not to touch slashes. + */ +void +deslashify(char *s) +{ + if (!(gopt.flags & CFM_NORMALIZE_SLASHES)) + return; + + if (s && *s) { + char *sl = s + strlen(s); + + while (*--sl == '/' && sl > s) + *sl = '\0'; + } +} + + +int +eval_fs_opts(am_opts *fo, char *opts, char *g_opts, char *path, char *key, char *map) +{ + int ok = TRUE; + + free_opts(fo); + + /* + * Clear out the option table + */ + memset((voidp) &fs_static, 0, sizeof(fs_static)); + memset((voidp) vars, 0, sizeof(vars)); + memset((voidp) fo, 0, sizeof(*fo)); + + /* set hostname */ + opt_host = (char *) am_get_hostname(); + + /* + * Set key, map & path before expansion + */ + opt_key = key; + opt_map = map; + opt_path = path; + + opt_dkey = strchr(key, '.'); + if (!opt_dkey) { + opt_dkey = NullStr; + opt_keyd = key; + } else { + opt_keyd = strnsave(key, opt_dkey - key); + opt_dkey++; + if (*opt_dkey == '\0') /* check for 'host.' */ + opt_dkey = NullStr; + } + + /* + * Expand global options + */ + fs_static.fs_glob = expand_selectors(g_opts); + + /* + * Expand local options + */ + fs_static.fs_local = expand_selectors(opts); + + /* break global options into fs_static fields */ + if ((ok = split_opts(fs_static.fs_glob, key))) { + dlog("global split_opts ok"); + /* + * evaluate local selectors + */ + if ((ok = eval_selectors(fs_static.fs_local, key))) { + dlog("local eval_selectors ok"); + /* if the local selectors matched, then do the local overrides */ + ok = split_opts(fs_static.fs_local, key); + if (ok) + dlog("local split_opts ok"); + } + } + + /* + * Normalize remote host name. + * 1. Expand variables + * 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 + * sharing of mount maps. + */ + 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. + */ + *fo = fs_static; + + /* + * Clear defined options + */ + if (opt_keyd != key && opt_keyd != nullstr) + XFREE(opt_keyd); + opt_keyd = nullstr; + opt_dkey = NullStr; + opt_key = opt_map = opt_path = nullstr; + + return ok; +} diff --git a/amd/readdir.c b/amd/readdir.c new file mode 100644 index 000000000000..d025c9118c30 --- /dev/null +++ b/amd/readdir.c @@ -0,0 +1,498 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/readdir.c + * + */ + + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + + +/**************************************************************************** + *** MACROS *** + ****************************************************************************/ +#define DOT_DOT_COOKIE (u_int) 1 +#define MAX_CHAIN 2048 + + +/**************************************************************************** + *** FORWARD DEFINITIONS *** + ****************************************************************************/ +static int key_already_in_chain(char *keyname, const nfsentry *chain); +static nfsentry *make_entry_chain(am_node *mp, const nfsentry *current_chain, int fully_browsable); +static int amfs_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, u_int count, int fully_browsable); + + +/**************************************************************************** + *** FUNCTIONS *** + ****************************************************************************/ +/* + * Was: NEW_TOPLVL_READDIR + * Search a chain for an entry with some name. + * -Erez Zadok <ezk@cs.columbia.edu> + */ +static int +key_already_in_chain(char *keyname, const nfsentry *chain) +{ + const nfsentry *tmpchain = chain; + + while (tmpchain) { + if (keyname && tmpchain->ne_name && STREQ(keyname, tmpchain->ne_name)) + return 1; + tmpchain = tmpchain->ne_nextentry; + } + + return 0; +} + + +/* + * Create a chain of entries which are not linked. + * -Erez Zadok <ezk@cs.columbia.edu> + */ +static nfsentry * +make_entry_chain(am_node *mp, const nfsentry *current_chain, int fully_browsable) +{ + static u_int last_cookie = (u_int) 2; /* monotonically increasing */ + static nfsentry chain[MAX_CHAIN]; + static int max_entries = MAX_CHAIN; + char *key; + int num_entries = 0, i; + u_int preflen = 0; + nfsentry *retval = (nfsentry *) NULL; + mntfs *mf; + mnt_map *mmp; + + if (!mp) { + plog(XLOG_DEBUG, "make_entry_chain: mp is (NULL)"); + return retval; + } + mf = mp->am_mnt; + if (!mf) { + plog(XLOG_DEBUG, "make_entry_chain: mp->am_mnt is (NULL)"); + return retval; + } + mmp = (mnt_map *) mf->mf_private; + if (!mmp) { + plog(XLOG_DEBUG, "make_entry_chain: mp->am_mnt->mf_private is (NULL)"); + return retval; + } + + if (mp->am_pref) + preflen = strlen(mp->am_pref); + + /* iterate over keys */ + for (i = 0; i < NKVHASH; i++) { + kv *k; + for (k = mmp->kvhash[i]; k ; k = k->next) { + + /* + * Skip unwanted entries which are either not real entries or + * very difficult to interpret (wildcards...) This test needs + * lots of improvement. Any takers? + */ + key = k->key; + if (!key) + continue; + + /* Skip '/defaults' */ + if (STREQ(key, "/defaults")) + continue; + + /* Skip '*' */ + if (!fully_browsable && strchr(key, '*')) + continue; + + /* + * If the map has a prefix-string then check if the key starts with + * this string, and if it does, skip over this prefix. If it has a + * prefix and it doesn't match the start of the key, skip it. + */ + if (preflen) { + if (preflen > strlen(key)) + continue; + if (!NSTREQ(key, mp->am_pref, preflen)) + continue; + key += preflen; + } + + /* no more '/' are allowed, unless browsable_dirs=full was used */ + if (!fully_browsable && strchr(key, '/')) + continue; + + /* no duplicates allowed */ + if (key_already_in_chain(key, current_chain)) + continue; + + /* fill in a cell and link the entry */ + if (num_entries >= max_entries) { + /* out of space */ + plog(XLOG_DEBUG, "make_entry_chain: no more space in chain"); + if (num_entries > 0) { + chain[num_entries - 1].ne_nextentry = 0; + retval = &chain[0]; + } + return retval; + } + + /* we have space. put entry in next cell */ + ++last_cookie; + chain[num_entries].ne_fileid = (u_int) last_cookie; + *(u_int *) chain[num_entries].ne_cookie = (u_int) last_cookie; + chain[num_entries].ne_name = key; + if (num_entries < max_entries - 1) { /* link to next one */ + chain[num_entries].ne_nextentry = &chain[num_entries + 1]; + } + ++num_entries; + } /* end of "while (k)" */ + } /* end of "for (i ... NKVHASH ..." */ + + /* terminate chain */ + if (num_entries > 0) { + chain[num_entries - 1].ne_nextentry = 0; + retval = &chain[0]; + } + + return retval; +} + + + +/* This one is called only if map is browsable */ +static int +amfs_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, u_int count, int fully_browsable) +{ + u_int gen = *(u_int *) cookie; + int chain_length, i; + static nfsentry *te, *te_next; + static int j; + + dp->dl_eof = FALSE; /* assume readdir not done */ + + if (amuDebug(D_READDIR)) + plog(XLOG_DEBUG, "amfs_readdir_browsable gen=%u, count=%d", + gen, count); + + if (gen == 0) { + /* + * In the default instance (which is used to start a search) we return + * "." and "..". + * + * This assumes that the count is big enough to allow both "." and ".." + * to be returned in a single packet. If it isn't (which would be + * fairly unbelievable) then tough. + */ + dlog("amfs_readdir_browsable: default search"); + /* + * Check for enough room. This is extremely approximate but is more + * than enough space. Really need 2 times: + * 4byte fileid + * 4byte cookie + * 4byte name length + * 4byte name + * plus the dirlist structure */ + if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp)))) + return EINVAL; + + /* + * compute # of entries to send in this chain. + * heuristics: 128 bytes per entry. + * This is too much probably, but it seems to work better because + * of the re-entrant nature of nfs_readdir, and esp. on systems + * like OpenBSD 2.2. + */ + chain_length = count / 128; + + /* reset static state counters */ + te = te_next = NULL; + + dp->dl_entries = ep; + + /* construct "." */ + ep[0].ne_fileid = mp->am_gen; + ep[0].ne_name = "."; + ep[0].ne_nextentry = &ep[1]; + *(u_int *) ep[0].ne_cookie = 0; + + /* construct ".." */ + if (mp->am_parent) + ep[1].ne_fileid = mp->am_parent->am_gen; + else + ep[1].ne_fileid = mp->am_gen; + + ep[1].ne_name = ".."; + ep[1].ne_nextentry = 0; + *(u_int *) ep[1].ne_cookie = DOT_DOT_COOKIE; + + /* + * If map is browsable, call a function make_entry_chain() to construct + * a linked list of unmounted keys, and return it. Then link the chain + * to the regular list. Get the chain only once, but return + * chunks of it each time. + */ + te = make_entry_chain(mp, dp->dl_entries, fully_browsable); + if (!te) + return 0; + if (amuDebug(D_READDIR)) { + nfsentry *ne; + for (j = 0, ne = te; ne; ne = ne->ne_nextentry) + plog(XLOG_DEBUG, "gen1 key %4d \"%s\"", j++, ne->ne_name); + } + + /* return only "chain_length" entries */ + te_next = te; + for (i=1; i<chain_length; ++i) { + te_next = te_next->ne_nextentry; + if (!te_next) + break; + } + if (te_next) { + nfsentry *te_saved = te_next->ne_nextentry; + te_next->ne_nextentry = NULL; /* terminate "te" chain */ + te_next = te_saved; /* save rest of "te" for next iteration */ + dp->dl_eof = FALSE; /* tell readdir there's more */ + } else { + dp->dl_eof = TRUE; /* tell readdir that's it */ + } + ep[1].ne_nextentry = te; /* append this chunk of "te" chain */ + if (amuDebug(D_READDIR)) { + nfsentry *ne; + for (j = 0, ne = te; ne; ne = ne->ne_nextentry) + plog(XLOG_DEBUG, "gen2 key %4d \"%s\"", j++, ne->ne_name); + for (j = 0, ne = ep; ne; ne = ne->ne_nextentry) + plog(XLOG_DEBUG, "gen2+ key %4d \"%s\" fi=%d ck=%d", + j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie); + plog(XLOG_DEBUG, "EOF is %d", dp->dl_eof); + } + return 0; + } /* end of "if (gen == 0)" statement */ + + dlog("amfs_readdir_browsable: real child"); + + if (gen == DOT_DOT_COOKIE) { + dlog("amfs_readdir_browsable: End of readdir in %s", mp->am_path); + dp->dl_eof = TRUE; + dp->dl_entries = 0; + return 0; + } + + /* + * If browsable directories, then continue serving readdir() with another + * chunk of entries, starting from where we left off (when gen was equal + * to 0). Once again, assume last chunk served to readdir. + */ + dp->dl_eof = TRUE; + dp->dl_entries = ep; + + te = te_next; /* reset 'te' from last saved te_next */ + if (!te) { /* another indicator of end of readdir */ + dp->dl_entries = 0; + return 0; + } + /* + * compute # of entries to send in this chain. + * heuristics: 128 bytes per entry. + */ + chain_length = count / 128; + + /* return only "chain_length" entries */ + for (i = 1; i < chain_length; ++i) { + te_next = te_next->ne_nextentry; + if (!te_next) + break; + } + if (te_next) { + nfsentry *te_saved = te_next->ne_nextentry; + te_next->ne_nextentry = NULL; /* terminate "te" chain */ + te_next = te_saved; /* save rest of "te" for next iteration */ + dp->dl_eof = FALSE; /* tell readdir there's more */ + } + ep = te; /* send next chunk of "te" chain */ + dp->dl_entries = ep; + if (amuDebug(D_READDIR)) { + nfsentry *ne; + plog(XLOG_DEBUG, "dl_entries=%p, te_next=%p, dl_eof=%d", + dp->dl_entries, te_next, dp->dl_eof); + for (ne = te; ne; ne = ne->ne_nextentry) + plog(XLOG_DEBUG, "gen3 key %4d \"%s\"", j++, ne->ne_name); + } + return 0; +} + + +/* + * This readdir function which call a special version of it that allows + * browsing if browsable_dirs=yes was set on the map. + */ +int +amfs_generic_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, u_int count) +{ + u_int gen = *(u_int *) cookie; + am_node *xp; + mntent_t mnt; + + dp->dl_eof = FALSE; /* assume readdir not done */ + + /* check if map is browsable */ + if (mp->am_mnt && mp->am_mnt->mf_mopts) { + mnt.mnt_opts = mp->am_mnt->mf_mopts; + if (amu_hasmntopt(&mnt, "fullybrowsable")) + return amfs_readdir_browsable(mp, cookie, dp, ep, count, TRUE); + if (amu_hasmntopt(&mnt, "browsable")) + return amfs_readdir_browsable(mp, cookie, dp, ep, count, FALSE); + } + + /* when gen is 0, we start reading from the beginning of the directory */ + if (gen == 0) { + /* + * In the default instance (which is used to start a search) we return + * "." and "..". + * + * This assumes that the count is big enough to allow both "." and ".." + * to be returned in a single packet. If it isn't (which would be + * fairly unbelievable) then tough. + */ + dlog("amfs_generic_readdir: default search"); + /* + * Check for enough room. This is extremely approximate but is more + * than enough space. Really need 2 times: + * 4byte fileid + * 4byte cookie + * 4byte name length + * 4byte name + * plus the dirlist structure */ + if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp)))) + return EINVAL; + + xp = next_nonerror_node(mp->am_child); + dp->dl_entries = ep; + + /* construct "." */ + ep[0].ne_fileid = mp->am_gen; + ep[0].ne_name = "."; + ep[0].ne_nextentry = &ep[1]; + *(u_int *) ep[0].ne_cookie = 0; + + /* construct ".." */ + if (mp->am_parent) + ep[1].ne_fileid = mp->am_parent->am_gen; + else + ep[1].ne_fileid = mp->am_gen; + ep[1].ne_name = ".."; + ep[1].ne_nextentry = 0; + *(u_int *) ep[1].ne_cookie = (xp ? xp->am_gen : DOT_DOT_COOKIE); + + if (!xp) + dp->dl_eof = TRUE; /* by default assume readdir done */ + + if (amuDebug(D_READDIR)) { + nfsentry *ne; + int j; + for (j = 0, ne = ep; ne; ne = ne->ne_nextentry) + plog(XLOG_DEBUG, "gen1 key %4d \"%s\" fi=%d ck=%d", + j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie); + } + return 0; + } + dlog("amfs_generic_readdir: real child"); + + if (gen == DOT_DOT_COOKIE) { + dlog("amfs_generic_readdir: End of readdir in %s", mp->am_path); + dp->dl_eof = TRUE; + dp->dl_entries = 0; + if (amuDebug(D_READDIR)) + plog(XLOG_DEBUG, "end of readdir eof=TRUE, dl_entries=0\n"); + return 0; + } + + /* non-browsable directories code */ + xp = mp->am_child; + while (xp && xp->am_gen != gen) + xp = xp->am_osib; + + if (xp) { + int nbytes = count / 2; /* conservative */ + int todo = MAX_READDIR_ENTRIES; + + dp->dl_entries = ep; + do { + am_node *xp_next = next_nonerror_node(xp->am_osib); + + if (xp_next) { + *(u_int *) ep->ne_cookie = xp_next->am_gen; + } else { + *(u_int *) ep->ne_cookie = DOT_DOT_COOKIE; + dp->dl_eof = TRUE; + } + + ep->ne_fileid = xp->am_gen; + ep->ne_name = xp->am_name; + nbytes -= sizeof(*ep) + 1; + if (xp->am_name) + nbytes -= strlen(xp->am_name); + + xp = xp_next; + + if (nbytes > 0 && !dp->dl_eof && todo > 1) { + ep->ne_nextentry = ep + 1; + ep++; + --todo; + } else { + todo = 0; + } + } while (todo > 0); + + ep->ne_nextentry = 0; + + if (amuDebug(D_READDIR)) { + nfsentry *ne; + int j; + for (j=0,ne=ep; ne; ne=ne->ne_nextentry) + plog(XLOG_DEBUG, "gen2 key %4d \"%s\" fi=%d ck=%d", + j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie); + } + return 0; + } + return ESTALE; +} diff --git a/amd/restart.c b/amd/restart.c new file mode 100644 index 000000000000..4f71e381e25a --- /dev/null +++ b/amd/restart.c @@ -0,0 +1,288 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/restart.c + * + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + + +static void +restart_fake_mntfs(mntent_t *me, am_ops *fs_ops) +{ + mntfs *mf; + am_opts mo; + char *cp; + + /* + * Partially fake up an opts structure + */ + memset(&mo, 0, sizeof(mo)); + mo.opt_rhost = 0; + mo.opt_rfs = 0; + cp = strchr(me->mnt_fsname, ':'); + if (cp) { + *cp = '\0'; + mo.opt_rhost = strdup(me->mnt_fsname); + mo.opt_rfs = strdup(cp + 1); + *cp = ':'; + } else if (STREQ(me->mnt_type, MNTTAB_TYPE_NFS)) { + /* + * Hacky workaround for mnttab NFS entries that only list the server + */ + plog(XLOG_WARNING, "NFS server entry assumed to be %s:/", me->mnt_fsname); + mo.opt_rhost = strdup(me->mnt_fsname); + mo.opt_rfs = strdup("/"); + me->mnt_fsname = str3cat(me->mnt_fsname, mo.opt_rhost, ":", "/"); + } + mo.opt_fs = me->mnt_dir; + mo.opt_opts = me->mnt_opts; + + /* + * Make a new mounted filesystem + */ + mf = find_mntfs(fs_ops, &mo, me->mnt_dir, + me->mnt_fsname, "", me->mnt_opts, ""); + if (mf->mf_refc == 1) { + mf->mf_flags |= MFF_RESTART | MFF_MOUNTED; + mf->mf_error = 0; /* Already mounted correctly */ + mf->mf_fo = 0; + /* + * Only timeout non-NFS entries + */ + if (!STREQ(me->mnt_type, MNTTAB_TYPE_NFS)) + mf->mf_flags |= MFF_RSTKEEP; + if (fs_ops->fs_init) { + /* + * Don't care whether this worked since + * it is checked again when the fs is + * inherited. + */ + (void) (*fs_ops->fs_init) (mf); + } + plog(XLOG_INFO, "%s restarted fstype %s on %s, flags 0x%x", + me->mnt_fsname, fs_ops->fs_type, me->mnt_dir, mf->mf_flags); + } else { + /* Something strange happened - two mounts at the same place! */ + free_mntfs(mf); + } + /* + * Clean up mo + */ + if (mo.opt_rhost) + XFREE(mo.opt_rhost); + if (mo.opt_rfs) + XFREE(mo.opt_rfs); +} + + +/* + * Handle an amd restart. + * + * Scan through the mount list finding all "interesting" mount points. + * Next hack up partial data structures and add the mounted file + * system to the list of known filesystems. + * + * This module relies on internal details of other components. If + * you change something else make *sure* restart() still works. + */ +void +restart(void) +{ + mntlist *ml, *mlp; + + /* + * Read the existing mount table. For each entry, find nfs, ufs or auto + * mounts and create a partial am_node to represent it. + */ + for (mlp = ml = read_mtab("restart", mnttab_file_name); + mlp; + mlp = mlp->mnext) { + mntent_t *me = mlp->mnt; + am_ops *fs_ops = 0; + + if (STREQ(me->mnt_type, MNTTAB_TYPE_NFS)) { + /* + * NFS entry, or possibly an Amd entry... + * The mnt_fsname for daemon mount points is + * host:(pidXXX) + * or (seen on Solaris) + * host:daemon(pidXXX) + */ + char *colon = strchr(me->mnt_fsname, ':'); + if (colon && strstr(colon, "(pid")) + continue; + } + + /* Search for the correct filesystem ops */ + fs_ops = ops_search(me->mnt_type); + + /* + * Catch everything else with symlinks to + * avoid recursive mounts. This is debatable... + */ + if (!fs_ops) + fs_ops = &amfs_link_ops; + + restart_fake_mntfs(me, fs_ops); + } + + /* + * Free the mount list + */ + free_mntlist(ml); +} + + +/* + * Handle an amd restart for amd's own mount points. + * + * Scan through the mount list finding all daemon mount points + * (determined by the presence of a pid inside the mount info). + * Next hack up partial data structures and add the mounted file + * system to the list of known filesystems. + * + * This module relies on internal details of other components. If + * you change something else make *sure* restart() still works. + */ +void +restart_automounter_nodes(void) +{ + mntlist *ml, *mlp; + /* reasonably sized list of restarted nfs ports */ + u_short old_ports[256]; + + memset((voidp) &old_ports, 0, sizeof(u_short) * 256); + + /* + * Read the existing mount table. For each entry, find nfs, ufs or auto + * mounts and create a partial am_node to represent it. + */ + for (mlp = ml = read_mtab("restart", mnttab_file_name); + mlp; + mlp = mlp->mnext) { + mntent_t *me = mlp->mnt; + am_ops *fs_ops = 0; + char *colon; + long pid; + u_short port; + int err; + + if (!STREQ(me->mnt_type, MNTTAB_TYPE_NFS)) + continue; /* to next mlp */ + /* + * NFS entry, or possibly an Amd entry... + * The mnt_fsname for daemon mount points is + * host:(pidXXX) + * or (seen on Solaris) + * host:daemon(pidXXX) + */ + colon = strchr(me->mnt_fsname, ':'); + if (!colon || !strstr(colon, "(pid")) + continue; + /* if got here, then we matched an existing Amd mount point */ + err = 1; + + plog(XLOG_WARNING, "%s is an existing automount point", me->mnt_dir); + + /* Is the old automounter still alive? */ + if (sscanf(colon, "%*[^(](pid%ld%*[,)]", &pid) != 1) { + plog(XLOG_WARNING, "Can't parse pid in %s", me->mnt_fsname); + goto give_up; + } + if (kill(pid, 0) != -1 || errno != ESRCH) { + plog(XLOG_WARNING, "Automounter (pid: %ld) still alive", pid); + goto give_up; + } + + /* + * Do we have a map for this mount point? Who cares, we'll restart + * anyway -- getting ESTALE is way better than hanging. + */ + + /* Can we restart it? Only if it tells us what port it was using... */ + if (sscanf(colon, "%*[^,],port%hu)", &port) != 1) { + plog(XLOG_WARNING, "No port specified for %s", me->mnt_fsname); + goto give_up; + } + + /* Maybe we already own that port... */ + if (port != nfs_port) { + int i; + for (i = 0; i < 256; i++) { + if (old_ports[i] == port || + old_ports[i] == 0) + break; + } + if (i == 256) { + plog(XLOG_WARNING, "Too many open ports (256)"); + goto give_up; + } + + if (old_ports[i] == 0) { + int soNFS; + SVCXPRT *nfsxprt; + if (create_nfs_service(&soNFS, &port, &nfsxprt, nfs_program_2) != 0) { + plog(XLOG_WARNING, "Can't bind to port %u", port); + goto give_up; + } + old_ports[i] = nfs_port = port; + } + } + err = 0; + + give_up: + if (err) { + plog(XLOG_WARNING, "Can't restart %s, leaving it alone", me->mnt_dir); + fs_ops = &amfs_link_ops; + } else { + fs_ops = &amfs_toplvl_ops; + } + + restart_fake_mntfs(me, fs_ops); + } /* end of "for (mlp" */ + + /* free the mount list */ + free_mntlist(ml); +} diff --git a/amd/rpc_fwd.c b/amd/rpc_fwd.c new file mode 100644 index 000000000000..b3c8be4fa3b7 --- /dev/null +++ b/amd/rpc_fwd.c @@ -0,0 +1,478 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * 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. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/rpc_fwd.c + * + */ + +/* + * RPC packet forwarding + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* + * Note that the ID field in the external packet is only + * ever treated as a 32 bit opaque data object, so there + * is no need to convert to and from network byte ordering. + */ + +#define XID_ALLOC() (xid++) +#define MAX_PACKET_SIZE 8192 /* Maximum UDP packet size */ + +/* + * Each pending reply has an rpc_forward structure + * associated with it. These have a 15 second lifespan. + * If a new structure is required, then an expired + * one will be re-allocated if available, otherwise a fresh + * one is allocated. Whenever a reply is received the + * structure is discarded. + */ +typedef struct rpc_forward rpc_forward; +struct rpc_forward { + qelem rf_q; /* Linked list */ + time_t rf_ttl; /* Time to live */ + u_int rf_xid; /* Packet id */ + u_int rf_oldid; /* Original packet id */ + fwd_fun *rf_fwd; /* Forwarding function */ + voidp rf_ptr; + struct sockaddr_in rf_sin; +}; + +/* + * Head of list of pending replies + */ +qelem rpc_head = {&rpc_head, &rpc_head}; +int fwd_sock; +static u_int xid; + + +/* + * Allocate a rely structure + */ +static rpc_forward * +fwd_alloc(void) +{ + time_t now = clocktime(NULL); + rpc_forward *p = 0, *p2; + + /* + * First search for an existing expired one. + */ + ITER(p2, rpc_forward, &rpc_head) { + if (p2->rf_ttl <= now) { + p = p2; + break; + } + } + + /* + * If one couldn't be found then allocate + * a new structure and link it at the + * head of the list. + */ + if (p) { + /* + * Call forwarding function to say that + * this message was junked. + */ + dlog("Re-using packet forwarding slot - id %#x", p->rf_xid); + if (p->rf_fwd) + (*p->rf_fwd) (0, 0, 0, &p->rf_sin, p->rf_ptr, FALSE); + rem_que(&p->rf_q); + } else { + p = ALLOC(struct rpc_forward); + } + ins_que(&p->rf_q, &rpc_head); + + /* + * Set the time to live field + * Timeout in 43 seconds + */ + p->rf_ttl = now + 43; + + return p; +} + + +/* + * Free an allocated reply structure. + * First unlink it from the list, then + * discard it. + */ +static void +fwd_free(rpc_forward *p) +{ + rem_que(&p->rf_q); + XFREE(p); +} + + +/* + * Initialize the RPC forwarder + */ +int +fwd_init(void) +{ +#ifdef FIONBIO + int on = 1; +#endif /* FIONBIO */ + +#ifdef HAVE_TRANSPORT_TYPE_TLI + /* + * Create ping TLI socket (/dev/tcp and /dev/ticlts did not work) + * (HPUX-11 does not like using O_NDELAY in flags) + */ + fwd_sock = t_open("/dev/udp", O_RDWR|O_NONBLOCK, 0); + if (fwd_sock < 0) { + plog(XLOG_ERROR, "unable to create RPC forwarding TLI socket: %s", + t_errlist[t_errno]); + return errno; + } +#else /* not HAVE_TRANSPORT_TYPE_TLI */ + /* + * Create ping socket + */ + fwd_sock = socket(AF_INET, SOCK_DGRAM, 0); + if (fwd_sock < 0) { + plog(XLOG_ERROR, "unable to create RPC forwarding socket: %m"); + return errno; + } +#endif /* not HAVE_TRANSPORT_TYPE_TLI */ + + /* + * Some things we talk to require a priv port - so make one here + */ + if (bind_resv_port(fwd_sock, (u_short *) 0) < 0) + plog(XLOG_ERROR, "can't bind privileged port (rpc_fwd)"); + + if (fcntl(fwd_sock, F_SETFL, FNDELAY) < 0 +#ifdef FIONBIO + && ioctl(fwd_sock, FIONBIO, &on) < 0 +#endif /* FIONBIO */ + ) { + plog(XLOG_ERROR, "Can't set non-block on forwarding socket: %m"); + return errno; + } + + return 0; +} + + +/* + * Locate a packet in the forwarding list + */ +static rpc_forward * +fwd_locate(u_int id) +{ + rpc_forward *p; + + ITER(p, rpc_forward, &rpc_head) { + if (p->rf_xid == id) + return p; + } + + return 0; +} + + +/* + * This is called to forward a packet to another + * RPC server. The message id is changed and noted + * so that when a reply appears we can tie it up + * correctly. Just matching the reply's source address + * would not work because it might come from a + * different address. + */ +int +fwd_packet(int type_id, char *pkt, int len, struct sockaddr_in *fwdto, struct sockaddr_in *replyto, opaque_t cb_arg, fwd_fun cb) +{ + rpc_forward *p; + u_int *pkt_int; + int error; +#ifdef HAVE_TRANSPORT_TYPE_TLI + struct t_unitdata ud; +#endif /* HAVE_TRANSPORT_TYPE_TLI */ + + if ((int) amd_state >= (int) Finishing) + return ENOENT; + + /* + * See if the type_id is fully specified. + * If so, then discard any old entries + * for this id. + * Otherwise make sure the type_id is + * fully qualified by allocating an id here. + */ + switch (type_id & RPC_XID_MASK) { + case RPC_XID_PORTMAP: + dlog("Sending PORTMAP request %#x", type_id); + break; + case RPC_XID_MOUNTD: + dlog("Sending MOUNTD request %#x", type_id); + break; + case RPC_XID_NFSPING: + dlog("Sending NFS ping %#x", type_id); + break; + case RPC_XID_WEBNFS: + dlog("Sending WebNFS lookup %#x", type_id); + break; + default: + dlog("UNKNOWN RPC XID %#x", type_id); + break; + } + + if (type_id & ~RPC_XID_MASK) { + p = fwd_locate(type_id); + if (p) { + dlog("Discarding earlier rpc fwd handle"); + fwd_free(p); + } + } else { + dlog("Allocating a new xid..."); + type_id = MK_RPC_XID(type_id, XID_ALLOC()); + } + + p = fwd_alloc(); + if (!p) + return ENOBUFS; + + error = 0; + + pkt_int = (u_int *) pkt; + + /* + * Get the original packet id + */ + p->rf_oldid = ntohl(*pkt_int); + + /* + * Replace with newly allocated id + */ + p->rf_xid = type_id; + *pkt_int = htonl(type_id); + + /* + * The sendto may fail if, for example, the route + * to a remote host is lost because an intermediate + * gateway has gone down. Important to fill in the + * rest of "p" otherwise nasty things happen later... + */ +#ifdef DEBUG + { + char dq[20]; + if (p && fwdto) + dlog("Sending packet id %#x to %s:%d", + p->rf_xid, + inet_dquad(dq, sizeof(dq), fwdto->sin_addr.s_addr), + ntohs(fwdto->sin_port)); + } +#endif /* DEBUG */ + + /* if NULL, remote server probably down */ + if (!fwdto) { + error = AM_ERRNO_HOST_DOWN; + goto out; + } + +#ifdef HAVE_TRANSPORT_TYPE_TLI + ud.addr.buf = (char *) fwdto; + if (fwdto) /* if NULL, set sizes to zero */ + ud.addr.maxlen = ud.addr.len = sizeof(struct sockaddr_in); + else + ud.addr.maxlen = ud.addr.len = 0; + ud.opt.buf = (char *) NULL; + ud.opt.maxlen = ud.opt.len = 0; + ud.udata.buf = pkt; + ud.udata.maxlen = ud.udata.len = len; + if (t_sndudata(fwd_sock, &ud) < 0) { + plog(XLOG_ERROR,"fwd_packet failed: t_errno=%d, errno=%d",t_errno,errno); + error = errno; + } +#else /* not HAVE_TRANSPORT_TYPE_TLI */ + if (sendto(fwd_sock, (char *) pkt, len, 0, + (struct sockaddr *) fwdto, sizeof(*fwdto)) < 0) + error = errno; +#endif /* not HAVE_TRANSPORT_TYPE_TLI */ + + /* + * Save callback function and return address + */ +out: + p->rf_fwd = cb; + if (replyto) + p->rf_sin = *replyto; + else + memset((voidp) &p->rf_sin, 0, sizeof(p->rf_sin)); + p->rf_ptr = cb_arg; + + return error; +} + + +/* + * Called when some data arrives on the forwarding socket + */ +void +fwd_reply(void) +{ + int len; + u_int pkt[MAX_PACKET_SIZE / sizeof(u_int) + 1]; + u_int *pkt_int; + u_int pkt_xid; + int rc; + rpc_forward *p; + struct sockaddr_in src_addr; + RECVFROM_FROMLEN_TYPE src_addr_len; +#ifdef HAVE_TRANSPORT_TYPE_TLI + struct t_unitdata ud; + int flags = 0; +#endif /* HAVE_TRANSPORT_TYPE_TLI */ + + /* + * Determine the length of the packet + */ + len = MAX_PACKET_SIZE; + + /* + * Read the packet and check for validity + */ +again: + src_addr_len = sizeof(src_addr); +#ifdef HAVE_TRANSPORT_TYPE_TLI + ud.addr.buf = (char *) &src_addr; + ud.addr.maxlen = ud.addr.len = src_addr_len; + ud.opt.buf = (char *) NULL; + ud.opt.maxlen = ud.opt.len = 0; + ud.udata.buf = (char *) pkt; + ud.udata.maxlen = ud.udata.len = len; + /* XXX: use flags accordingly such as if T_MORE set */ + rc = t_rcvudata(fwd_sock, &ud, &flags); + if (rc == 0) /* success, reset rc to length */ + rc = ud.udata.len; + else { + plog(XLOG_ERROR,"fwd_reply failed: t_errno=%d, errno=%d, flags=%d",t_errno,errno, flags); + /* + * Clear error indication, otherwise the error condition persists and + * amd gets into an infinite loop. + */ + if (t_errno == TLOOK) + t_rcvuderr(fwd_sock, NULL); + } +#else /* not HAVE_TRANSPORT_TYPE_TLI */ + rc = recvfrom(fwd_sock, + (char *) pkt, + len, + 0, + (struct sockaddr *) &src_addr, + &src_addr_len); +#endif /* not HAVE_TRANSPORT_TYPE_TLI */ + + /* + * XXX: in svr4, if the T_MORE bit of flags is set, what do + * we then do? -Erez + */ + if (rc < 0 || src_addr_len != sizeof(src_addr) || + src_addr.sin_family != AF_INET) { + if (rc < 0 && errno == EINTR) + goto again; + plog(XLOG_ERROR, "Error reading RPC reply: %m"); + goto out; + } + + /* + * Do no more work if finishing soon + */ + if ((int) amd_state >= (int) Finishing) + goto out; + + /* + * Find packet reference + */ + pkt_int = (u_int *) pkt; + pkt_xid = ntohl(*pkt_int); + + switch (pkt_xid & RPC_XID_MASK) { + case RPC_XID_PORTMAP: + dlog("Receiving PORTMAP reply %#x", pkt_xid); + break; + case RPC_XID_MOUNTD: + dlog("Receiving MOUNTD reply %#x", pkt_xid); + break; + case RPC_XID_NFSPING: + dlog("Receiving NFS ping %#x", pkt_xid); + break; + case RPC_XID_WEBNFS: + dlog("Receiving WebNFS lookup %#x", pkt_xid); + break; + default: + dlog("UNKNOWN RPC XID %#x", pkt_xid); + break; + } + + p = fwd_locate(pkt_xid); + if (!p) { + dlog("Can't forward reply id %#x", pkt_xid); + goto out; + } + + if (p->rf_fwd) { + /* + * Put the original message id back + * into the packet. + */ + *pkt_int = htonl(p->rf_oldid); + + /* + * Call forwarding function + */ + (*p->rf_fwd) ((voidp) pkt, rc, &src_addr, &p->rf_sin, p->rf_ptr, TRUE); + } + + /* + * Free forwarding info + */ + fwd_free(p); + +out:; +} diff --git a/amd/sched.c b/amd/sched.c new file mode 100644 index 000000000000..8efe57a3ab81 --- /dev/null +++ b/amd/sched.c @@ -0,0 +1,304 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/sched.c + * + */ + +/* + * Process scheduler + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + + +typedef struct pjob pjob; + +struct pjob { + qelem hdr; /* Linked list */ + int pid; /* Process ID of job */ + cb_fun *cb_fun; /* Callback function */ + opaque_t cb_arg; /* Argument for callback */ + int w; /* everyone these days uses int, not a "union wait" */ + wchan_t wchan; /* Wait channel */ +}; + +/* globals */ +qelem proc_list_head = {&proc_list_head, &proc_list_head}; +qelem proc_wait_list = {&proc_wait_list, &proc_wait_list}; +int task_notify_todo; + + +void +ins_que(qelem *elem, qelem *pred) +{ + qelem *p = pred->q_forw; + + elem->q_back = pred; + elem->q_forw = p; + pred->q_forw = elem; + p->q_back = elem; +} + + +void +rem_que(qelem *elem) +{ + qelem *p = elem->q_forw; + qelem *p2 = elem->q_back; + + p2->q_forw = p; + p->q_back = p2; +} + + +static pjob * +sched_job(cb_fun *cf, opaque_t ca) +{ + pjob *p = ALLOC(struct pjob); + + p->cb_fun = cf; + p->cb_arg = ca; + + /* + * Now place on wait queue + */ + ins_que(&p->hdr, &proc_wait_list); + + return p; +} + + +/* + * tf: The task to execute (ta is its arguments) + * cf: Continuation function (ca is its arguments) + */ +void +run_task(task_fun *tf, opaque_t ta, cb_fun *cf, opaque_t ca) +{ + pjob *p = sched_job(cf, ca); +#ifdef HAVE_SIGACTION + sigset_t new, mask; +#else /* not HAVE_SIGACTION */ + int mask; +#endif /* not HAVE_SIGACTION */ + + p->wchan = (wchan_t) p; + +#ifdef HAVE_SIGACTION + sigemptyset(&new); /* initialize signal set we wish to block */ + sigaddset(&new, SIGCHLD); /* only block on SIGCHLD */ + sigprocmask(SIG_BLOCK, &new, &mask); +#else /* not HAVE_SIGACTION */ + mask = sigblock(sigmask(SIGCHLD)); +#endif /* not HAVE_SIGACTION */ + + if ((p->pid = background())) { +#ifdef HAVE_SIGACTION + sigprocmask(SIG_SETMASK, &mask, NULL); +#else /* not HAVE_SIGACTION */ + sigsetmask(mask); +#endif /* not HAVE_SIGACTION */ + return; + } + + /* child code runs here, parent has returned to caller */ + + exit((*tf) (ta)); + /* firewall... */ + abort(); +} + + +/* + * Schedule a task to be run when woken up + */ +void +sched_task(cb_fun *cf, opaque_t ca, wchan_t wchan) +{ + /* + * Allocate a new task + */ + pjob *p = sched_job(cf, ca); + + dlog("SLEEP on %p", wchan); + p->wchan = wchan; + p->pid = 0; + p->w = 0; /* was memset (when ->w was union) */ +} + + +static void +wakeupjob(pjob *p) +{ + rem_que(&p->hdr); + ins_que(&p->hdr, &proc_list_head); + task_notify_todo++; +} + + +void +wakeup(wchan_t wchan) +{ + pjob *p, *p2; + + if (!foreground) + return; + + /* + * Can't use ITER() here because + * wakeupjob() juggles the list. + */ + for (p = AM_FIRST(pjob, &proc_wait_list); + p2 = NEXT(pjob, p), p != HEAD(pjob, &proc_wait_list); + p = p2) { + if (p->wchan == wchan) { + wakeupjob(p); + } + } +} + + +void +wakeup_task(int rc, int term, wchan_t wchan) +{ + wakeup(wchan); +} + + +wchan_t +get_mntfs_wchan(mntfs *mf) +{ + if (mf && + mf->mf_ops && + mf->mf_ops->get_wchan) + return mf->mf_ops->get_wchan(mf); + return mf; +} + + +/* + * Run any pending tasks. + * This must be called with SIGCHLD disabled + */ +void +do_task_notify(void) +{ + /* + * Keep taking the first item off the list and processing it. + * + * Done this way because the callback can, quite reasonably, + * queue a new task, so no local reference into the list can be + * held here. + */ + while (AM_FIRST(pjob, &proc_list_head) != HEAD(pjob, &proc_list_head)) { + pjob *p = AM_FIRST(pjob, &proc_list_head); + rem_que(&p->hdr); + /* + * This job has completed + */ + --task_notify_todo; + + /* + * Do callback if it exists + */ + if (p->cb_fun) { + /* these two trigraphs will ensure compatibility with strict POSIX.1 */ + p->cb_fun(WIFEXITED(p->w) ? WEXITSTATUS(p->w) : 0, + WIFSIGNALED(p->w) ? WTERMSIG(p->w) : 0, + p->cb_arg); + } + XFREE(p); + } +} + + +RETSIGTYPE +sigchld(int sig) +{ + int w; /* everyone these days uses int, not a "union wait" */ + int pid; + +#ifdef HAVE_WAITPID + while ((pid = waitpid((pid_t) -1, &w, WNOHANG)) > 0) { +#else /* not HAVE_WAITPID */ + while ((pid = wait3( &w, WNOHANG, (struct rusage *) 0)) > 0) { +#endif /* not HAVE_WAITPID */ + pjob *p, *p2; + + if (WIFSIGNALED(w)) + plog(XLOG_ERROR, "Process %d exited with signal %d", + pid, WTERMSIG(w)); + else + dlog("Process %d exited with status %d", + pid, WEXITSTATUS(w)); + + for (p = AM_FIRST(pjob, &proc_wait_list); + p2 = NEXT(pjob, p), p != HEAD(pjob, &proc_wait_list); + p = p2) { + if (p->pid == pid) { + p->w = w; + wakeupjob(p); + break; + } + } /* end of for loop */ + + if (p == HEAD(pjob, &proc_wait_list)) + dlog("can't locate task block for pid %d", pid); + + /* + * Must count down children inside the while loop, otherwise we won't + * count them all, and NumChildren (and later backoff) will be set + * incorrectly. SH/RUNIT 940519. + */ + if (--NumChildren < 0) + NumChildren = 0; + } /* end of "while wait..." loop */ + +#ifdef REINSTALL_SIGNAL_HANDLER + signal(sig, sigchld); +#endif /* REINSTALL_SIGNAL_HANDLER */ + + if (select_intr_valid) + longjmp(select_intr, sig); +} diff --git a/amd/srvr_amfs_auto.c b/amd/srvr_amfs_auto.c new file mode 100644 index 000000000000..4742cf6f750d --- /dev/null +++ b/amd/srvr_amfs_auto.c @@ -0,0 +1,210 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * 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. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/srvr_amfs_auto.c + * + */ + +/* + * Automount FS server ("localhost") modeling + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* globals */ + +/* statics */ +static qelem amfs_auto_srvr_list = {&amfs_auto_srvr_list, &amfs_auto_srvr_list}; +static fserver *localhost; + + +/* + * Find an nfs server for the local host + */ +fserver * +amfs_generic_find_srvr(mntfs *mf) +{ + fserver *fs = localhost; + + if (!fs) { + fs = ALLOC(struct fserver); + fs->fs_refc = 0; + fs->fs_host = strdup("localhost"); + fs->fs_ip = 0; + fs->fs_cid = 0; + fs->fs_pinger = AM_PINGER; + fs->fs_flags = FSF_VALID | FSF_PING_UNINIT; + fs->fs_type = "local"; + fs->fs_private = 0; + fs->fs_prfree = 0; + + ins_que(&fs->fs_q, &amfs_auto_srvr_list); + + srvrlog(fs, "starts up"); + + localhost = fs; + } + fs->fs_refc++; + + return fs; +} + + +/***************************************************************************** + *** GENERIC ROUTINES FOLLOW + *****************************************************************************/ + +/* + * Wakeup anything waiting for this server + */ +void +wakeup_srvr(fserver *fs) +{ + fs->fs_flags &= ~FSF_WANT; + wakeup((voidp) fs); +} + + +/* + * Called when final ttl of server has expired + */ +static void +timeout_srvr(voidp v) +{ + fserver *fs = v; + + /* + * If the reference count is still zero then + * we are free to remove this node + */ + if (fs->fs_refc == 0) { + dlog("Deleting file server %s", fs->fs_host); + if (fs->fs_flags & FSF_WANT) + wakeup_srvr(fs); + + /* + * Remove from queue. + */ + rem_que(&fs->fs_q); + /* + * (Possibly) call the private free routine. + */ + if (fs->fs_private && fs->fs_prfree) + (*fs->fs_prfree) (fs->fs_private); + + /* + * Free the net address + */ + if (fs->fs_ip) + XFREE(fs->fs_ip); + + /* + * Free the host name. + */ + XFREE(fs->fs_host); + + /* + * Discard the fserver object. + */ + XFREE(fs); + } +} + + +/* + * Free a file server + */ +void +free_srvr(fserver *fs) +{ + if (--fs->fs_refc == 0) { + /* + * The reference count is now zero, + * so arrange for this node to be + * removed in AM_TTL seconds if no + * other mntfs is referencing it. + */ + int ttl = (FSRV_ERROR(fs) || FSRV_ISDOWN(fs)) ? 19 : AM_TTL; + + dlog("Last hard reference to file server %s - will timeout in %ds", fs->fs_host, ttl); + if (fs->fs_cid) { + untimeout(fs->fs_cid); + /* + * Turn off pinging - XXX + */ + fs->fs_flags &= ~FSF_PINGING; + } + + /* + * Keep structure lying around for a while + */ + fs->fs_cid = timeout(ttl, timeout_srvr, (voidp) fs); + + /* + * Mark the fileserver down and invalid again + */ + fs->fs_flags &= ~FSF_VALID; + fs->fs_flags |= FSF_DOWN; + } +} + + +/* + * Make a duplicate fserver reference + */ +fserver * +dup_srvr(fserver *fs) +{ + fs->fs_refc++; + return fs; +} + + +/* + * Log state change + */ +void +srvrlog(fserver *fs, char *state) +{ + plog(XLOG_INFO, "file server %s, type %s, state %s", fs->fs_host, fs->fs_type, state); +} diff --git a/amd/srvr_nfs.c b/amd/srvr_nfs.c new file mode 100644 index 000000000000..0c74a658e5ee --- /dev/null +++ b/amd/srvr_nfs.c @@ -0,0 +1,1042 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/srvr_nfs.c + * + */ + +/* + * NFS server modeling + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +/* + * Number of pings allowed to fail before host is declared down + * - three-fifths of the allowed mount time... + */ +#define MAX_ALLOWED_PINGS (3 + /* for luck ... */ 1) + +/* + * How often to ping when starting a new server + */ +#define FAST_NFS_PING 3 + +#if (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME +# error: sanity check failed in srvr_nfs.c +/* + * you cannot do things this way... + * sufficient fast pings must be given the chance to fail + * within the allowed mount time + */ +#endif /* (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME */ + +/* structures and typedefs */ +typedef struct nfs_private { + u_short np_mountd; /* Mount daemon port number */ + char np_mountd_inval; /* Port *may* be invalid */ + int np_ping; /* Number of failed ping attempts */ + time_t np_ttl; /* Time when server is thought dead */ + int np_xid; /* RPC transaction id for pings */ + int np_error; /* Error during portmap request */ +} nfs_private; + +/* globals */ +qelem nfs_srvr_list = {&nfs_srvr_list, &nfs_srvr_list}; + +/* statics */ +static int global_xid; /* For NFS pings */ +#define XID_ALLOC() (++global_xid) + +#ifdef HAVE_FS_NFS3 +# define NUM_NFS_VERS 2 +#else /* not HAVE_FS_NFS3 */ +# define NUM_NFS_VERS 1 +#endif /* not HAVE_FS_NFS3 */ +static int ping_len[NUM_NFS_VERS]; +static char ping_buf[NUM_NFS_VERS][sizeof(struct rpc_msg) + 32]; + +#if defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) +/* + * Protocols we know about, in order of preference. + * + * Note that Solaris 8 and newer NetBSD systems are switching to UDP first, + * so this order may have to be adjusted for Amd in the future once more + * vendors make that change. -Erez 11/24/2000 + * + * Or we might simply make this is a platform-specific order. -Ion 09/13/2003 + */ +static char *protocols[] = { "tcp", "udp", NULL }; +#endif /* defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) */ + +/* forward definitions */ +static void nfs_keepalive(voidp); + + +/* + * Flush cached data for an fserver (or for all, if fs==NULL) + */ +void +flush_srvr_nfs_cache(fserver *fs) +{ + fserver *fs2 = NULL; + + ITER(fs2, fserver, &nfs_srvr_list) { + if (fs == NULL || fs == fs2) { + nfs_private *np = (nfs_private *) fs2->fs_private; + if (np) { + np->np_mountd_inval = TRUE; + np->np_error = -1; + } + } + } +} + + +/* + * Startup the NFS ping for a particular version. + */ +static void +create_ping_payload(u_long nfs_version) +{ + XDR ping_xdr; + struct rpc_msg ping_msg; + + /* + * Non nfs mounts like /afs/glue.umd.edu have ended up here. + */ + if (nfs_version == 0) { + nfs_version = NFS_VERSION; + plog(XLOG_WARNING, "create_ping_payload: nfs_version = 0, changed to 2"); + } else + plog(XLOG_INFO, "create_ping_payload: nfs_version: %d", (int) nfs_version); + + rpc_msg_init(&ping_msg, NFS_PROGRAM, nfs_version, NFSPROC_NULL); + + /* + * Create an XDR endpoint + */ + xdrmem_create(&ping_xdr, ping_buf[nfs_version - NFS_VERSION], sizeof(ping_buf[0]), XDR_ENCODE); + + /* + * Create the NFS ping message + */ + if (!xdr_callmsg(&ping_xdr, &ping_msg)) { + plog(XLOG_ERROR, "Couldn't create ping RPC message"); + going_down(3); + } + /* + * Find out how long it is + */ + ping_len[nfs_version - NFS_VERSION] = xdr_getpos(&ping_xdr); + + /* + * Destroy the XDR endpoint - we don't need it anymore + */ + xdr_destroy(&ping_xdr); +} + + +/* + * Called when a portmap reply arrives + */ +static void +got_portmap(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, voidp idv, int done) +{ + fserver *fs2 = (fserver *) idv; + fserver *fs = 0; + + /* + * Find which fileserver we are talking about + */ + ITER(fs, fserver, &nfs_srvr_list) + if (fs == fs2) + break; + + if (fs == fs2) { + u_long port = 0; /* XXX - should be short but protocol is naff */ + int error = done ? pickup_rpc_reply(pkt, len, (voidp) &port, (XDRPROC_T_TYPE) xdr_u_long) : -1; + nfs_private *np = (nfs_private *) fs->fs_private; + + if (!error && port) { + dlog("got port (%d) for mountd on %s", (int) port, fs->fs_host); + /* + * Grab the port number. Portmap sends back + * an u_long in native ordering, so it + * needs converting to a u_short in + * network ordering. + */ + np->np_mountd = htons((u_short) port); + np->np_mountd_inval = FALSE; + np->np_error = 0; + } else { + dlog("Error fetching port for mountd on %s", fs->fs_host); + dlog("\t error=%d, port=%d", error, (int) port); + /* + * Almost certainly no mountd running on remote host + */ + np->np_error = error ? error : ETIMEDOUT; + } + + if (fs->fs_flags & FSF_WANT) + wakeup_srvr(fs); + } else if (done) { + dlog("Got portmap for old port request"); + } else { + dlog("portmap request timed out"); + } +} + + +/* + * Obtain portmap information + */ +static int +call_portmap(fserver *fs, AUTH *auth, u_long prog, u_long vers, u_long prot) +{ + struct rpc_msg pmap_msg; + int len; + char iobuf[UDPMSGSIZE]; + int error; + struct pmap pmap; + + rpc_msg_init(&pmap_msg, PMAPPROG, PMAPVERS, PMAPPROC_NULL); + pmap.pm_prog = prog; + pmap.pm_vers = vers; + pmap.pm_prot = prot; + pmap.pm_port = 0; + len = make_rpc_packet(iobuf, + sizeof(iobuf), + PMAPPROC_GETPORT, + &pmap_msg, + (voidp) &pmap, + (XDRPROC_T_TYPE) xdr_pmap, + auth); + if (len > 0) { + struct sockaddr_in sin; + memset((voidp) &sin, 0, sizeof(sin)); + sin = *fs->fs_ip; + sin.sin_port = htons(PMAPPORT); + error = fwd_packet(RPC_XID_PORTMAP, iobuf, len, + &sin, &sin, (voidp) fs, got_portmap); + } else { + error = -len; + } + + return error; +} + + +static void +recompute_portmap(fserver *fs) +{ + int error; + u_long mnt_version; + + /* + * No portmap calls for pure WebNFS servers. + */ + if (fs->fs_flags & FSF_WEBNFS) + return; + + if (nfs_auth) + error = 0; + else + error = make_nfs_auth(); + + if (error) { + nfs_private *np = (nfs_private *) fs->fs_private; + np->np_error = error; + return; + } + + if (fs->fs_version == 0) + plog(XLOG_WARNING, "recompute_portmap: nfs_version = 0 fixed"); + + plog(XLOG_INFO, "recompute_portmap: NFS version %d on %s", + (int) fs->fs_version, fs->fs_host); +#ifdef HAVE_FS_NFS3 + if (fs->fs_version == NFS_VERSION3) + mnt_version = AM_MOUNTVERS3; + else +#endif /* HAVE_FS_NFS3 */ + mnt_version = MOUNTVERS; + + plog(XLOG_INFO, "Using MOUNT version: %d", (int) mnt_version); + call_portmap(fs, nfs_auth, MOUNTPROG, mnt_version, (u_long) IPPROTO_UDP); +} + + +int +get_mountd_port(fserver *fs, u_short *port, wchan_t wchan) +{ + int error = -1; + if (FSRV_ISDOWN(fs)) + return EWOULDBLOCK; + + if (FSRV_ISUP(fs)) { + nfs_private *np = (nfs_private *) fs->fs_private; + if (np->np_error == 0) { + *port = np->np_mountd; + error = 0; + } else { + error = np->np_error; + } + /* + * Now go get the port mapping again in case it changed. + * Note that it is used even if (np_mountd_inval) + * is True. The flag is used simply as an + * indication that the mountd may be invalid, not + * that it is known to be invalid. + */ + if (np->np_mountd_inval) + recompute_portmap(fs); + else + np->np_mountd_inval = TRUE; + } + if (error < 0 && wchan && !(fs->fs_flags & FSF_WANT)) { + /* + * If a wait channel is supplied, and no + * error has yet occurred, then arrange + * that a wakeup is done on the wait channel, + * whenever a wakeup is done on this fs node. + * Wakeup's are done on the fs node whenever + * it changes state - thus causing control to + * come back here and new, better things to happen. + */ + fs->fs_flags |= FSF_WANT; + sched_task(wakeup_task, wchan, (wchan_t) fs); + } + return error; +} + + +/* + * This is called when we get a reply to an RPC ping. + * The value of id was taken from the nfs_private + * structure when the ping was transmitted. + */ +static void +nfs_keepalive_callback(voidp pkt, int len, struct sockaddr_in *sp, struct sockaddr_in *tsp, voidp idv, int done) +{ + int xid = (long) idv; /* cast needed for 64-bit archs */ + fserver *fs; + int found_map = 0; + + if (!done) + return; + + /* + * For each node... + */ + ITER(fs, fserver, &nfs_srvr_list) { + nfs_private *np = (nfs_private *) fs->fs_private; + if (np->np_xid == xid && (fs->fs_flags & FSF_PINGING)) { + /* + * Reset the ping counter. + * Update the keepalive timer. + * Log what happened. + */ + if (fs->fs_flags & FSF_DOWN) { + fs->fs_flags &= ~FSF_DOWN; + if (fs->fs_flags & FSF_VALID) { + srvrlog(fs, "is up"); + } else { + if (np->np_ping > 1) + srvrlog(fs, "ok"); + else + srvrlog(fs, "starts up"); + fs->fs_flags |= FSF_VALID; + } + + map_flush_srvr(fs); + } else { + if (fs->fs_flags & FSF_VALID) { + dlog("file server %s type nfs is still up", fs->fs_host); + } else { + if (np->np_ping > 1) + srvrlog(fs, "ok"); + fs->fs_flags |= FSF_VALID; + } + } + + /* + * Adjust ping interval + */ + untimeout(fs->fs_cid); + fs->fs_cid = timeout(fs->fs_pinger, nfs_keepalive, (voidp) fs); + + /* + * Update ttl for this server + */ + np->np_ttl = clocktime(NULL) + + (MAX_ALLOWED_PINGS - 1) * FAST_NFS_PING + fs->fs_pinger - 1; + + /* + * New RPC xid... + */ + np->np_xid = XID_ALLOC(); + + /* + * Failed pings is zero... + */ + np->np_ping = 0; + + /* + * Recompute portmap information if not known + */ + if (np->np_mountd_inval) + recompute_portmap(fs); + + found_map++; + break; + } + } + + if (found_map == 0) + dlog("Spurious ping packet"); +} + + +static void +check_fs_addr_change(fserver *fs) +{ + struct hostent *hp = NULL; + struct in_addr ia; + char *old_ipaddr, *new_ipaddr; + + hp = gethostbyname(fs->fs_host); + if (!hp || + hp->h_addrtype != AF_INET || + !STREQ((char *) hp->h_name, fs->fs_host) || + memcmp((voidp) &fs->fs_ip->sin_addr, + (voidp) hp->h_addr, + sizeof(fs->fs_ip->sin_addr)) == 0) + return; + /* if got here: downed server changed IP address */ + old_ipaddr = strdup(inet_ntoa(fs->fs_ip->sin_addr)); + memmove((voidp) &ia, (voidp) hp->h_addr, sizeof(struct in_addr)); + new_ipaddr = inet_ntoa(ia); /* ntoa uses static buf */ + plog(XLOG_WARNING, "EZK: down fileserver %s changed ip: %s -> %s", + fs->fs_host, old_ipaddr, new_ipaddr); + XFREE(old_ipaddr); + /* copy new IP addr */ + memmove((voidp) &fs->fs_ip->sin_addr, + (voidp) hp->h_addr, + sizeof(fs->fs_ip->sin_addr)); + /* XXX: do we need to un/set these flags? */ + fs->fs_flags &= ~FSF_DOWN; + fs->fs_flags |= FSF_VALID | FSF_WANT; + map_flush_srvr(fs); /* XXX: a race with flush_srvr_nfs_cache? */ + flush_srvr_nfs_cache(fs); + fs->fs_flags |= FSF_FORCE_UNMOUNT; + +#if 0 + flush_nfs_fhandle_cache(fs); /* done in caller: nfs_keepalive_timeout */ + /* XXX: need to purge nfs_private so that somehow it will get re-initialized? */ +#endif +} + + +/* + * Called when no ping-reply received + */ +static void +nfs_keepalive_timeout(voidp v) +{ + fserver *fs = v; + nfs_private *np = (nfs_private *) fs->fs_private; + + /* + * Another ping has failed + */ + np->np_ping++; + if (np->np_ping > 1) + srvrlog(fs, "not responding"); + + /* + * Not known to be up any longer + */ + if (FSRV_ISUP(fs)) + fs->fs_flags &= ~FSF_VALID; + + /* + * If ttl has expired then guess that it is dead + */ + if (np->np_ttl < clocktime(NULL)) { + int oflags = fs->fs_flags; + dlog("ttl has expired"); + if ((fs->fs_flags & FSF_DOWN) == 0) { + /* + * Server was up, but is now down. + */ + srvrlog(fs, "is down"); + fs->fs_flags |= FSF_DOWN | FSF_VALID; + /* + * Since the server is down, the portmap + * information may now be wrong, so it + * must be flushed from the local cache + */ + flush_nfs_fhandle_cache(fs); + np->np_error = -1; + check_fs_addr_change(fs); /* check if IP addr of fserver changed */ + } else { + /* + * Known to be down + */ + if ((fs->fs_flags & FSF_VALID) == 0) + srvrlog(fs, "starts down"); + fs->fs_flags |= FSF_VALID; + } + if (oflags != fs->fs_flags && (fs->fs_flags & FSF_WANT)) + wakeup_srvr(fs); + /* + * Reset failed ping count + */ + np->np_ping = 0; + } else { + if (np->np_ping > 1) + dlog("%d pings to %s failed - at most %d allowed", np->np_ping, fs->fs_host, MAX_ALLOWED_PINGS); + } + + /* + * New RPC xid, so any late responses to the previous ping + * get ignored... + */ + np->np_xid = XID_ALLOC(); + + /* + * Run keepalive again + */ + nfs_keepalive(fs); +} + + +/* + * Keep track of whether a server is alive + */ +static void +nfs_keepalive(voidp v) +{ + fserver *fs = v; + int error; + nfs_private *np = (nfs_private *) fs->fs_private; + int fstimeo = -1; + + /* + * Send an NFS ping to this node + */ + + if (ping_len[fs->fs_version - NFS_VERSION] == 0) + create_ping_payload(fs->fs_version); + + /* + * Queue the packet... + */ + error = fwd_packet(MK_RPC_XID(RPC_XID_NFSPING, np->np_xid), + ping_buf[fs->fs_version - NFS_VERSION], + ping_len[fs->fs_version - NFS_VERSION], + fs->fs_ip, + (struct sockaddr_in *) 0, + (voidp) ((long) np->np_xid), /* cast needed for 64-bit archs */ + nfs_keepalive_callback); + + /* + * See if a hard error occurred + */ + switch (error) { + case ENETDOWN: + case ENETUNREACH: + case EHOSTDOWN: + case EHOSTUNREACH: + np->np_ping = MAX_ALLOWED_PINGS; /* immediately down */ + np->np_ttl = (time_t) 0; + /* + * This causes an immediate call to nfs_keepalive_timeout + * whenever the server was thought to be up. + * See +++ below. + */ + fstimeo = 0; + break; + + case 0: + dlog("Sent NFS ping to %s", fs->fs_host); + break; + } + + /* + * Back off the ping interval if we are not getting replies and + * the remote system is known to be down. + */ + switch (fs->fs_flags & (FSF_DOWN | FSF_VALID)) { + case FSF_VALID: /* Up */ + if (fstimeo < 0) /* +++ see above */ + fstimeo = FAST_NFS_PING; + break; + + case FSF_VALID | FSF_DOWN: /* Down */ + fstimeo = fs->fs_pinger; + break; + + default: /* Unknown */ + fstimeo = FAST_NFS_PING; + break; + } + + dlog("NFS timeout in %d seconds", fstimeo); + + fs->fs_cid = timeout(fstimeo, nfs_keepalive_timeout, (voidp) fs); +} + + +static void +start_nfs_pings(fserver *fs, int pingval) +{ + if (pingval == 0) /* could be because ping mnt option not found */ + pingval = AM_PINGER; + /* if pings haven't been initalized, then init them for first time */ + if (fs->fs_flags & FSF_PING_UNINIT) { + fs->fs_flags &= ~FSF_PING_UNINIT; + plog(XLOG_INFO, "initializing %s's pinger to %d sec", fs->fs_host, pingval); + goto do_pings; + } + + if ((fs->fs_flags & FSF_PINGING) && fs->fs_pinger == pingval) { + dlog("already running pings to %s", fs->fs_host); + return; + } + + /* if got here, then we need to update the ping value */ + plog(XLOG_INFO, "changing %s's ping value from %d%s to %d%s", + fs->fs_host, + fs->fs_pinger, (fs->fs_pinger < 0 ? " (off)" : ""), + pingval, (pingval < 0 ? " (off)" : "")); + do_pings: + fs->fs_pinger = pingval; + + if (fs->fs_cid) + untimeout(fs->fs_cid); + if (pingval < 0) { + srvrlog(fs, "wired up (pings disabled)"); + fs->fs_flags |= FSF_VALID; + fs->fs_flags &= ~FSF_DOWN; + } else { + fs->fs_flags |= FSF_PINGING; + nfs_keepalive(fs); + } +} + + +/* + * Find an nfs server for a host. + */ +fserver * +find_nfs_srvr(mntfs *mf) +{ + char *host = mf->mf_fo->opt_rhost; + fserver *fs; + int pingval; + mntent_t mnt; + nfs_private *np; + struct hostent *hp = NULL; + struct sockaddr_in *ip = NULL; + u_long nfs_version = 0; /* default is no version specified */ + u_long best_nfs_version = 0; + char *nfs_proto = NULL; /* no IP protocol either */ + int nfs_port = 0; + int nfs_port_opt = 0; + int fserver_is_down = 0; + + /* + * Get ping interval from mount options. + * Current only used to decide whether pings + * are required or not. < 0 = no pings. + */ + mnt.mnt_opts = mf->mf_mopts; + pingval = hasmntval(&mnt, "ping"); + + if (mf->mf_flags & MFF_NFS_SCALEDOWN) { + /* + * the server granted us a filehandle, but we were unable to mount it. + * therefore, scale down to NFSv2/UDP and try again. + */ + nfs_version = NFS_VERSION; + nfs_proto = "udp"; + plog(XLOG_WARNING, "find_nfs_srvr: NFS mount failed, trying again with NFSv2/UDP"); + mf->mf_flags &= ~MFF_NFS_SCALEDOWN; + } else { + /* + * Get the NFS version from the mount options. This is used + * to decide the highest NFS version to try. + */ +#ifdef MNTTAB_OPT_VERS + nfs_version = hasmntval(&mnt, MNTTAB_OPT_VERS); +#endif /* MNTTAB_OPT_VERS */ + +#ifdef MNTTAB_OPT_PROTO + { + char *proto_opt = hasmnteq(&mnt, MNTTAB_OPT_PROTO); + if (proto_opt) { + char **p; + for (p = protocols; *p; p++) + if (NSTREQ(proto_opt, *p, strlen(*p))) { + nfs_proto = *p; + break; + } + if (*p == NULL) + plog(XLOG_WARNING, "ignoring unknown protocol option for %s:%s", + host, mf->mf_fo->opt_rfs); + } + } +#endif /* MNTTAB_OPT_PROTO */ + +#ifdef HAVE_NFS_NFSV2_H + /* allow overriding if nfsv2 option is specified in mount options */ + if (amu_hasmntopt(&mnt, "nfsv2")) { + nfs_version = NFS_VERSION;/* nullify any ``vers=X'' statements */ + nfs_proto = "udp"; /* nullify any ``proto=tcp'' statements */ + plog(XLOG_WARNING, "found compatibility option \"nfsv2\": set options vers=2,proto=udp for host %s", host); + } +#endif /* HAVE_NFS_NFSV2_H */ + + /* check if we've globally overridden the NFS version/protocol */ + if (gopt.nfs_vers) { + nfs_version = gopt.nfs_vers; + plog(XLOG_INFO, "find_nfs_srvr: force NFS version to %d", + (int) nfs_version); + } + if (gopt.nfs_proto) { + nfs_proto = gopt.nfs_proto; + plog(XLOG_INFO, "find_nfs_srvr: force NFS protocol transport to %s", nfs_proto); + } + } + + /* + * lookup host address and canonical name + */ + hp = gethostbyname(host); + + /* + * New code from Bob Harris <harris@basil-rathbone.mit.edu> + * Use canonical name to keep track of file server + * information. This way aliases do not generate + * multiple NFS pingers. (Except when we're normalizing + * hosts.) + */ + if (hp && !(gopt.flags & CFM_NORMALIZE_HOSTNAMES)) + host = (char *) hp->h_name; + + if (hp) { + switch (hp->h_addrtype) { + case AF_INET: + ip = ALLOC(struct sockaddr_in); + memset((voidp) ip, 0, sizeof(*ip)); + /* as per POSIX, sin_len need not be set (used internally by kernel) */ + ip->sin_family = AF_INET; + memmove((voidp) &ip->sin_addr, (voidp) hp->h_addr, sizeof(ip->sin_addr)); + break; + + default: + plog(XLOG_USER, "No IP address for host %s", host); + goto no_dns; + } + } else { + plog(XLOG_USER, "Unknown host: %s", host); + goto no_dns; + } + + /* + * This may not be the best way to do things, but it really doesn't make + * sense to query a file server which is marked as 'down' for any + * version/proto combination. + */ + ITER(fs, fserver, &nfs_srvr_list) { + if (FSRV_ISDOWN(fs) && + STREQ(host, fs->fs_host)) { + plog(XLOG_WARNING, "fileserver %s is already hung - not running NFS proto/version discovery", host); + fs->fs_refc++; + if (ip) + XFREE(ip); + return fs; + } + } + + /* + * Get the NFS Version, and verify server is up. + * If the client only supports NFSv2, hardcode it but still try to + * contact the remote portmapper to see if the service is running. + */ +#ifndef HAVE_FS_NFS3 + nfs_version = NFS_VERSION; + nfs_proto = "udp"; + plog(XLOG_INFO, "The client supports only NFS(2,udp)"); +#endif /* not HAVE_FS_NFS3 */ + + + if (amu_hasmntopt(&mnt, MNTTAB_OPT_PUBLIC)) { + /* + * Use WebNFS to obtain file handles. + */ + mf->mf_flags |= MFF_WEBNFS; + plog(XLOG_INFO, "%s option used, NOT contacting the portmapper on %s", + MNTTAB_OPT_PUBLIC, host); + /* + * Prefer NFSv3/tcp if the client supports it (cf. RFC 2054, 7). + */ + if (!nfs_version) { +#ifdef HAVE_FS_NFS3 + nfs_version = NFS_VERSION3; +#else /* not HAVE_FS_NFS3 */ + nfs_version = NFS_VERSION; +#endif /* not HAVE_FS_NFS3 */ + plog(XLOG_INFO, "No NFS version specified, will use NFSv%d", + (int) nfs_version); + } + if (!nfs_proto) { +#if defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) + nfs_proto = "tcp"; +#else /* not defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) */ + nfs_proto = "udp"; +#endif /* not defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) */ + plog(XLOG_INFO, "No NFS protocol transport specified, will use %s", + nfs_proto); + } + } else { + /* + * Find the best combination of NFS version and protocol. + * When given a choice, use the highest available version, + * and use TCP over UDP if available. + */ + if (check_pmap_up(host, ip)) { + if (nfs_proto) { + best_nfs_version = get_nfs_version(host, ip, nfs_version, nfs_proto); + nfs_port = ip->sin_port; + } +#ifdef MNTTAB_OPT_PROTO + else { + u_int proto_nfs_version; + char **p; + + for (p = protocols; *p; p++) { + proto_nfs_version = get_nfs_version(host, ip, nfs_version, *p); + + if (proto_nfs_version > best_nfs_version) { + best_nfs_version = proto_nfs_version; + nfs_proto = *p; + nfs_port = ip->sin_port; + } + } + } +#endif /* MNTTAB_OPT_PROTO */ + } else { + plog(XLOG_INFO, "portmapper service not running on %s", host); + } + + /* use the portmapper results only nfs_version is not set yet */ + if (!best_nfs_version) { + /* + * If the NFS server is down or does not support the portmapper call + * (such as certain Novell NFS servers) we mark it as version 2 and we + * let the nfs code deal with the case when it is down. If/when the + * server comes back up and it can support NFSv3 and/or TCP, it will + * use those. + */ + if (nfs_version == 0) { + nfs_version = NFS_VERSION; + nfs_proto = "udp"; + } + plog(XLOG_INFO, "NFS service not running on %s", host); + fserver_is_down = 1; + } else { + if (nfs_version == 0) + nfs_version = best_nfs_version; + plog(XLOG_INFO, "Using NFS version %d, protocol %s on host %s", + (int) nfs_version, nfs_proto, host); + } + } + + /* + * Determine the NFS port. + * + * A valid "port" mount option overrides anything else. + * If the port has been determined from the portmapper, use that. + * Default to NFS_PORT otherwise (cf. RFC 2054, 3). + */ + nfs_port_opt = hasmntval(&mnt, MNTTAB_OPT_PORT); + if (nfs_port_opt > 0) + nfs_port = htons(nfs_port_opt); + if (!nfs_port) + nfs_port = htons(NFS_PORT); + + dlog("find_nfs_srvr: using port %d for nfs on %s", + (int) ntohs(nfs_port), host); + ip->sin_port = nfs_port; + +no_dns: + /* + * Try to find an existing fs server structure for this host. + * Note that differing versions or protocols have their own structures. + * XXX: Need to fix the ping mechanism to actually use the NFS protocol + * chosen here (right now it always uses datagram sockets). + */ + ITER(fs, fserver, &nfs_srvr_list) { + if (STREQ(host, fs->fs_host) && + nfs_version == fs->fs_version && + STREQ(nfs_proto, fs->fs_proto)) { + /* + * fill in the IP address -- this is only needed + * if there is a chance an IP address will change + * between mounts. + * Mike Mitchell, mcm@unx.sas.com, 09/08/93 + */ + if (hp && fs->fs_ip && + memcmp((voidp) &fs->fs_ip->sin_addr, + (voidp) hp->h_addr, + sizeof(fs->fs_ip->sin_addr)) != 0) { + struct in_addr ia; + char *old_ipaddr, *new_ipaddr; + old_ipaddr = strdup(inet_ntoa(fs->fs_ip->sin_addr)); + memmove((voidp) &ia, (voidp) hp->h_addr, sizeof(struct in_addr)); + new_ipaddr = inet_ntoa(ia); /* ntoa uses static buf */ + plog(XLOG_WARNING, "fileserver %s changed ip: %s -> %s", + fs->fs_host, old_ipaddr, new_ipaddr); + XFREE(old_ipaddr); + flush_nfs_fhandle_cache(fs); + memmove((voidp) &fs->fs_ip->sin_addr, (voidp) hp->h_addr, sizeof(fs->fs_ip->sin_addr)); + } + + /* + * If the new file systems doesn't use WebNFS, the nfs pings may + * try to contact the portmapper. + */ + if (!(mf->mf_flags & MFF_WEBNFS)) + fs->fs_flags &= ~FSF_WEBNFS; + + /* check if pingval needs to be updated/set/reset */ + start_nfs_pings(fs, pingval); + + /* + * Following if statement from Mike Mitchell <mcm@unx.sas.com> + * Initialize the ping data if we aren't pinging now. The np_ttl and + * np_ping fields are especially important. + */ + if (!(fs->fs_flags & FSF_PINGING)) { + np = (nfs_private *) fs->fs_private; + np->np_mountd_inval = TRUE; + np->np_xid = XID_ALLOC(); + np->np_error = -1; + np->np_ping = 0; + /* + * Initially the server will be deemed dead + * after MAX_ALLOWED_PINGS of the fast variety + * have failed. + */ + np->np_ttl = MAX_ALLOWED_PINGS * FAST_NFS_PING + clocktime(NULL) - 1; + start_nfs_pings(fs, pingval); + if (fserver_is_down) + fs->fs_flags |= FSF_VALID | FSF_DOWN; + } + + fs->fs_refc++; + if (ip) + XFREE(ip); + return fs; + } + } + + /* + * Get here if we can't find an entry + */ + + /* + * Allocate a new server + */ + fs = ALLOC(struct fserver); + fs->fs_refc = 1; + fs->fs_host = strdup(hp ? hp->h_name : "unknown_hostname"); + if (gopt.flags & CFM_NORMALIZE_HOSTNAMES) + host_normalize(&fs->fs_host); + fs->fs_ip = ip; + fs->fs_cid = 0; + if (ip) { + fs->fs_flags = FSF_DOWN; /* Starts off down */ + } else { + fs->fs_flags = FSF_ERROR | FSF_VALID; + mf->mf_flags |= MFF_ERROR; + mf->mf_error = ENOENT; + } + if (mf->mf_flags & MFF_WEBNFS) + fs->fs_flags |= FSF_WEBNFS; + fs->fs_version = nfs_version; + fs->fs_proto = nfs_proto; + fs->fs_type = MNTTAB_TYPE_NFS; + fs->fs_pinger = AM_PINGER; + fs->fs_flags |= FSF_PING_UNINIT; /* pinger hasn't been initialized */ + np = ALLOC(struct nfs_private); + memset((voidp) np, 0, sizeof(*np)); + np->np_mountd_inval = TRUE; + np->np_xid = XID_ALLOC(); + np->np_error = -1; + + /* + * Initially the server will be deemed dead after + * MAX_ALLOWED_PINGS of the fast variety have failed. + */ + np->np_ttl = clocktime(NULL) + MAX_ALLOWED_PINGS * FAST_NFS_PING - 1; + fs->fs_private = (voidp) np; + fs->fs_prfree = (void (*)(voidp)) free; + + if (!FSRV_ERROR(fs)) { + /* start of keepalive timer, first updating pingval */ + start_nfs_pings(fs, pingval); + if (fserver_is_down) + fs->fs_flags |= FSF_VALID | FSF_DOWN; + } + + /* + * Add to list of servers + */ + ins_que(&fs->fs_q, &nfs_srvr_list); + + return fs; +} |
