summaryrefslogtreecommitdiffstats
path: root/contrib/amd/amd
diff options
context:
space:
mode:
authorobrien <obrien@FreeBSD.org>1998-08-23 22:07:21 +0000
committerobrien <obrien@FreeBSD.org>1998-08-23 22:07:21 +0000
commit663d5a0f32ed8dfc091ffb6153161591ac6ba563 (patch)
tree60b090a6cbdb64326bb128ea49a231d08eb2680e /contrib/amd/amd
downloadFreeBSD-src-663d5a0f32ed8dfc091ffb6153161591ac6ba563.zip
FreeBSD-src-663d5a0f32ed8dfc091ffb6153161591ac6ba563.tar.gz
Virgin import of AMD (am-utils) v6.0a16
Diffstat (limited to 'contrib/amd/amd')
-rw-r--r--contrib/amd/amd/am_ops.c441
-rw-r--r--contrib/amd/amd/amd.8352
-rw-r--r--contrib/amd/amd/amd.c535
-rw-r--r--contrib/amd/amd/amd.h303
-rw-r--r--contrib/amd/amd/amfs_auto.c1595
-rw-r--r--contrib/amd/amd/amfs_direct.c106
-rw-r--r--contrib/amd/amd/amfs_error.c150
-rw-r--r--contrib/amd/amd/amfs_host.c686
-rw-r--r--contrib/amd/amd/amfs_inherit.c200
-rw-r--r--contrib/amd/amd/amfs_link.c141
-rw-r--r--contrib/amd/amd/amfs_linkx.c104
-rw-r--r--contrib/amd/amd/amfs_nfsl.c237
-rw-r--r--contrib/amd/amd/amfs_nfsx.c532
-rw-r--r--contrib/amd/amd/amfs_program.c191
-rw-r--r--contrib/amd/amd/amfs_root.c99
-rw-r--r--contrib/amd/amd/amfs_toplvl.c355
-rw-r--r--contrib/amd/amd/amfs_union.c124
-rw-r--r--contrib/amd/amd/amq_subr.c501
-rw-r--r--contrib/amd/amd/amq_svc.c157
-rw-r--r--contrib/amd/amd/autil.c418
-rw-r--r--contrib/amd/amd/clock.c247
-rw-r--r--contrib/amd/amd/conf.c939
-rw-r--r--contrib/amd/amd/conf_parse.y159
-rw-r--r--contrib/amd/amd/conf_tok.l186
-rw-r--r--contrib/amd/amd/get_args.c389
-rw-r--r--contrib/amd/amd/info_file.c265
-rw-r--r--contrib/amd/amd/info_hesiod.c163
-rw-r--r--contrib/amd/amd/info_ldap.c465
-rw-r--r--contrib/amd/amd/info_ndbm.c141
-rw-r--r--contrib/amd/amd/info_nis.c401
-rw-r--r--contrib/amd/amd/info_nisplus.c321
-rw-r--r--contrib/amd/amd/info_passwd.c195
-rw-r--r--contrib/amd/amd/info_union.c149
-rw-r--r--contrib/amd/amd/map.c1112
-rw-r--r--contrib/amd/amd/mapc.c1205
-rw-r--r--contrib/amd/amd/mntfs.c335
-rw-r--r--contrib/amd/amd/nfs_prot_svc.c250
-rw-r--r--contrib/amd/amd/nfs_start.c472
-rw-r--r--contrib/amd/amd/nfs_subr.c610
-rw-r--r--contrib/amd/amd/ops_TEMPLATE.c293
-rw-r--r--contrib/amd/amd/ops_autofs.c1275
-rw-r--r--contrib/amd/amd/ops_cachefs.c247
-rw-r--r--contrib/amd/amd/ops_cdfs.c206
-rw-r--r--contrib/amd/amd/ops_efs.c164
-rw-r--r--contrib/amd/amd/ops_lofs.c154
-rw-r--r--contrib/amd/amd/ops_mfs.c55
-rw-r--r--contrib/amd/amd/ops_nfs.c799
-rw-r--r--contrib/amd/amd/ops_nfs3.c55
-rw-r--r--contrib/amd/amd/ops_nullfs.c55
-rw-r--r--contrib/amd/amd/ops_pcfs.c179
-rw-r--r--contrib/amd/amd/ops_tfs.c55
-rw-r--r--contrib/amd/amd/ops_tmpfs.c55
-rw-r--r--contrib/amd/amd/ops_ufs.c173
-rw-r--r--contrib/amd/amd/ops_umapfs.c55
-rw-r--r--contrib/amd/amd/ops_unionfs.c55
-rw-r--r--contrib/amd/amd/ops_xfs.c164
-rw-r--r--contrib/amd/amd/opts.c1304
-rw-r--r--contrib/amd/amd/restart.c208
-rw-r--r--contrib/amd/amd/rpc_fwd.c476
-rw-r--r--contrib/amd/amd/sched.c300
-rw-r--r--contrib/amd/amd/srvr_amfs_auto.c214
-rw-r--r--contrib/amd/amd/srvr_nfs.c851
62 files changed, 22623 insertions, 0 deletions
diff --git a/contrib/amd/amd/am_ops.c b/contrib/amd/amd/am_ops.c
new file mode 100644
index 0000000..918c3f1
--- /dev/null
+++ b/contrib/amd/amd/am_ops.c
@@ -0,0 +1,441 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: am_ops.c,v 5.2.2.1 1992/02/09 15:08:17 jsp beta $
+ *
+ */
+
+#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_AM_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_AM_FS_LINK
+ &amfs_link_ops, /* symlink F/S */
+#endif /* HAVE_AM_FS_LINK */
+
+ /*
+ * Other amd-supported meta-filesystems.
+ */
+#ifdef HAVE_AM_FS_NFSX
+ &amfs_nfsx_ops, /* multiple-nfs F/S */
+#endif /* HAVE_AM_FS_NFSX */
+#ifdef HAVE_AM_FS_NFSL
+ &amfs_nfsl_ops, /* NFS with local link existence check */
+#endif /* HAVE_AM_FS_NFSL */
+#ifdef HAVE_AM_FS_HOST
+ &amfs_host_ops, /* multiple exported nfs F/S */
+#endif /* HAVE_AM_FS_HOST */
+#ifdef HAVE_AM_FS_LINKX
+ &amfs_linkx_ops, /* symlink F/S with link target verify */
+#endif /* HAVE_AM_FS_LINKX */
+#ifdef HAVE_AM_FS_PROGRAM
+ &amfs_program_ops, /* program F/S */
+#endif /* HAVE_AM_FS_PROGRAM */
+#ifdef HAVE_AM_FS_UNION
+ &amfs_union_ops, /* union F/S */
+#endif /* HAVE_AM_FS_UNION */
+#ifdef HAVE_AM_FS_INHERIT
+ &amfs_inherit_ops, /* inheritance F/S */
+#endif /* HAVE_AM_FS_INHERIT */
+
+ /*
+ * 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 5 should be last, in the order:
+ * (1) amfs_auto
+ * (2) amfs_direct
+ * (3) amfs_toplvl
+ * (4) autofs
+ * (5) amfs_error
+ */
+#ifdef HAVE_AM_FS_AUTO
+ &amfs_auto_ops, /* Automounter F/S */
+#endif /* HAVE_AM_FS_AUTO */
+#ifdef HAVE_AM_FS_DIRECT
+ &amfs_direct_ops, /* direct-mount F/S */
+#endif /* HAVE_AM_FS_DIRECT */
+#ifdef HAVE_AM_FS_TOPLVL
+ &amfs_toplvl_ops, /* top-level mount F/S */
+#endif /* HAVE_AM_FS_TOPLVL */
+#ifdef HAVE_FS_AUTOFS
+ &autofs_ops, /* autofs mount F/S */
+#endif /* HAVE_FS_AUTOFS */
+#ifdef HAVE_AM_FS_ERROR
+ &amfs_error_ops, /* error F/S */
+#endif /* HAVE_AM_FS_ERROR */
+ 0
+};
+
+
+void
+ops_showamfstypes(char *buf)
+{
+ struct am_ops **ap;
+ int l = 0;
+
+ buf[0] = '\0';
+ for (ap = vops; *ap; ap++) {
+ strcat(buf, (*ap)->fs_type);
+ if (ap[1])
+ strcat(buf, ", ");
+ l += strlen((*ap)->fs_type) + 2;
+ if (l > 60) {
+ l = 0;
+ strcat(buf, "\n ");
+ }
+ }
+}
+
+
+static void
+ops_show1(char *buf, int *lp, const char *name)
+{
+ strcat(buf, name);
+ strcat(buf, ", ");
+ *lp += strlen(name) + 2;
+ if (*lp > 60) {
+ strcat(buf, "\t\n");
+ *lp = 0;
+ }
+}
+
+
+void
+ops_showfstypes(char *buf)
+{
+ int l = 0;
+
+ buf[0] = '\0';
+
+#ifdef MNTTAB_TYPE_AUTOFS
+ ops_show1(buf, &l, MNTTAB_TYPE_AUTOFS);
+#endif /* MNTTAB_TYPE_AUTOFS */
+
+#ifdef MNTTAB_TYPE_CACHEFS
+ ops_show1(buf, &l, MNTTAB_TYPE_CACHEFS);
+#endif /* MNTTAB_TYPE_CACHEFS */
+
+#ifdef MNTTAB_TYPE_CDFS
+ ops_show1(buf, &l, MNTTAB_TYPE_CDFS);
+#endif /* MNTTAB_TYPE_CDFS */
+
+#ifdef MNTTAB_TYPE_CFS
+ ops_show1(buf, &l, MNTTAB_TYPE_CFS);
+#endif /* MNTTAB_TYPE_CFS */
+
+#ifdef MNTTAB_TYPE_LOFS
+ ops_show1(buf, &l, MNTTAB_TYPE_LOFS);
+#endif /* MNTTAB_TYPE_LOFS */
+
+#ifdef MNTTAB_TYPE_EFS
+ ops_show1(buf, &l, MNTTAB_TYPE_EFS);
+#endif /* MNTTAB_TYPE_EFS */
+
+#ifdef MNTTAB_TYPE_MFS
+ ops_show1(buf, &l, MNTTAB_TYPE_MFS);
+#endif /* MNTTAB_TYPE_MFS */
+
+#ifdef MNTTAB_TYPE_NFS
+ ops_show1(buf, &l, MNTTAB_TYPE_NFS);
+#endif /* MNTTAB_TYPE_NFS */
+
+#ifdef MNTTAB_TYPE_NFS3
+ ops_show1(buf, &l, "nfs3"); /* always hard-code as nfs3 */
+#endif /* MNTTAB_TYPE_NFS3 */
+
+#ifdef MNTTAB_TYPE_NULLFS
+ ops_show1(buf, &l, MNTTAB_TYPE_NULLFS);
+#endif /* MNTTAB_TYPE_NULLFS */
+
+#ifdef MNTTAB_TYPE_PCFS
+ ops_show1(buf, &l, MNTTAB_TYPE_PCFS);
+#endif /* MNTTAB_TYPE_PCFS */
+
+#ifdef MNTTAB_TYPE_TFS
+ ops_show1(buf, &l, MNTTAB_TYPE_TFS);
+#endif /* MNTTAB_TYPE_TFS */
+
+#ifdef MNTTAB_TYPE_TMPFS
+ ops_show1(buf, &l, MNTTAB_TYPE_TMPFS);
+#endif /* MNTTAB_TYPE_TMPFS */
+
+#ifdef MNTTAB_TYPE_UFS
+ ops_show1(buf, &l, MNTTAB_TYPE_UFS);
+#endif /* MNTTAB_TYPE_UFS */
+
+#ifdef MNTTAB_TYPE_UMAPFS
+ ops_show1(buf, &l, MNTTAB_TYPE_UMAPFS);
+#endif /* MNTTAB_TYPE_UMAPFS */
+
+#ifdef MNTTAB_TYPE_UNIONFS
+ ops_show1(buf, &l, MNTTAB_TYPE_UNIONFS);
+#endif /* MNTTAB_TYPE_UNIONFS */
+
+#ifdef MNTTAB_TYPE_XFS
+ ops_show1(buf, &l, 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';
+ strcat(buf, ".\n");
+}
+
+
+/*
+ * 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)) {
+ strcpy(buf, &opt[2]);
+ } else {
+ /* finally return a string prepended with 'no' */
+ strcpy(buf, "no");
+ strcat(buf, opt);
+ }
+ 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(char *opts1, char *opts2)
+{
+ mntent_t mnt2; /* place holder for opts2 */
+ char *newstr; /* new string to return (malloc'ed) */
+ char *tmpstr; /* temp */
+ char *eq; /* pointer to '=' within temp */
+ char oneopt[80]; /* one option w/o value if any */
+ char *revoneopt; /* reverse of oneopt */
+ int len = strlen(opts1) + strlen(opts2) + 2; /* space for "," and NULL */
+ char *s1 = strdup(opts1); /* copy of opts1 to munge */
+
+ /* initialization */
+ mnt2.mnt_opts = opts2;
+ newstr = xmalloc(len);
+ newstr[0] = '\0';
+
+ for (tmpstr = strtok(s1, ",");
+ tmpstr;
+ tmpstr = strtok(NULL, ",")) {
+ /* copy option to temp buffer */
+ strncpy(oneopt, tmpstr, 80);
+ oneopt[79] = '\0';
+ /* if option has a value such as rsize=1024, chop the value part */
+ if ((eq = strchr(oneopt, '=')))
+ eq[1] = '\0';
+ /* find reverse option of oneopt */
+ revoneopt = reverse_option(oneopt);
+ /* if option orits reverse exist in opts2, ignore it */
+ if (hasmntopt(&mnt2, oneopt) || hasmntopt(&mnt2, revoneopt))
+ continue;
+ /* add option to returned string */
+ if (newstr && newstr[0]) {
+ strcat(newstr, ",");
+ strcat(newstr, tmpstr);
+ } else {
+ strcpy(newstr, tmpstr);
+ }
+ }
+
+ /* finally, append opts2 itself */
+ if (newstr && newstr[0]) {
+ strcat(newstr, ",");
+ strcat(newstr, opts2);
+ } else {
+ strcpy(newstr, opts2);
+ }
+
+ XFREE(s1);
+ return newstr;
+}
+
+
+am_ops *
+ops_match(am_opts *fo, char *key, char *g_key, char *path, char *keym, char *map)
+{
+ am_ops **vp;
+ am_ops *rop = 0;
+
+ /*
+ * 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
+ */
+ for (vp = vops; (rop = *vp); vp++)
+ if (STREQ(rop->fs_type, fo->opt_type))
+ break;
+ 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.
+ */
+ if (fo->opt_addopts) {
+ char *mergedstr;
+ mergedstr = merge_opts(fo->opt_opts, fo->opt_addopts);
+ plog(XLOG_USER, "merge opts \"%s\" add \"%s\" => \"%s\"",
+ fo->opt_opts, fo->opt_addopts, mergedstr);
+ XFREE(fo->opt_opts);
+ fo->opt_opts = mergedstr;
+ }
+
+ /*
+ * Check the filesystem is happy
+ */
+ if (fo->fs_mtab)
+ XFREE(fo->fs_mtab);
+
+ if ((fo->fs_mtab = (*rop->fs_match) (fo)))
+ return rop;
+
+ /*
+ * Return error file system
+ */
+ fo->fs_mtab = (*amfs_error_ops.fs_match) (fo);
+ return &amfs_error_ops;
+}
diff --git a/contrib/amd/amd/amd.8 b/contrib/amd/amd/amd.8
new file mode 100644
index 0000000..a738809
--- /dev/null
+++ b/contrib/amd/amd/amd.8
@@ -0,0 +1,352 @@
+.\"
+.\" Copyright (c) 1997-1998 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 5.2.2.1 1992/02/09 15:11:11 jsp beta $
+.\"
+.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 \-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 " interval.interval"
+Specify the
+.IR interval ,
+in tenths of a second, between NFS/RPC/UDP retries.
+The default is 0.8 seconds.
+The second values alters the restransmit counter.
+Useful defaults are supplied if either or both
+values are missing.
+
+.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 \-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
+.PD 0
+.TP 5
+.B /a
+directory under which filesystems are dynamically mounted
+.TP 5
+.B /etc/amd.conf
+default configuration file
+.PD
+.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
+.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.columbia.edu>, Department of Computer Science, Columbia
+University, 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/contrib/amd/amd/amd.c b/contrib/amd/amd/amd.c
new file mode 100644
index 0000000..7ef2ce7
--- /dev/null
+++ b/contrib/amd/amd/amd.c
@@ -0,0 +1,535 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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.c,v 5.2.2.1 1992/02/09 15:08:15 jsp beta $
+ *
+ */
+
+/*
+ * 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[16 + MAXHOSTNAMELEN]; /* "kiska.southseas.nz:(pid%d)" */
+char *progname; /* "amd" */
+char *hostdomain = "unknown.domain";
+char hostname[MAXHOSTNAMELEN] = "localhost"; /* Hostname */
+char hostd[2 * MAXHOSTNAMELEN]; /* 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 foreground = 1; /* This is the top-level server */
+int immediate_abort; /* Should close-down unmounts be retried */
+int orig_umask;
+int select_intr_valid;
+
+jmp_buf select_intr;
+pid_t mypid; /* Current process id */
+serv_state amd_state;
+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_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 */
+
+#ifdef DEBUG
+ if (sig != SIGHUP)
+ dlog("spurious call to sighup");
+#endif /* DEBUG */
+ /*
+ * Force a reload by zero'ing the timer
+ */
+ if (amd_state == Run)
+ do_mapc_reload = 0;
+}
+
+
+static RETSIGTYPE
+parent_exit(int sig)
+{
+ exit(0);
+}
+
+
+static int
+daemon_mode(void)
+{
+ int bgpid;
+
+#ifdef HAVE_SIGACTION
+ struct sigaction sa, osa;
+
+ 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 reache 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) mypid);
+ fflush(stdout);
+ /* do not fclose 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) 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) */
+
+ memset(&gopt, 0, sizeof(struct amu_global_options));
+
+ /* name of current architecture */
+ gopt.arch = HOST_ARCH;
+
+ /* automounter temp dir */
+ gopt.auto_dir = "/a";
+
+ /* cluster name */
+ gopt.cluster = NULL;
+
+ /*
+ * 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;
+
+ /* pid file */
+ gopt.pid_file = "/dev/stdout";
+
+ /* local domain */
+ gopt.sub_domain = NULL;
+
+ /* NFS retransmit counter */
+ gopt.amfs_auto_retrans = -1;
+
+ /* NFS retry interval */
+ gopt.amfs_auto_timeo = -1;
+
+ /* cache duration */
+ gopt.am_timeo = AM_TTL;
+
+ /* dismount interval */
+ gopt.am_timeo_w = AM_TTL_W;
+
+ /*
+ * various CFM_* flags.
+ * by default, only the "plock" option is on (if available).
+ */
+ gopt.flags = CFM_PROCESS_LOCK;
+
+#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;
+#endif /* HAVE_MAP_LDAP */
+
+#ifdef HAVE_MAP_NIS
+ /* YP domain name */
+ gopt.nis_domain = NULL;
+#endif /* HAVE_MAP_NIS */
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ char *domdot, *verstr;
+ int ppid = 0;
+ int error;
+#ifdef HAVE_SIGACTION
+ struct sigaction sa;
+#endif /* HAVE_SIGACTION */
+
+ /*
+ * 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";
+
+ /*
+ * Initialise process id. This is kept
+ * cached since it is used for generating
+ * and using file handles.
+ */
+ mypid = getpid();
+
+ /*
+ * Get local machine name
+ */
+ if (gethostname(hostname, sizeof(hostname)) < 0) {
+ plog(XLOG_FATAL, "gethostname: %m");
+ going_down(1);
+ }
+
+ /*
+ * 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 initialise 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;
+ }
+ strcpy(hostd, hostname);
+
+ /*
+ * Trap interrupts for shutdowns.
+ */
+#ifdef HAVE_SIGACTION
+ sa.sa_handler = sigterm;
+ sa.sa_flags = 0;
+ sigemptyset(&(sa.sa_mask));
+ sigaddset(&(sa.sa_mask), SIGINT);
+ sigaddset(&(sa.sa_mask), SIGTERM);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGTERM, &sa, NULL);
+#else /* not HAVE_SIGACTION */
+ (void) signal(SIGINT, sigterm);
+#endif /* not HAVE_SIGACTION */
+
+ /*
+ * Trap Terminate so that we can shutdown gracefully (some chance)
+ */
+#ifdef HAVE_SIGACTION
+ sa.sa_handler = sigterm;
+ sa.sa_flags = 0;
+ sigemptyset(&(sa.sa_mask));
+ sigaddset(&(sa.sa_mask), SIGTERM);
+ sigaction(SIGTERM, &sa, NULL);
+#else /* not HAVE_SIGACTION */
+ (void) signal(SIGTERM, sigterm);
+#endif /* not HAVE_SIGACTION */
+
+ /*
+ * Hangups tell us to reload the cache
+ */
+#ifdef HAVE_SIGACTION
+ sa.sa_handler = sighup;
+ sa.sa_flags = 0;
+ sigemptyset(&(sa.sa_mask));
+ sigaddset(&(sa.sa_mask), SIGHUP);
+ sigaction(SIGHUP, &sa, NULL);
+#else /* not HAVE_SIGACTION */
+ (void) signal(SIGHUP, sighup);
+#endif /* not HAVE_SIGACTION */
+
+ /*
+ * Trap Death-of-a-child. These allow us to
+ * pick up the exit status of backgrounded mounts.
+ * See "sched.c".
+ */
+#ifdef HAVE_SIGACTION
+ sa.sa_handler = sigchld;
+ sa.sa_flags = 0;
+ sigemptyset(&(sa.sa_mask));
+ sigaddset(&(sa.sa_mask), SIGCHLD);
+ sigaction(SIGCHLD, &sa, NULL);
+
+ /*
+ * construct global "masked_sigs" used in nfs_start.c
+ */
+ sigemptyset(&masked_sigs);
+ sigaddset(&masked_sigs, SIGHUP);
+ sigaddset(&masked_sigs, SIGCHLD);
+ sigaddset(&masked_sigs, SIGTERM);
+ sigaddset(&masked_sigs, SIGINT);
+#else /* not HAVE_SIGACTION */
+ (void) signal(SIGCHLD, sigchld);
+#endif /* not 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.
+ */
+ verstr = strtok(get_version_string(), "\n");
+ plog(XLOG_INFO, "AM-UTILS VERSION INFORMATION:");
+ while (verstr) {
+ plog(XLOG_INFO, verstr);
+ verstr = strtok(NULL, "\n");
+ }
+
+ /*
+ * Get our own IP address so that we
+ * can mount the automounter.
+ */
+ amu_get_myaddress(&myipaddr);
+ plog(XLOG_INFO, "My ip addr is 0x%x", htonl(myipaddr.s_addr));
+
+ /* 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 = %d)", geteuid());
+ going_down(1);
+ }
+
+ /*
+ * Lock process text and data segment in memory.
+ */
+#ifdef HAVE_PLOCK
+ if (gopt.flags & CFM_PROCESS_LOCK) {
+ if (plock(PROCLOCK) != 0) {
+ plog(XLOG_WARNING, "Couldn't lock process text and data segment in memory: %m");
+ } else {
+ plog(XLOG_INFO, "Locked process text and data segment in memory");
+ }
+ }
+#endif /* HAVE_PLOCK */
+
+#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 */
+
+#ifdef DEBUG
+ amuDebug(D_DAEMON)
+#endif /* DEBUG */
+ ppid = daemon_mode();
+
+ sprintf(pid_fsname, "%s:(pid%ld)", hostname, (long) mypid);
+
+ do_mapc_reload = clocktime() + ONE_HOUR;
+
+ /*
+ * Register automounter with system.
+ */
+ error = mount_automounter(ppid);
+ if (error && ppid)
+ kill(SIGALRM, ppid);
+ going_down(error);
+
+ abort();
+ return 1; /* should never get here */
+}
diff --git a/contrib/amd/amd/amd.h b/contrib/amd/amd/amd.h
new file mode 100644
index 0000000..610dfe2
--- /dev/null
+++ b/contrib/amd/amd/amd.h
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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.h,v 1.1 1996/01/13 23:23:39 ezk Exp ezk $
+ *
+ */
+
+#ifndef _AMD_H
+#define _AMD_H
+
+
+/*
+ * MACROS:
+ */
+
+/* options for amd.conf */
+#define CFM_BROWSABLE_DIRS 0x0001
+#define CFM_MOUNT_TYPE_AUTOFS 0x0002
+#define CFM_ENABLE_DEFAULT_SELECTORS 0x0004
+#define CFM_NORMALIZE_HOSTNAMES 0x0008
+#define CFM_PROCESS_LOCK 0x0010
+#define CFM_PRINT_PID 0x0020
+#define CFM_RESTART_EXISTING_MOUNTS 0x0040
+#define CFM_SHOW_STATFS_ENTRIES 0x0080
+#define CFM_FULLY_QUALIFIED_HOSTS 0x0100
+#define CFM_BROWSABLE_DIRS_FULL 0x0200 /* allow '/' in readdir() */
+#define CFM_UNMOUNT_ON_EXIT 0x0400 /* when amd finishing */
+
+/* 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 */
+
+/* Hash table size */
+#define NKVHASH (1 << 2) /* Power of two */
+
+/* interval between forced retries of a mount */
+#define RETRY_INTERVAL 2
+
+#define ereturn(x) { *error_return = x; return 0; }
+
+
+/*
+ * TYPEDEFS:
+ */
+
+typedef struct cf_map cf_map_t;
+typedef struct kv kv;
+/*
+ * 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 *);
+
+
+
+/*
+ * 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 */
+ char *cluster; /* cluster name */
+ char *karch; /* kernel architecture */
+ char *logfile; /* amd log file */
+ char *op_sys; /* operating system name */
+ char *op_sys_ver; /* OS version */
+ char *pid_file; /* PID file */
+ char *sub_domain; /* local domain */
+ char *map_options; /* global map options */
+ char *map_type; /* global map type */
+ char *search_path; /* search path for maps */
+ char *mount_type; /* mount type for map */
+ u_int flags; /* various CFM_* flags */
+ int amfs_auto_retrans; /* NFS retransmit counter */
+ int amfs_auto_timeo; /* NFS retry interval */
+ int am_timeo; /* cache duration */
+ int am_timeo_w; /* dismount interval */
+ int portmap_program; /* amd RPC program number */
+#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) */
+#endif /* HAVE_MAP_LDAP */
+#ifdef HAVE_MAP_NIS
+ char *nis_domain; /* YP domain name */
+#endif /* HAVE_MAP_NIS */
+};
+
+/* 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_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? */
+};
+
+/*
+ * 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 */
+ 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 */
+ /* options available via amd conf file */
+ char *cf_map_type; /* file, hesiod, ndbm, nis, etc. */
+ char *cf_search_path; /* /etc/local:/etc/amdmaps:/misc/yp */
+ void *map_data; /* Map data black box */
+};
+
+/*
+ * 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 succesful.
+ * We rely on the RPC retry mechanism to resend the lookup request which
+ * can then be handled.
+ */
+struct continuation {
+ char **ivec; /* Current mount info */
+ am_node *mp; /* Node we are trying to mount */
+ char *key; /* Map key */
+ char *info; /* Info string */
+ char **xivec; /* Saved strsplit vector */
+ char *auto_opts; /* Automount options */
+ am_opts fs_opts; /* Filesystem options */
+ char *def_opts; /* Default automount options */
+ int retry; /* Try again? */
+ int tried; /* Have we tried any yet? */
+ time_t start; /* Time we started this mount */
+ int callout; /* Callout identifier */
+};
+
+
+/*
+ * 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 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 *root_fh(char *dir);
+extern am_node * autofs_lookuppn(am_node *mp, char *fname, int *error_return, int op);
+extern am_node *find_ap(char *);
+extern am_node *find_ap2(char *, am_node *);
+extern bool_t xdr_amq_mount_info_qelem(XDR *xdrs, qelem *qhead);
+extern fserver *find_nfs_srvr(mntfs *mf);
+extern int auto_fmount(am_node *mp);
+extern int auto_fumount(am_node *mp);
+extern int mount_nfs_fh(am_nfs_handle_t *fhp, char *dir, char *fs_name, char *opts, mntfs *mf);
+extern int process_last_regular_map(void);
+extern int set_conf_kv(const char *section, const char *k, const char *v);
+extern int try_mount(voidp mvp);
+extern int yyparse (void);
+extern nfsentry *make_entry_chain(am_node *mp, const nfsentry *current_chain, int fully_browsable);
+extern void amfs_auto_cont(int rc, int term, voidp closure);
+extern void amfs_auto_mkcacheref(mntfs *mf);
+extern void amfs_auto_retry(int rc, int term, voidp closure);
+extern void assign_error_mntfs(am_node *mp);
+extern void flush_srvr_nfs_cache(void);
+extern void free_continuation(struct continuation *cp);
+extern void mf_mounted(mntfs *mf);
+extern void quick_reply(am_node *mp, int error);
+extern void root_newmap(const char *, const char *, const char *, const cf_map_t *);
+
+/* amd global variables */
+extern FILE *yyin;
+extern SVCXPRT *nfs_program_2_transp; /* For quick_reply() */
+extern char *conf_tag;
+extern int NumChild;
+extern int fwd_sock;
+extern int select_intr_valid;
+extern int usage;
+extern int use_conf_file; /* use amd configuration file */
+extern jmp_buf select_intr;
+extern qelem mfhead;
+extern struct amu_global_options gopt; /* where global options are stored */
+
+#ifdef HAVE_SIGACTION
+extern sigset_t masked_sigs;
+#endif /* HAVE_SIGACTION */
+
+#if defined(HAVE_AM_FS_LINK) || defined(HAVE_AM_FS_LINKX)
+extern char *amfs_link_match(am_opts *fo);
+extern int amfs_link_fumount(mntfs *mf);
+#endif /* defined(HAVE_AM_FS_LINK) || defined(HAVE_AM_FS_LINKX) */
+
+#ifdef HAVE_AM_FS_NFSL
+extern char *nfs_match(am_opts *fo);
+#endif /* HAVE_AM_FS_NFSL */
+
+#if defined(HAVE_FS_NFS3) && !defined(HAVE_XDR_MOUNTRES3)
+extern bool_t xdr_mountres3(XDR *xdrs, mountres3 *objp);
+#endif /* defined(HAVE_FS_NFS3) && !defined(HAVE_XDR_MOUNTRES3) */
+
+#ifdef HAVE_FS_AUTOFS
+extern SVCXPRT *autofsxprt;
+extern u_short autofs_port;
+
+extern int autofs_mount(am_node *mp);
+extern int autofs_umount(am_node *mp);
+extern int create_autofs_service(int *soAUTOFSp, u_short *autofs_portp, SVCXPRT **autofs_xprtp, void (*dispatch_fxn)(struct svc_req *rqstp, SVCXPRT *transp));
+extern int svc_create_local_service(void (*dispatch) (), u_long prognum, u_long versnum, char *nettype, char *servname);
+extern void autofs_mounted(mntfs *mf);
+extern void autofs_program_1(struct svc_req *rqstp, SVCXPRT *transp);
+#endif /* HAVE_FS_AUTOFS */
+
+/* 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 */
+
+#endif /* not _AMD_H */
diff --git a/contrib/amd/amd/amfs_auto.c b/contrib/amd/amd/amfs_auto.c
new file mode 100644
index 0000000..0530142
--- /dev/null
+++ b/contrib/amd/amd/amfs_auto.c
@@ -0,0 +1,1595 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: amfs_auto.c,v 1.1 1997-1998/06/30 19:22:30 ezk Exp ezk $
+ *
+ */
+
+/*
+ * Automount file system
+ */
+
+#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)
+
+/* DEVELOPERS: turn this on for special debugging of readdir code */
+#undef DEBUG_READDIR
+
+/****************************************************************************
+ *** STRUCTURES ***
+ ****************************************************************************/
+
+
+
+/****************************************************************************
+ *** FORWARD DEFINITIONS ***
+ ****************************************************************************/
+static int amfs_auto_bgmount(struct continuation * cp, int mpe);
+static int amfs_auto_mount(am_node *mp);
+static int amfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable);
+static void amfs_auto_umounted(am_node *mp);
+
+
+/****************************************************************************
+ *** OPS STRUCTURES ***
+ ****************************************************************************/
+am_ops amfs_auto_ops =
+{
+ "auto",
+ amfs_auto_match,
+ 0, /* amfs_auto_init */
+ amfs_auto_mount,
+ 0,
+ amfs_auto_umount,
+ 0,
+ amfs_auto_lookuppn,
+ amfs_auto_readdir,
+ 0, /* amfs_auto_readlink */
+ 0, /* amfs_auto_mounted */
+ amfs_auto_umounted,
+ find_amfs_auto_srvr,
+ FS_AMQINFO | FS_DIRECTORY
+};
+
+
+/****************************************************************************
+ *** FUNCTIONS ***
+ ****************************************************************************/
+/*
+ * AMFS_AUTO needs nothing in particular.
+ */
+char *
+amfs_auto_match(am_opts *fo)
+{
+ char *p = fo->opt_rfs;
+
+ if (!fo->opt_rfs) {
+ plog(XLOG_USER, "auto: no mount point named (rfs:=)");
+ return 0;
+ }
+ if (!fo->opt_fs) {
+ plog(XLOG_USER, "auto: no map named (fs:=)");
+ return 0;
+ }
+
+ /*
+ * Swap round fs:= and rfs:= options
+ * ... historical (jsp)
+ */
+ 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 : ".");
+}
+
+
+
+
+/*
+ * Build a new map cache for this node, or re-use
+ * an existing cache for the same map.
+ */
+void
+amfs_auto_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 = (voidp) mapc_find(mf->mf_info, cache,
+ mf->mf_fo->opt_maptype);
+ mf->mf_prfree = mapc_free;
+}
+
+
+/*
+ * Mount a sub-mount
+ */
+static int
+amfs_auto_mount(am_node *mp)
+{
+ mntfs *mf = mp->am_mnt;
+
+ /*
+ * 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 = "";
+ } 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, "/");
+ }
+
+ /*
+ * Attach a map cache
+ */
+ amfs_auto_mkcacheref(mf);
+
+ return 0;
+}
+
+
+
+
+/*
+ * Unmount an automount sub-node
+ */
+int
+amfs_auto_umount(am_node *mp)
+{
+ return 0;
+}
+
+
+/*
+ * Unmount an automount node
+ */
+static void
+amfs_auto_umounted(am_node *mp)
+{
+ /*
+ * If this is a pseudo-directory then just adjust the link count
+ * in the parent, otherwise call the generic unmount routine
+ */
+ if (mp->am_parent && mp->am_parent->am_parent)
+ --mp->am_parent->am_fattr.na_nlink;
+}
+
+
+/*
+ * Discard an old continuation
+ */
+void
+free_continuation(struct continuation *cp)
+{
+ if (cp->callout)
+ untimeout(cp->callout);
+ XFREE(cp->key);
+ XFREE(cp->xivec);
+ XFREE(cp->info);
+ XFREE(cp->auto_opts);
+ XFREE(cp->def_opts);
+ free_opts(&cp->fs_opts);
+ XFREE(cp);
+}
+
+
+/*
+ * Discard the underlying mount point and replace
+ * with a reference to an error filesystem.
+ */
+void
+assign_error_mntfs(am_node *mp)
+{
+ if (mp->am_error > 0) {
+ /*
+ * Save the old error code
+ */
+ int error = mp->am_error;
+ if (error <= 0)
+ error = mp->am_mnt->mf_error;
+ /*
+ * Discard the old filesystem
+ */
+ free_mntfs(mp->am_mnt);
+ /*
+ * 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;
+ }
+}
+
+
+/*
+ * The continuation function. This is called by
+ * the task notifier when a background mount attempt
+ * completes.
+ */
+void
+amfs_auto_cont(int rc, int term, voidp closure)
+{
+ struct continuation *cp = (struct continuation *) closure;
+ mntfs *mf = cp->mp->am_mnt;
+
+ /*
+ * Definitely not trying to mount at the moment
+ */
+ mf->mf_flags &= ~MFF_MOUNTING;
+
+ /*
+ * While we are mounting - try to avoid race conditions
+ */
+ new_ttl(cp->mp);
+
+ /*
+ * Wakeup anything waiting for this mount
+ */
+ wakeup((voidp) mf);
+
+ /*
+ * Check for termination signal or exit status...
+ */
+ if (rc || term) {
+ am_node *xmp;
+
+ 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", cp->mp->am_path, term);
+ } else {
+ /*
+ * Check for exit status...
+ */
+ mf->mf_error = rc;
+ mf->mf_flags |= MFF_ERROR;
+ errno = rc; /* XXX */
+ if (!STREQ(cp->mp->am_mnt->mf_ops->fs_type, "linkx"))
+ plog(XLOG_ERROR, "%s: mount (amfs_auto_cont): %m", cp->mp->am_path);
+ }
+
+ /*
+ * 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->ivec++;
+ xmp = cp->mp;
+ (void) amfs_auto_bgmount(cp, 0);
+ assign_error_mntfs(xmp);
+ } else {
+ /*
+ * The mount worked.
+ */
+ am_mounted(cp->mp);
+ free_continuation(cp);
+ }
+
+ reschedule_timeout_mp();
+}
+
+
+/*
+ * Retry a mount
+ */
+void
+amfs_auto_retry(int rc, int term, voidp closure)
+{
+ struct continuation *cp = (struct continuation *) closure;
+ int error = 0;
+
+#ifdef DEBUG
+ dlog("Commencing retry for mount of %s", cp->mp->am_path);
+#endif /* DEBUG */
+
+ new_ttl(cp->mp);
+
+ if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime()) {
+ /*
+ * The entire mount has timed out. Set the error code and skip past all
+ * the info vectors so that amfs_auto_bgmount will not have any more
+ * ways to try the mount, so causing an error.
+ */
+ plog(XLOG_INFO, "mount of \"%s\" has timed out", cp->mp->am_path);
+ error = ETIMEDOUT;
+ while (*cp->ivec)
+ cp->ivec++;
+ /* explicitly forbid further retries after timeout */
+ cp->retry = FALSE;
+ }
+ if (error || !IN_PROGRESS(cp)) {
+ (void) amfs_auto_bgmount(cp, error);
+ }
+ reschedule_timeout_mp();
+}
+
+
+/*
+ * Try to mount a file system. Can be called
+ * directly or in a sub-process by run_task.
+ */
+int
+try_mount(voidp mvp)
+{
+ int error = 0;
+ am_node *mp = (am_node *) mvp;
+ mntfs *mf = mp->am_mnt;
+
+ /*
+ * If the directory is not yet made and it needs to be made, then make it!
+ * This may be run in a background process in which case the flag setting
+ * won't be noticed later - but it is set anyway just after run_task is
+ * called. It should probably go away totally...
+ */
+ if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_ops->fs_flags & FS_MKMNT) {
+ error = mkdirs(mf->mf_mount, 0555);
+ if (!error)
+ mf->mf_flags |= MFF_MKMNT;
+ }
+
+ /*
+ * Mount it!
+ */
+ error = mount_node(mp);
+
+#ifdef DEBUG
+ if (error > 0) {
+ errno = error;
+ dlog("amfs_auto call to mount_node failed: %m");
+ }
+#endif /* DEBUG */
+
+ return error;
+}
+
+
+/*
+ * 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 = initialise 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 occured on this mount then
+ update stats
+ save error in mount point
+ fi
+ endfor
+ */
+static int
+amfs_auto_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 failed to 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%x",
+ cp->fs_opts.opt_fs, 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 initialised, 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);
+
+ mp->am_fattr.na_fileid = mp->am_gen;
+
+ 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 %ds", 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 amfs_auto_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 *
+amfs_auto_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 *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 amfs_auto_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 occured then return it.
+ */
+ if (error) {
+#ifdef DEBUG
+ errno = error; /* XXX */
+ dlog("Returning error: %m", error);
+#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 amfs_auto_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_ENABLE_DEFAULT_SELECTORS) {
+ /*
+ * 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);
+ if (pt == &amfs_error_ops) {
+ plog(XLOG_MAP, "failed to match defaults for \"%s\"", *sp);
+ } else {
+ dfl = strip_selectors(*sp, "/defaults");
+ plog(XLOG_MAP, "matched default selectors \"%s\"", dfl);
+ break;
+ }
+ ++sp;
+ }
+ }
+ } else { /* not enable_default_selectors */
+ /*
+ * 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_ENABLE_DEFAULT_SELECTORS) && 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);
+
+ /*
+ * 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_ops == &amfs_direct_ops ? "" : mp->am_path,
+ *fname == '/' ? "" : "/", fname);
+
+#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 = amfs_auto_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);
+}
+
+
+/*
+ * 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;
+}
+
+
+/*
+ * This readdir function which call a special version of it that allows
+ * browsing if browsable_dirs=yes was set on the map.
+ */
+int
+amfs_auto_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, 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 (hasmntopt(&mnt, "fullybrowsable"))
+ return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, TRUE);
+ if (hasmntopt(&mnt, "browsable"))
+ return amfs_auto_readdir_browsable(mp, cookie, dp, ep, count, FALSE);
+ }
+
+ 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.
+ */
+#ifdef DEBUG
+ dlog("default search");
+#endif /* DEBUG */
+ /*
+ * 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 : ~(u_int) 0;
+
+ if (!xp)
+ dp->dl_eof = TRUE; /* by default assume readdir done */
+
+ return 0;
+ }
+#ifdef DEBUG
+ dlog("real child");
+#endif /* DEBUG */
+
+ if (gen == ~(u_int) 0) {
+#ifdef DEBUG
+ dlog("End of readdir in %s", mp->am_path);
+#endif /* DEBUG */
+ dp->dl_eof = TRUE;
+ dp->dl_entries = 0;
+ 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 = ~(u_int) 0;
+ 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;
+
+ return 0;
+ }
+ return ESTALE;
+}
+
+
+/* This one is called only if map is browsable */
+static int
+amfs_auto_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count, int fully_browsable)
+{
+ u_int gen = *(u_int *) cookie;
+ int chain_length, i;
+ static nfsentry *te, *te_next;
+#ifdef DEBUG_READDIR
+ nfsentry *ne;
+ static int j;
+#endif /* DEBUG_READDIR */
+
+ dp->dl_eof = FALSE; /* assume readdir not done */
+
+#ifdef DEBUG_READDIR
+ plog(XLOG_INFO, "amfs_auto_readdir_browsable gen=%u, count=%d",
+ gen, count);
+#endif /* DEBUG_READDIR */
+
+ 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.
+ */
+#ifdef DEBUG
+ dlog("default search");
+#endif /* DEBUG */
+ /*
+ * 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 = ~(u_int) 0;
+
+ /*
+ * 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;
+#ifdef DEBUG_READDIR
+ j = 0;
+ for (ne=te; ne; ne=ne->ne_nextentry)
+ plog(XLOG_INFO, "gen1 key %4d \"%s\"", j++, ne->ne_name);
+#endif /* DEBUG_READDIR */
+
+ /* 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 interation */
+ 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 */
+#ifdef DEBUG_READDIR
+ for (ne=te; ne; ne=ne->ne_nextentry)
+ plog(XLOG_INFO, "gen2 key %4d \"%s\"", j++, ne->ne_name);
+#endif /* DEBUG_READDIR */
+ return 0;
+ } /* end of "if (gen == 0)" statement */
+
+#ifdef DEBUG
+ dlog("real child");
+#endif /* DEBUG */
+
+ if (gen == ~(u_int) 0) {
+#ifdef DEBUG
+ dlog("End of readdir in %s", mp->am_path);
+#endif /* DEBUG */
+ 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 interation */
+ dp->dl_eof = FALSE; /* tell readdir there's more */
+ }
+ ep = te; /* send next chunk of "te" chain */
+ dp->dl_entries = ep;
+#ifdef DEBUG_READDIR
+ plog(XLOG_INFO, "dl_entries=0x%x, te_next=0x%x, dl_eof=%d",
+ dp->dl_entries, te_next, dp->dl_eof);
+ for (ne=te; ne; ne=ne->ne_nextentry)
+ plog(XLOG_INFO, "gen3 key %4d \"%s\"", j++, ne->ne_name);
+#endif /* DEBUG_READDIR */
+ return 0;
+}
+
+
+int
+amfs_auto_fmount(am_node *mp)
+{
+ mntfs *mf = mp->am_mnt;
+ return (*mf->mf_ops->fmount_fs) (mf);
+}
+
+
+int
+amfs_auto_fumount(am_node *mp)
+{
+ mntfs *mf = mp->am_mnt;
+ return (*mf->mf_ops->fumount_fs) (mf);
+}
diff --git a/contrib/amd/amd/amfs_direct.c b/contrib/amd/amd/amfs_direct.c
new file mode 100644
index 0000000..1558ae0
--- /dev/null
+++ b/contrib/amd/amd/amfs_direct.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: amfs_direct.c,v 1.1 1997-1998/06/30 19:22:30 ezk Exp ezk $
+ *
+ */
+
+/*
+ * 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_auto_match,
+ 0, /* amfs_direct_init */
+ amfs_toplvl_mount,
+ 0,
+ amfs_toplvl_umount,
+ 0,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ amfs_direct_readlink,
+ amfs_toplvl_mounted,
+ 0, /* amfs_auto_umounted */
+ find_amfs_auto_srvr,
+ FS_MKMNT | FS_NOTIMEOUT | FS_BACKGROUND | FS_AMQINFO
+};
+
+
+/****************************************************************************
+ *** 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_auto_mkcacheref(mp->am_mnt); /* XXX */
+ xp = amfs_auto_lookuppn(mp, mp->am_path + 1, &rc, VLOOK_CREATE);
+ }
+ 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/contrib/amd/amd/amfs_error.c b/contrib/amd/amd/amfs_error.c
new file mode 100644
index 0000000..ccb037b
--- /dev/null
+++ b/contrib/amd/amd/amfs_error.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: amfs_error.c,v 5.2.2.1 1992/02/09 15:08:21 jsp beta $
+ *
+ */
+
+/*
+ * 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_fmount(mntfs *mf);
+static int amfs_error_fumount(mntfs *mf);
+static void amfs_error_umounted(am_node *mp);
+
+
+/*
+ * Ops structure
+ */
+am_ops amfs_error_ops =
+{
+ "error",
+ amfs_error_match,
+ 0, /* amfs_error_init */
+ amfs_auto_fmount,
+ amfs_error_fmount,
+ amfs_auto_fumount,
+ amfs_error_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* amfs_error_readlink */
+ 0, /* amfs_error_mounted */
+ amfs_error_umounted,
+ find_amfs_auto_srvr,
+ FS_DISCARD
+};
+
+
+
+/*
+ * EFS file system always matches
+ */
+static char *
+amfs_error_match(am_opts *fo)
+{
+ return strdup("(error-hook)");
+}
+
+
+static int
+amfs_error_fmount(mntfs *mf)
+{
+ return ENOENT;
+}
+
+
+static int
+amfs_error_fumount(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_lookuppn(am_node *mp, char *fname, int *error_return, int op)
+{
+ *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, int count)
+{
+ return ESTALE;
+}
+
+
+/*
+ * umounted() callback for EFS.
+ *
+ * This prevents core-dumps on callbacks to error file-systems from
+ * nfsx_fumount.
+ */
+static void
+amfs_error_umounted(am_node *mp)
+{
+ /* nothing to do */
+}
diff --git a/contrib/amd/amd/amfs_host.c b/contrib/amd/amd/amfs_host.c
new file mode 100644
index 0000000..6be259d
--- /dev/null
+++ b/contrib/amd/amd/amfs_host.c
@@ -0,0 +1,686 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: amfs_host.c,v 5.2.2.2 1992/05/31 16:36:08 jsp Exp $
+ *
+ */
+
+/*
+ * 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_fmount(mntfs *mf);
+static int amfs_host_fumount(mntfs *mf);
+static int amfs_host_init(mntfs *mf);
+static void amfs_host_umounted(am_node *mp);
+
+/*
+ * Ops structure
+ */
+am_ops amfs_host_ops =
+{
+ "host",
+ amfs_host_match,
+ amfs_host_init,
+ amfs_auto_fmount,
+ amfs_host_fmount,
+ amfs_auto_fumount,
+ amfs_host_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* amfs_host_readlink */
+ 0, /* amfs_host_mounted */
+ amfs_host_umounted,
+ find_nfs_srvr,
+ FS_MKMNT | FS_BACKGROUND | FS_AMQINFO
+};
+
+
+/*
+ * 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, const exports ex, const mntfs *mf)
+{
+ if (ex->ex_dir[0] == '/') {
+ if (ex->ex_dir[1] == 0)
+ strcpy(mntpt, (mf)->mf_mount);
+ else
+ sprintf(mntpt, "%s%s", mf->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)
+ sprintf(mntpt, "%s/%c%%", mf->mf_mount, ex->ex_dir[0]);
+ else
+ sprintf(mntpt, "%s/%s", mf->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)
+{
+ fserver *fs;
+ u_short 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
+ * nfs_srvr_port with our mntfs passed as the wait channel.
+ * nfs_srvr_port will check some things and then schedule
+ * it so that when the fileserver is ready, a wakeup is done
+ * on this mntfs. amfs_auto_cont() is already sleeping on this mntfs
+ * so as soon as that wakeup happens amfs_auto_cont() is called and
+ * this mount is retried.
+ */
+ if ((fs = 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.
+ */
+ (void) nfs_srvr_port(fs, &port, (voidp) mf);
+
+ return 0;
+}
+
+
+static int
+do_mount(am_nfs_handle_t *fhp, char *dir, char *fs_name, char *opts, mntfs *mf)
+{
+ struct stat stb;
+
+#ifdef DEBUG
+ dlog("amfs_host: mounting fs %s on %s\n", fs_name, dir);
+#endif /* DEBUG */
+
+ (void) mkdirs(dir, 0555);
+ if (stat(dir, &stb) < 0 || (stb.st_mode & S_IFMT) != S_IFDIR) {
+ plog(XLOG_ERROR, "No mount point for %s - skipping", dir);
+ return ENOENT;
+ }
+
+ return mount_nfs_fh(fhp, dir, fs_name, opts, 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;
+
+ /*
+ * Pick a number, any number...
+ */
+ tv.tv_sec = 20;
+ tv.tv_usec = 0;
+
+#ifdef DEBUG
+ dlog("Fetching fhandle for %s", dir);
+#endif /* DEBUG */
+
+ /*
+ * 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", nfs_version);
+#ifdef HAVE_FS_NFS3
+ if (nfs_version == NFS_VERSION3) {
+ memset((char *) &fhp->v3, 0, sizeof(fhp->v3));
+ clnt_stat = clnt_call(client,
+ MOUNTPROC_MNT,
+ (XDRPROC_T_TYPE) xdr_dirpath,
+ (SVC_IN_ARG_TYPE) &dir,
+ (XDRPROC_T_TYPE) xdr_mountres3,
+ (SVC_IN_ARG_TYPE) &fhp->v3,
+ 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 = fhp->v3.fhs_status)) {
+#ifdef DEBUG
+ dlog("fhandle fetch for mount version 3 failed: %m");
+#endif /* DEBUG */
+ return errno;
+ }
+ } 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) &fhp->v2,
+ tv);
+ if (clnt_stat != RPC_SUCCESS) {
+ char *msg = clnt_sperrno(clnt_stat);
+ plog(XLOG_ERROR, "mountd rpc failed: %s", msg);
+ return EIO;
+ }
+ /* Check status of filehandle */
+ if (fhp->v2.fhs_status) {
+ errno = fhp->v2.fhs_status;
+#ifdef DEBUG
+ dlog("fhandle fetch for mount version 1 failed: %m");
+#endif /* DEBUG */
+ return errno;
+ }
+#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;
+}
+
+
+/*
+ * Mount the export tree from a host
+ */
+static int
+amfs_host_fmount(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;
+
+ /*
+ * 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_fmount: NFS version %d", mf->mf_server->fs_version);
+#ifdef HAVE_FS_NFS3
+ if (mf->mf_server->fs_version == NFS_VERSION3)
+ mnt_version = 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 transmitt 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;
+
+#ifdef DEBUG
+ dlog("Fetching export list from %s", host);
+#endif /* DEBUG */
+
+ /*
+ * 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) {
+ char *msg = clnt_sperrno(clnt_stat);
+ plog(XLOG_ERROR, "host_fmount 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) {
+ /* printf("export %s\n", ex->ex_dir); */
+ 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, ex, mf);
+ if (!already_mounted(mlist, mntpt))
+ 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)) {
+#ifdef DEBUG
+ dlog("avoiding dup fhandle requested for %s", ep[j]->ex_dir);
+#endif /* DEBUG */
+ 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.
+ */
+ strncpy(fs_name, mf->mf_info, sizeof(fs_name));
+ if ((rfs_dir = strchr(fs_name, ':')) == (char *) 0) {
+ plog(XLOG_FATAL, "amfs_host_fmount: mf_info has no colon");
+ error = EINVAL;
+ goto out;
+ }
+ ++rfs_dir;
+ for (j = 0; j < n_export; j++) {
+ ex = ep[j];
+ if (ex) {
+ strcpy(rfs_dir, ex->ex_dir);
+ make_mntpt(mntpt, ex, mf);
+ if (do_mount(&fp[j], mntpt, fs_name, mf->mf_mopts, 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_fumount(mntfs *mf)
+{
+ mntlist *ml, *mprev;
+ 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;
+#ifdef DEBUG
+ dlog("amfs_host: unmounts %s", dir);
+#endif /* DEBUG */
+ /*
+ * Unmount "dir"
+ */
+ error = UMOUNT_FS(dir, mnttab_file_name);
+ /*
+ * Keep track of errors
+ */
+ if (error) {
+ if (!xerror)
+ 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_fmount(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(am_node *mp)
+{
+ mntfs *mf = mp->am_mnt;
+ 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;
+
+ /*
+ * 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", mf->mf_server->fs_version);
+#ifdef HAVE_FS_NFS3
+ if (mf->mf_server->fs_version == NFS_VERSION3)
+ mnt_version = 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;
+
+#ifdef DEBUG
+ dlog("Unmounting all from %s", host);
+#endif /* DEBUG */
+
+ 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 ... */
+ char *msg = clnt_sperrno(clnt_stat);
+ plog(XLOG_ERROR, "unmount all from %s rpc failed: %s", host, msg, clnt_stat);
+ goto out;
+ }
+
+out:
+ if (sock != RPC_ANYSOCK)
+ (void) amu_close(sock);
+ if (client)
+ clnt_destroy(client);
+}
diff --git a/contrib/amd/amd/amfs_inherit.c b/contrib/amd/amd/amfs_inherit.c
new file mode 100644
index 0000000..e9709bd
--- /dev/null
+++ b/contrib/amd/amd/amfs_inherit.c
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: amfs_inherit.c,v 5.2.2.1 1992/02/09 15:08:26 jsp beta $
+ *
+ */
+
+/*
+ * Inheritance file system.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/*
+ * This implements a filesystem restart.
+ *
+ * This is a *gross* hack - it knows far too
+ * much about the way other parts of the
+ * system work. See restart.c too.
+ */
+
+static char *amfs_inherit_match(am_opts *fo);
+static int amfs_inherit_fmount(mntfs *mf);
+static int amfs_inherit_fumount(mntfs *mf);
+static int amfs_inherit_init(mntfs *mf);
+static int amfs_inherit_mount(am_node *mp);
+
+
+/*
+ * Ops structure
+ */
+am_ops amfs_inherit_ops =
+{
+ "inherit",
+ amfs_inherit_match,
+ amfs_inherit_init,
+ amfs_inherit_mount,
+ amfs_inherit_fmount,
+ amfs_auto_fumount,
+ amfs_inherit_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* amfs_inherit_readlink */
+ 0, /* amfs_inherit_mounted */
+ 0, /* amfs_inherit_umounted */
+ find_amfs_auto_srvr,
+ FS_DISCARD
+};
+
+
+/*
+ * This should never be called.
+ */
+static char *
+amfs_inherit_match(am_opts *fo)
+{
+ plog(XLOG_FATAL, "amfs_inherit_match called!");
+ return 0;
+}
+
+
+static int
+amfs_inherit_init(mntfs *mf)
+{
+ mntfs *mf_link = (mntfs *) mf->mf_private;
+
+ if (mf_link == 0) {
+ plog(XLOG_ERROR, "Remount collision on %s?", mf->mf_mount);
+ plog(XLOG_FATAL, "Attempting to init not-a-filesystem");
+ return EINVAL;
+ }
+
+ if (mf_link->mf_ops->fs_init)
+ return (*mf_link->mf_ops->fs_init) (mf_link);
+ return 0;
+}
+
+
+/*
+ * Take the linked mount point and
+ * propogate.
+ */
+static mntfs *
+amfs_inherit_inherit(mntfs *mf)
+{
+ mntfs *mf_link = (mntfs *) mf->mf_private;
+
+ if (mf_link == 0) {
+ plog(XLOG_FATAL, "Attempting to inherit not-a-filesystem");
+ return 0; /* XXX */
+ }
+ mf_link->mf_fo = mf->mf_fo;
+
+ /*
+ * Discard the old map.
+ * Don't call am_unmounted since this
+ * node was never really mounted in the
+ * first place.
+ */
+ mf->mf_private = 0;
+ free_mntfs(mf);
+
+ /*
+ * Free the dangling reference
+ * to the mount link.
+ */
+ free_mntfs(mf_link);
+
+ /*
+ * Get a hold of the other entry
+ */
+ mf_link->mf_flags &= ~MFF_RESTART;
+
+ /* Say what happened */
+ plog(XLOG_INFO, "restarting %s on %s", mf_link->mf_info, mf_link->mf_mount);
+
+ return mf_link;
+}
+
+
+static int
+amfs_inherit_mount(am_node *mp)
+{
+ mntfs *newmf = amfs_inherit_inherit(mp->am_mnt);
+
+ if (newmf) {
+ mp->am_mnt = newmf;
+ /*
+ * XXX - must do the am_mounted call here
+ */
+ if (newmf->mf_ops->fs_flags & FS_MBACKGROUND)
+ am_mounted(mp);
+
+ new_ttl(mp);
+ return 0;
+ }
+ return EINVAL;
+}
+
+
+static int
+amfs_inherit_fmount(mntfs *mf)
+{
+ am_node *mp = find_mf(mf);
+
+ if (mp)
+ return amfs_inherit_mount(mp);
+ return amfs_inherit_inherit(mf) ? 0 : EINVAL;
+}
+
+
+static int
+amfs_inherit_fumount(mntfs *mf)
+{
+ /*
+ * Always succeed
+ */
+ return 0;
+}
diff --git a/contrib/amd/amd/amfs_link.c b/contrib/amd/amd/amfs_link.c
new file mode 100644
index 0000000..251c5eb
--- /dev/null
+++ b/contrib/amd/amd/amfs_link.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: amfs_link.c,v 5.2.2.1 1992/02/09 15:09:04 jsp beta $
+ *
+ */
+
+/*
+ * Symbol-link file system
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+
+/*
+ * Ops structures
+ */
+am_ops amfs_link_ops =
+{
+ "link",
+ amfs_link_match,
+ 0, /* amfs_link_init */
+ amfs_auto_fmount,
+ amfs_link_fmount,
+ amfs_auto_fumount,
+ amfs_link_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* amfs_link_readlink */
+ 0, /* amfs_link_mounted */
+ 0, /* amfs_link_umounted */
+ find_amfs_auto_srvr,
+ 0
+};
+
+
+/*
+ * SFS needs a link.
+ */
+char *
+amfs_link_match(am_opts *fo)
+{
+
+ if (!fo->opt_fs) {
+ plog(XLOG_USER, "link: no fs specified");
+ return 0;
+ }
+
+ /*
+ * Bug report (14/12/89) from Jay Plett <jay@princeton.edu>
+ * If an automount point has the same name as an existing
+ * link type mount Amd hits a race condition and either hangs
+ * or causes a symlink loop.
+ *
+ * If fs begins with a '/' change the opt_fs & opt_sublink
+ * fields so that the fs option doesn't end up pointing at
+ * an existing symlink.
+ *
+ * If sublink is nil then set sublink to fs
+ * else set sublink to fs / sublink
+ *
+ * Finally set fs to ".".
+ */
+ if (*fo->opt_fs == '/') {
+ char *fullpath;
+ char *link = fo->opt_sublink;
+ if (link) {
+ if (*link == '/')
+ fullpath = strdup(link);
+ else
+ fullpath = str3cat((char *) 0, fo->opt_fs, "/", link);
+ } else {
+ fullpath = strdup(fo->opt_fs);
+ }
+
+ if (fo->opt_sublink)
+ XFREE(fo->opt_sublink);
+ fo->opt_sublink = fullpath;
+ fo->opt_fs = str3cat(fo->opt_fs, ".", fullpath, "");
+ }
+
+ return strdup(fo->opt_fs);
+}
+
+
+int
+amfs_link_fmount(mntfs *mf)
+{
+ /*
+ * Wow - this is hard to implement! :-)
+ */
+ return 0;
+}
+
+
+int
+amfs_link_fumount(mntfs *mf)
+{
+ return 0;
+}
diff --git a/contrib/amd/amd/amfs_linkx.c b/contrib/amd/amd/amfs_linkx.c
new file mode 100644
index 0000000..e46206f
--- /dev/null
+++ b/contrib/amd/amd/amfs_linkx.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: amfs_linkx.c,v 5.2.2.1 1992/02/09 15:09:04 jsp beta $
+ *
+ */
+
+/*
+ * 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);
+
+/*
+ * linkx operations
+ */
+struct am_ops amfs_linkx_ops =
+{
+ "linkx",
+ amfs_link_match,
+ 0, /* amfs_linkx_init */
+ amfs_linkx_mount,
+ 0,
+ amfs_auto_fumount,
+ amfs_link_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* amfs_linkx_readlink */
+ 0, /* amfs_linkx_mounted */
+ 0, /* amfs_linkx_umounted */
+ find_amfs_auto_srvr,
+ FS_MBACKGROUND
+};
+
+
+static int
+amfs_linkx_mount(am_node *mp)
+{
+ /*
+ * Check for existence of target.
+ */
+ struct stat stb;
+ char *ln;
+
+ if (mp->am_link)
+ ln = mp->am_link;
+ else /* should never occur */
+ ln = mp->am_mnt->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;
+}
+
diff --git a/contrib/amd/amd/amfs_nfsl.c b/contrib/amd/amd/amfs_nfsl.c
new file mode 100644
index 0000000..8132afd
--- /dev/null
+++ b/contrib/amd/amd/amfs_nfsl.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: amfs_nfsl.c,v 5.2.2.3 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+/*
+ * 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_fmount(mntfs *mf);
+static int amfs_nfsl_fumount(mntfs *mf);
+static void amfs_nfsl_umounted(am_node *mp);
+static fserver *amfs_nfsl_ffserver(mntfs *mf);
+
+/*
+ * NFS-Link operations
+ */
+am_ops amfs_nfsl_ops =
+{
+ "nfsl", /* name of file system */
+ amfs_nfsl_match, /* match */
+ amfs_nfsl_init, /* initialize */
+ amfs_auto_fmount, /* mount vnode */
+ amfs_nfsl_fmount, /* mount vfs */
+ amfs_auto_fumount, /* unmount vnode */
+ amfs_nfsl_fumount, /* unmount VFS */
+ amfs_error_lookuppn, /* lookup path-name */
+ amfs_error_readdir, /* read directory */
+ 0, /* read link */
+ 0, /* after-mount extra actions */
+ amfs_nfsl_umounted, /* after-umount extra actions */
+ amfs_nfsl_ffserver, /* find a file server */
+ FS_MKMNT | FS_BACKGROUND | FS_AMQINFO /* flags */
+};
+
+
+/*
+ * 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 = fo->opt_fs;
+ char *ho = fo->opt_rhost;
+ struct stat stb;
+
+ if (!cp || !ho) {
+ plog(XLOG_USER, "amfs_nfsl: nost $fs and $rhost must be specified");
+ return NULL;
+ }
+
+ /*
+ * If this host is not the same as $rhost, or if link does not exist,
+ * perform nfs_match(), same as for type:=nfs.
+ * If link value exists (or same host), then perform amfs_link_match(),
+ * same as for linkx.
+ */
+ if (!STRCEQ(ho, hostname)) {
+ plog(XLOG_INFO, "amfs_nfsl: \"%s\" is not local host, using type:=nfs", ho);
+ return nfs_match(fo);
+ } else if (lstat(cp, &stb) < 0) {
+ plog(XLOG_INFO, "amfs_nfsl: \"%s\" does not exist, using type:=nfs", cp);
+ return nfs_match(fo);
+ } else {
+ plog(XLOG_INFO, "amfs_nfsl: \"%s\" exists, using type:=link", cp);
+ return amfs_link_match(fo);
+ }
+}
+
+
+/*
+ * Initialize.
+ * Returns: 0 if OK, non-zero (errno) if failed.
+ */
+static int
+amfs_nfsl_init(mntfs *mf)
+{
+ /*
+ * If a link, do nothing (same as type:=link).
+ * If non-link, do nfs_init (same as type:=nfs).
+ */
+ if (mf->mf_flags & MFF_NFSLINK) {
+ return 0;
+ } else {
+ return nfs_init(mf);
+ }
+}
+
+
+/*
+ * Mount vfs.
+ * Returns: 0 if OK, non-zero (errno) if failed.
+ */
+static int
+amfs_nfsl_fmount(mntfs *mf)
+{
+ /*
+ * If a link, do run amfs_link_fmount() (same as type:=link)
+ * If non-link, do nfs_fmount (same as type:=nfs).
+ */
+ if (mf->mf_flags & MFF_NFSLINK) {
+ return amfs_link_fmount(mf);
+ } else {
+ return nfs_fmount(mf);
+ }
+}
+
+
+/*
+ * Unmount VFS.
+ * Returns: 0 if OK, non-zero (errno) if failed.
+ */
+static int
+amfs_nfsl_fumount(mntfs *mf)
+{
+ /*
+ * If a link, do run amfs_link_fumount() (same as type:=link)
+ * If non-link, do nfs_fumount (same as type:=nfs).
+ */
+ if (mf->mf_flags & MFF_NFSLINK) {
+ return amfs_link_fumount(mf);
+ } else {
+ return nfs_fumount(mf);
+ }
+}
+
+
+/*
+ * 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(am_node *mp)
+{
+ mntfs *mf = mp->am_mnt;
+
+ /*
+ * If a link, do nothing (same as type:=link)
+ * If non-link, do nfs_fumount (same as type:=nfs).
+ */
+ if (mf->mf_flags & MFF_NFSLINK) {
+ return;
+ } else {
+ nfs_umounted(mp);
+ /*
+ * MUST remove mount point directories, because if they remain
+ * behind, the next nfsl access will think they are a link
+ * type file system, and not NFS! (when it performs link target
+ * existence test)
+ */
+ if (mf->mf_flags & MFF_MKMNT)
+ rmdirs(mf->mf_mount);
+ return;
+ }
+}
+
+
+/*
+ * Find a file server.
+ * Returns: fserver of found server, or NULL if not found.
+ */
+static fserver *
+amfs_nfsl_ffserver(mntfs *mf)
+{
+ char *cp = mf->mf_fo->opt_fs;
+ char *ho = mf->mf_fo->opt_rhost;
+ struct stat stb;
+
+ /*
+ * If this host is not the same as $rhost, or if link does not exist,
+ * perform find_nfs_srvr(), same as for type:=nfs.
+ * If link value exists (or same host), then perform
+ * find_amfs_auto_srvr(), same as for linkx.
+ */
+ if (!STREQ(ho, hostname) || lstat(cp, &stb) < 0) {
+ return find_nfs_srvr(mf);
+ } else {
+ mf->mf_flags |= MFF_NFSLINK;
+ return find_amfs_auto_srvr(mf);
+ }
+}
diff --git a/contrib/amd/amd/amfs_nfsx.c b/contrib/amd/amd/amfs_nfsx.c
new file mode 100644
index 0000000..272e10d
--- /dev/null
+++ b/contrib/amd/amd/amfs_nfsx.c
@@ -0,0 +1,532 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: amfs_linxx.c,v 5.2.2.3 1992/05/31 16:13:07 jsp Exp $
+ *
+ */
+
+/*
+ * 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;
+};
+
+/* forward definitions */
+static char *amfs_nfsx_match(am_opts *fo);
+static int amfs_nfsx_fmount (mntfs *);
+static int amfs_nfsx_fmount(mntfs *mf);
+static int amfs_nfsx_fumount(mntfs *mf);
+static int amfs_nfsx_init(mntfs *mf);
+
+/*
+ * Ops structure
+ */
+am_ops amfs_nfsx_ops =
+{
+ "nfsx",
+ amfs_nfsx_match,
+ amfs_nfsx_init,
+ amfs_auto_fmount,
+ amfs_nfsx_fmount,
+ amfs_auto_fumount,
+ amfs_nfsx_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* amfs_nfsx_readlink */
+ 0, /* amfs_nfsx_mounted */
+ 0, /* amfs_nfsx_umounted */
+ find_nfs_srvr, /* XXX */
+ /* FS_UBACKGROUND| */ FS_AMQINFO
+};
+
+
+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);
+#ifdef DEBUG
+ dlog("NFS: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"",
+ fo->opt_rhost, fo->opt_rfs, fo->opt_fs);
+#endif /* DEBUG */
+
+ return xmtab;
+}
+
+
+static void
+amfs_nfsx_prfree(voidp 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++) ;
+
+ nx = ALLOC(struct amfs_nfsx);
+ mf->mf_private = (voidp) 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));
+ {
+ 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);
+#ifdef DEBUG
+ dlog("amfs_nfsx: init mount for %s on %s", xinfo, mp);
+#endif /* DEBUG */
+ 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);
+ }
+ 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 = (*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, (voidp) mf, (voidp) m);
+ }
+ }
+ }
+
+ return glob_error;
+}
+
+
+static void
+amfs_nfsx_cont(int rc, int term, voidp closure)
+{
+ mntfs *mf = (mntfs *) closure;
+ struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
+ 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((voidp) 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);
+ n->n_error = 0;
+ }
+
+ /*
+ * Do the remaining bits
+ */
+ if (amfs_nfsx_fmount(mf) >= 0) {
+ wakeup((voidp) mf);
+ mf->mf_flags &= ~MFF_MOUNTING;
+ mf_mounted(mf);
+ }
+}
+
+
+static int
+try_amfs_nfsx_mount(voidp mv)
+{
+ mntfs *mf = (mntfs *) mv;
+ int error;
+
+ mf->mf_flags |= MFF_MOUNTING;
+ error = (*mf->mf_ops->fmount_fs) (mf);
+ mf->mf_flags &= ~MFF_MOUNTING;
+
+ return error;
+}
+
+
+static int
+amfs_nfsx_remount(mntfs *mf, int fg)
+{
+ struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
+ amfs_nfsx_mnt *n;
+ int glob_error = -1;
+
+ for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
+ mntfs *m = n->n_mnt;
+ if (n->n_error < 0) {
+ if (!(m->mf_flags & MFF_MKMNT) && m->mf_ops->fs_flags & FS_MKMNT) {
+ int error = mkdirs(m->mf_mount, 0555);
+ if (!error)
+ m->mf_flags |= MFF_MKMNT;
+ }
+ }
+ }
+
+ /*
+ * 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 (n->n_error < 0) {
+ /*
+ * Check fmount entry pt. exists
+ * and then mount...
+ */
+ if (!m->mf_ops->fmount_fs) {
+ n->n_error = EINVAL;
+ } else {
+#ifdef DEBUG
+ dlog("calling underlying fmount on %s", m->mf_mount);
+#endif /* DEBUG */
+ if (!fg && foreground && (m->mf_ops->fs_flags & FS_MBACKGROUND)) {
+ m->mf_flags |= MFF_MOUNTING; /* XXX */
+#ifdef DEBUG
+ dlog("backgrounding mount of \"%s\"", m->mf_info);
+#endif /* DEBUG */
+ nx->nx_try = n;
+ run_task(try_amfs_nfsx_mount, (voidp) m, amfs_nfsx_cont, (voidp) mf);
+ n->n_error = -1;
+ return -1;
+ } else {
+#ifdef DEBUG
+ dlog("foreground mount of \"%s\" ...", mf->mf_info);
+#endif /* DEBUG */
+ n->n_error = (*m->mf_ops->fmount_fs) (m);
+ }
+ }
+
+#ifdef DEBUG
+ if (n->n_error > 0) {
+ errno = n->n_error; /* XXX */
+ dlog("underlying fmount of %s failed: %m", m->mf_mount);
+ }
+#endif /* DEBUG */
+
+ 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_fmount(mntfs *mf)
+{
+ return amfs_nfsx_remount(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_fumount(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 occured 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) {
+#ifdef DEBUG
+ dlog("calling underlying fumount on %s", m->mf_mount);
+#endif /* DEBUG */
+ n->n_error = (*m->mf_ops->fumount_fs) (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(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;
+ am_node am;
+
+ /*
+ * XXX: all the umounted handler needs is a
+ * mntfs pointer, so pass an am_node with the right
+ * pointer in it.
+ */
+ memset((voidp) &am, 0, sizeof(am));
+ am.am_mnt = m;
+#ifdef DEBUG
+ dlog("calling underlying umounted on %s", m->mf_mount);
+#endif /* DEBUG */
+ (*m->mf_ops->umounted) (&am);
+
+ if (n->n_error < 0) {
+ if (m->mf_ops->fs_flags & 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/contrib/amd/amd/amfs_program.c b/contrib/amd/amd/amfs_program.c
new file mode 100644
index 0000000..2bd0778
--- /dev/null
+++ b/contrib/amd/amd/amfs_program.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: amfs_program.c,v 5.2.2.1 1992/02/09 15:08:56 jsp beta $
+ *
+ */
+
+/*
+ * 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_fmount(mntfs *mf);
+static int amfs_program_fumount(mntfs *mf);
+static int amfs_program_init(mntfs *mf);
+
+/*
+ * Ops structure
+ */
+am_ops amfs_program_ops =
+{
+ "program",
+ amfs_program_match,
+ amfs_program_init,
+ amfs_auto_fmount,
+ amfs_program_fmount,
+ amfs_auto_fumount,
+ amfs_program_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* amfs_program_readlink */
+ 0, /* amfs_program_mounted */
+ 0, /* amfs_program_umounted */
+ find_amfs_auto_srvr,
+ FS_BACKGROUND | FS_AMQINFO
+};
+
+
+/*
+ * Execute needs a mount and unmount command.
+ */
+static char *
+amfs_program_match(am_opts *fo)
+{
+ char *prog;
+
+ if (!fo->opt_mount || !fo->opt_unmount) {
+ plog(XLOG_USER, "program: no mount/unmount specified");
+ return 0;
+ }
+ prog = strchr(fo->opt_mount, ' ');
+
+ return strdup(prog ? prog + 1 : fo->opt_mount);
+}
+
+
+static int
+amfs_program_init(mntfs *mf)
+{
+ /*
+ * Save unmount command
+ */
+ if (mf->mf_refc == 1) {
+ mf->mf_private = (voidp) strdup(mf->mf_fo->opt_unmount);
+ mf->mf_prfree = (void (*)(voidp)) 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);
+ (void) dup(fileno(logfp));
+ if (fileno(logfp) != fileno(stderr)) {
+ (void) fclose(stderr);
+ (void) dup(fileno(logfp));
+ }
+
+ /*
+ * Try the exec
+ */
+#ifdef DEBUG
+ amuDebug(D_FULL) {
+ char **cp = xivec;
+ plog(XLOG_DEBUG, "executing (un)mount command...");
+ while (*cp) {
+ plog(XLOG_DEBUG, "arg[%d] = '%s'", cp - xivec, *cp);
+ cp++;
+ }
+ }
+#endif /* DEBUG */
+
+ 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_fmount(mntfs *mf)
+{
+ return amfs_program_exec(mf->mf_fo->opt_mount);
+}
+
+
+static int
+amfs_program_fumount(mntfs *mf)
+{
+ return amfs_program_exec((char *) mf->mf_private);
+}
diff --git a/contrib/amd/amd/amfs_root.c b/contrib/amd/amd/amfs_root.c
new file mode 100644
index 0000000..3c1cd17
--- /dev/null
+++ b/contrib/amd/amd/amfs_root.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: amfs_root.c,v 1.1 1997-1998/06/30 19:22:30 ezk Exp ezk $
+ *
+ */
+
+/*
+ * 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);
+
+/****************************************************************************
+ *** OPS STRUCTURES ***
+ ****************************************************************************/
+am_ops amfs_root_ops =
+{
+ "root",
+ 0, /* amfs_root_match */
+ 0, /* amfs_root_init */
+ amfs_root_mount,
+ 0,
+ amfs_auto_umount,
+ 0,
+ amfs_auto_lookuppn,
+ amfs_auto_readdir,
+ 0, /* amfs_root_readlink */
+ 0, /* amfs_root_mounted */
+ 0, /* amfs_root_umounted */
+ find_amfs_auto_srvr,
+ FS_NOTIMEOUT | FS_AMQINFO | FS_DIRECTORY
+};
+
+
+/****************************************************************************
+ *** FUNCTIONS ***
+ ****************************************************************************/
+
+/*
+ * Mount the root...
+ */
+static int
+amfs_root_mount(am_node *mp)
+{
+ mntfs *mf = mp->am_mnt;
+
+ mf->mf_mount = strealloc(mf->mf_mount, pid_fsname);
+ mf->mf_private = (voidp) mapc_find(mf->mf_info, "", NULL);
+ mf->mf_prfree = mapc_free;
+
+ return 0;
+}
diff --git a/contrib/amd/amd/amfs_toplvl.c b/contrib/amd/amd/amfs_toplvl.c
new file mode 100644
index 0000000..f36c66f
--- /dev/null
+++ b/contrib/amd/amd/amfs_toplvl.c
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: amfs_toplvl.c,v 1.1 1997-1998/06/30 19:22:30 ezk Exp ezk $
+ *
+ */
+
+/*
+ * Top-level file system
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/****************************************************************************
+ *** FORWARD DEFINITIONS ***
+ ****************************************************************************/
+
+
+/****************************************************************************
+ *** OPS STRUCTURES ***
+ ****************************************************************************/
+am_ops amfs_toplvl_ops =
+{
+ "toplvl",
+ amfs_auto_match,
+ 0, /* amfs_auto_init */
+ amfs_toplvl_mount,
+ 0,
+ amfs_toplvl_umount,
+ 0,
+ amfs_auto_lookuppn,
+ amfs_auto_readdir, /* browsable version of readdir() */
+ 0, /* amfs_toplvl_readlink */
+ amfs_toplvl_mounted,
+ 0, /* amfs_toplvl_umounted */
+ find_amfs_auto_srvr,
+ FS_MKMNT | FS_NOTIMEOUT | FS_BACKGROUND | FS_AMQINFO | FS_DIRECTORY
+};
+
+
+/****************************************************************************
+ *** FUNCTIONS ***
+ ****************************************************************************/
+
+/*
+ * Mount an automounter directory.
+ * The automounter is connected into the system
+ * as a user-level NFS server. mount_amfs_toplvl 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.
+ */
+static int
+mount_amfs_toplvl(char *dir, char *opts)
+{
+ char fs_hostname[MAXHOSTNAMELEN + MAXPATHLEN + 1];
+ int retry, error, genflags;
+ mntent_t mnt;
+ nfs_args_t nfs_args;
+ am_nfs_fh *fhp;
+ am_nfs_handle_t anh;
+ MTYPE_TYPE type = MOUNT_TYPE_NFS;
+#ifndef HAVE_TRANSPORT_TYPE_TLI
+ u_short port;
+ struct sockaddr_in sin;
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+
+ memset((voidp) &mnt, 0, sizeof(mnt));
+ mnt.mnt_dir = dir;
+ mnt.mnt_fsname = pid_fsname;
+ mnt.mnt_opts = opts;
+
+ /*
+ * 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 */
+
+ /*
+ * SET MOUNT ARGS
+ */
+ /*
+ * get fhandle of remote path for automount point
+ */
+ fhp = root_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. 127.0.0.1
+ * is not used since that will not work in HP-UX clusters and
+ * this is no more expensive.
+ */
+ memset((voidp) &sin, 0, sizeof(sin));
+ 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 */
+
+ /*
+ * Make a ``hostname'' string for the kernel
+ */
+ sprintf(fs_hostname, "pid%ld@%s:%s",
+ (long) (foreground ? mypid : getppid()),
+ hostname,
+ dir);
+ /*
+ * Most kernels have a name length restriction (64 bytes)...
+ */
+ if (strlen(fs_hostname) >= MAXHOSTNAMELEN)
+ strcpy(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)
+ strcpy(fs_hostname + HOSTNAMESZ - 3, "..");
+#endif /* HOSTNAMESZ */
+
+ /*
+ * Finally we can compute the mount genflags set above.
+ */
+ genflags = compute_mount_flags(&mnt);
+
+ /* setup the many fields and flags within nfs_args */
+ memmove(&anh.v2.fhs_fh, 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,
+ &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, and so some options must be corrected by hand *
+ * more carefully, *after* compute_nfs_args() runs. *
+ *************************************************************************/
+ compute_automounter_nfs_args(&nfs_args, &mnt);
+
+ /* This is it! Here we try to mount amd on its mount points */
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ print_nfs_args(&nfs_args, 0);
+#endif /* DEBUG */
+ error = mount_fs(&mnt, genflags, (caddr_t) &nfs_args, retry, type,
+ 0, NULL, mnttab_file_name);
+
+#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 */
+
+ return error;
+}
+
+
+/*
+ * Mount the top-level
+ */
+int
+amfs_toplvl_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_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;
+ }
+ if (mf->mf_ops == &amfs_toplvl_ops)
+ mnttype = "indirect";
+ else if (mf->mf_ops == &amfs_direct_ops)
+ mnttype = "direct";
+#ifdef HAVE_AM_FS_UNION
+ else if (mf->mf_ops == &amfs_union_ops)
+ mnttype = "union";
+#endif /* HAVE_AM_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_amfs_toplvl(mf->mf_mount, opts);
+ if (error) {
+ errno = error;
+ plog(XLOG_FATAL, "mount_amfs_toplvl: %m");
+ return error;
+ }
+ return 0;
+}
+
+
+void
+amfs_toplvl_mounted(mntfs *mf)
+{
+ amfs_auto_mkcacheref(mf);
+}
+
+
+/*
+ * Unmount a top-level automount node
+ */
+int
+amfs_toplvl_umount(am_node *mp)
+{
+ int error;
+ struct stat stb;
+
+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) {
+#ifdef DEBUG
+ dlog("lstat(%s): %m", mp->am_path);
+#endif /* DEBUG */
+ }
+ error = UMOUNT_FS(mp->am_path, mnttab_file_name);
+ if (error == EBUSY) {
+ plog(XLOG_WARNING, "amfs_toplvl_unmount retrying %s in 1s", mp->am_path);
+ sleep(1); /* XXX */
+ goto again;
+ }
+ return error;
+}
diff --git a/contrib/amd/amd/amfs_union.c b/contrib/amd/amd/amfs_union.c
new file mode 100644
index 0000000..95fc8fd
--- /dev/null
+++ b/contrib/amd/amd/amfs_union.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: amfs_union.c,v 1.1 1997-1998/06/30 19:22:30 ezk Exp ezk $
+ *
+ */
+
+/*
+ * 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 void amfs_union_mounted(mntfs *mf);
+
+
+/****************************************************************************
+ *** OPS STRUCTURES ***
+ ****************************************************************************/
+am_ops amfs_union_ops =
+{
+ "union",
+ amfs_auto_match,
+ 0, /* amfs_auto_init */
+ amfs_toplvl_mount,
+ 0,
+ amfs_toplvl_umount,
+ 0,
+ amfs_auto_lookuppn,
+ amfs_auto_readdir,
+ 0, /* amfs_toplvl_readlink */
+ amfs_union_mounted,
+ 0, /* amfs_toplvl_umounted */
+ find_amfs_auto_srvr,
+ FS_MKMNT | FS_NOTIMEOUT | FS_BACKGROUND | FS_AMQINFO | FS_DIRECTORY
+};
+
+
+/*
+ * Create a reference to a union'ed entry
+ * XXX: this function may not be used anywhere...
+ */
+static int
+create_amfs_union_node(char *dir, voidp arg)
+{
+ if (!STREQ(dir, "/defaults")) {
+ int error = 0;
+ (void) amfs_toplvl_ops.lookuppn(arg, dir, &error, VLOOK_CREATE);
+ if (error > 0) {
+ errno = error; /* XXX */
+ plog(XLOG_ERROR, "Could not mount %s: %m", dir);
+ }
+ return error;
+ }
+ return 0;
+}
+
+
+static void
+amfs_union_mounted(mntfs *mf)
+{
+ int i;
+
+ amfs_auto_mkcacheref(mf);
+
+ /*
+ * Having made the union mount point,
+ * populate all the entries...
+ */
+ for (i = 0; i <= last_used_map; i++) {
+ am_node *mp = exported_ap[i];
+ if (mp && 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,
+ (void (*)(char *, voidp)) create_amfs_union_node,
+ mp);
+ break;
+ }
+ }
+}
diff --git a/contrib/amd/amd/amq_subr.c b/contrib/amd/amd/amq_subr.c
new file mode 100644
index 0000000..775ee81
--- /dev/null
+++ b/contrib/amd/amd/amq_subr.c
@@ -0,0 +1,501 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: amq_subr.c,v 5.2.2.1 1992/02/09 15:08:18 jsp beta $
+ *
+ */
+/*
+ * Auxilliary 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 *) &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 *) &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;
+
+ aml.amq_mount_tree_list_val = (amq_mount_tree_p *) &exported_ap[0];
+ 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:
+#ifdef DEBUG
+ if (debug_option(opt->as_str))
+#endif /* DEBUG */
+ 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))
+ 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 *) 0);
+ flush_srvr_nfs_cache();
+ }
+ break;
+ }
+
+ return &rc;
+}
+
+
+amq_mount_info_list *
+amqproc_getmntfs_1_svc(voidp argp, struct svc_req *rqstp)
+{
+ return (amq_mount_info_list *) &mfhead; /* XXX */
+}
+
+#ifdef ENABLE_AMQ_MOUNT
+/*
+ * This is code that is vulnerable to IP spoofing attacks. Unless you
+ * absolutely need it, I suggest you do not enable it
+ * (using configure --enable-amq-mount)
+ */
+static int
+ok_security(struct svc_req *rqstp)
+{
+ struct sockaddr_in *sin = (struct sockaddr_in *) NULL;
+
+ if ((sin = amu_svc_getcaller(rqstp->rq_xprt)) == NULL) {
+ plog(XLOG_ERROR, "amu_svc_getcaller returned NULL");
+ return(0); /* assume security is therefore not OK */
+ }
+
+ if (ntohs(sin->sin_port) >= 1024 ||
+ !(sin->sin_addr.s_addr == htonl(0x7f000001) ||
+ sin->sin_addr.s_addr == myipaddr.s_addr)) {
+ char dq[20];
+ plog(XLOG_INFO, "AMQ request from %s.%d DENIED",
+ inet_dquad(dq, sin->sin_addr.s_addr),
+ ntohs(sin->sin_port));
+ return (0);
+ }
+
+ return (1);
+}
+
+
+int *
+amqproc_mount_1_svc(voidp argp, struct svc_req *rqstp)
+{
+ static int rc;
+ char *s = *(amq_string *) argp;
+ char *cp;
+
+ plog(XLOG_INFO, "amq requested mount of %s", s);
+ /*
+ * Minimalist security check.
+ */
+ if (!ok_security(rqstp)) {
+ rc = EACCES;
+ return &rc;
+ }
+ /*
+ * Find end of key
+ */
+ for (cp = (char *) s; *cp && (!isascii(*cp) || !isspace(*cp)); cp++) ;
+
+ if (!*cp) {
+ plog(XLOG_INFO, "amqproc_mount: Invalid arguments");
+ rc = EINVAL;
+ return &rc;
+ }
+ *cp++ = '\0';
+
+ /*
+ * Find start of value
+ */
+ while (*cp && isascii(*cp) && isspace(*cp))
+ cp++;
+
+ root_newmap(s, cp, (char *) 0, NULL);
+ rc = mount_auto_node(s, (voidp) root_node);
+ if (rc < 0)
+ return 0;
+ return &rc;
+}
+
+#else /* not ENABLE_AMQ_MOUNT */
+
+int *
+amqproc_mount_1_svc(voidp argp, struct svc_req *rqstp)
+{
+ static int rc;
+ char *s = *(amq_string *) argp;
+
+ plog(XLOG_ERROR, "amq requested mount of %s, but code is disabled", s);
+
+ rc = EINVAL;
+ return &rc;
+}
+#endif /* not ENABLE_AMQ_MOUNT */
+
+
+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;
+}
+
+
+/*
+ * XDR routines.
+ */
+
+
+bool_t
+xdr_amq_setopt(XDR *xdrs, amq_setopt *objp)
+{
+ if (!xdr_enum(xdrs, (enum_t *) & 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;
+
+ 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);
+ }
+ if (!xdr_long(xdrs, (long *) &mp->am_stats.s_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 **) &mp->am_osib, sizeof(amq_mount_tree), (XDRPROC_T_TYPE) xdr_amq_mount_subtree)) {
+ return (FALSE);
+ }
+ if (!xdr_pointer(xdrs, (char **) &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 = 0;
+
+ if (!xdr_amq_mount_tree_node(xdrs, objp)) {
+ return (FALSE);
+ }
+ if (!xdr_pointer(xdrs, (char **) &mnil, sizeof(amq_mount_tree), (XDRPROC_T_TYPE) xdr_amq_mount_subtree)) {
+ return (FALSE);
+ }
+ if (!xdr_pointer(xdrs, (char **) &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 **) &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_ops->fs_flags & 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_ops->fs_flags & 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 (mf->mf_server->fs_flags & FSF_ERROR)
+ up = 0;
+ else
+ switch (mf->mf_server->fs_flags & (FSF_DOWN | FSF_VALID)) {
+ case FSF_DOWN | FSF_VALID:
+ up = 0;
+ break;
+ case FSF_VALID:
+ up = 1;
+ break;
+ default:
+ up = -1;
+ break;
+ }
+ 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/contrib/amd/amd/amq_svc.c b/contrib/amd/amd/amq_svc.c
new file mode 100644
index 0000000..5e4c9fd
--- /dev/null
+++ b/contrib/amd/amd/amq_svc.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: amq_svc.c,v 5.2.2.1 1992/02/09 15:09:26 jsp beta $
+ *
+ */
+
+#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 *);
+
+
+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;
+ amq_string amqproc_mount_1_arg;
+ } argument;
+ char *result;
+ xdrproc_t xdr_argument, xdr_result;
+ amqsvcproc_t local;
+
+ 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_MOUNT:
+ xdr_argument = (xdrproc_t) xdr_amq_string;
+ xdr_result = (xdrproc_t) xdr_int;
+ local = (amqsvcproc_t) amqproc_mount_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;
+
+ 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/contrib/amd/amd/autil.c b/contrib/amd/amd/autil.c
new file mode 100644
index 0000000..ca089bd
--- /dev/null
+++ b/contrib/amd/amd/autil.c
@@ -0,0 +1,418 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: autil.c,v 5.2.2.2 1992/03/07 17:52:06 jsp Exp $
+ *
+ */
+
+/*
+ * 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 NumChild = 0; /* number of children of primary amd */
+static char invalid_keys[] = "\"'!;@ \t\n";
+
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+# define PARENT_USLEEP_TIME 100000 /* 0.1 seconds */
+#endif /* HAVE_TRANSPORT_TYPE_TLI */
+
+
+char *
+strealloc(char *p, char *s)
+{
+ int len = strlen(s) + 1;
+
+ p = (char *) xrealloc((voidp) p, len);
+
+ strcpy(p, s);
+#ifdef DEBUG_MEM
+ malloc_verify();
+#endif /* DEBUG_MEM */
+ return p;
+}
+
+
+char **
+strsplit(char *s, int ch, int qc)
+{
+ char **ivec;
+ int ic = 0;
+ int done = 0;
+
+ ivec = (char **) xmalloc((ic + 1) * sizeof(char *));
+
+ while (!done) {
+ char *v;
+
+ /*
+ * skip to split char
+ */
+ while (*s && (ch == ' ' ? (isascii(*s) && isspace((int)*s)) : *s == ch))
+ *s++ = '\0';
+
+ /*
+ * End of string?
+ */
+ if (!*s)
+ break;
+
+ /*
+ * remember start of string
+ */
+ v = s;
+
+ /*
+ * skip to split char
+ */
+ while (*s && !(ch == ' ' ? (isascii(*s) && isspace((int)*s)) : *s == ch)) {
+ if (*s++ == qc) {
+ /*
+ * Skip past string.
+ */
+ s++;
+ while (*s && *s != qc)
+ s++;
+ if (*s == qc)
+ s++;
+ }
+ }
+
+ if (!*s)
+ done = 1;
+ *s++ = '\0';
+
+ /*
+ * save string in new ivec slot
+ */
+ ivec[ic++] = v;
+ ivec = (char **) xrealloc((voidp) ivec, (ic + 1) * sizeof(char *));
+#ifdef DEBUG
+ amuDebug(D_STR)
+ plog(XLOG_DEBUG, "strsplit saved \"%s\"", v);
+#endif /* DEBUG */
+ }
+
+#ifdef DEBUG
+ amuDebug(D_STR)
+ plog(XLOG_DEBUG, "strsplit saved a total of %d strings", ic);
+#endif /* DEBUG */
+
+ ivec[ic] = 0;
+
+ return ivec;
+}
+
+
+/*
+ * 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
+ */
+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;
+ clock_valid = 0;
+ hp = gethostbyname(*chp);
+ if (hp && hp->h_addrtype == AF_INET) {
+#ifdef DEBUG
+ dlog("Hostname %s normalized to %s", *chp, hp->h_name);
+#endif /* DEBUG */
+ *chp = strealloc(*chp, (char *) hp->h_name);
+ }
+ }
+ 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, "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();
+ reschedule_timeout_mp();
+ }
+}
+
+
+void
+mf_mounted(mntfs *mf)
+{
+ 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);
+ }
+ mf->mf_fo = 0;
+ }
+
+ /*
+ * 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)
+{
+ mntfs *mf = mp->am_mnt;
+
+ mf_mounted(mf);
+
+ /*
+ * Patch up path for direct mounts
+ */
+ if (mp->am_parent && mp->am_parent->am_mnt->mf_ops == &amfs_direct_ops)
+ mp->am_path = str3cat(mp->am_path, mp->am_parent->am_path, "/", ".");
+
+ /*
+ * Check whether this mount should be cached permanently
+ */
+ if (mf->mf_ops->fs_flags & FS_NOTIMEOUT) {
+ mp->am_flags |= AMF_NOTIMEOUT;
+ } else if (mf->mf_mount[1] == '\0' && mf->mf_mount[0] == '/') {
+ mp->am_flags |= AMF_NOTIMEOUT;
+ } else {
+ mntent_t mnt;
+ if (mf->mf_mopts) {
+ mnt.mnt_opts = mf->mf_mopts;
+ if (hasmntopt(&mnt, "nounmount"))
+ mp->am_flags |= AMF_NOTIMEOUT;
+ if ((mp->am_timeo = hasmntval(&mnt, "utimeout")) == 0)
+ mp->am_timeo = gopt.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 : mp->am_mnt->mf_mount);
+
+ /*
+ * Record mount time
+ */
+ mp->am_fattr.na_mtime.nt_seconds = mp->am_stats.s_mtime = clocktime();
+ new_ttl(mp);
+
+ /*
+ * Update mtime of parent node
+ */
+ if (mp->am_parent && mp->am_parent->am_mnt)
+ mp->am_parent->am_fattr.na_mtime.nt_seconds = mp->am_stats.s_mtime;
+
+ /*
+ * Now, if we can, do a reply to our NFS client here
+ * to speed things up.
+ */
+ quick_reply(mp, 0);
+
+ /*
+ * Update stats
+ */
+ amd_stats.d_mok++;
+}
+
+
+int
+mount_node(am_node *mp)
+{
+ mntfs *mf = mp->am_mnt;
+ int error = 0;
+
+ mf->mf_flags |= MFF_MOUNTING;
+ error = (*mf->mf_ops->mount_fs) (mp);
+
+ mf = mp->am_mnt;
+ if (error >= 0)
+ mf->mf_flags &= ~MFF_MOUNTING;
+ if (!error && !(mf->mf_ops->fs_flags & FS_MBACKGROUND)) {
+ /* ...but see ifs_mount */
+ am_mounted(mp);
+ }
+
+ 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) (mp);
+
+ /*
+ * Update mtime of parent node
+ */
+ if (mp->am_parent && mp->am_parent->am_mnt)
+ mp->am_parent->am_fattr.na_mtime.nt_seconds = clocktime();
+
+ 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) */
+ mypid = getpid();
+ foreground = 0;
+ } else { /* parent process, has one more child */
+ NumChild++;
+ }
+
+ return pid;
+}
+
+
+int
+background(void)
+{
+ int pid = dofork();
+
+ if (pid == 0) {
+#ifdef DEBUG
+ dlog("backgrounded");
+#endif /* DEBUG */
+ foreground = 0;
+ }
+ return pid;
+}
diff --git a/contrib/amd/amd/clock.c b/contrib/amd/amd/clock.c
new file mode 100644
index 0000000..3c0494a
--- /dev/null
+++ b/contrib/amd/amd/clock.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: clock.c,v 5.2.2.1 1992/02/09 15:08:20 jsp beta $
+ *
+ */
+
+/*
+ * Callouts.
+ *
+ * Modelled 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>
+
+int timeout(u_int secs, void (*fn) (voidp), voidp closure);
+void reschedule_timeouts(time_t now, time_t then);
+
+typedef struct callout callout;
+struct callout {
+ callout *c_next; /* List of callouts */
+ void (*c_fn) (voidp); /* Function to call */
+ voidp c_closure; /* Closure 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(struct ) (++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)(closure) will be called at clocktime() + secs
+ */
+int
+timeout(u_int secs, void (*fn) (voidp), voidp closure)
+{
+ callout *cp, *cp2;
+ time_t t = clocktime() + secs;
+
+ /*
+ * Allocate and fill in a new callout structure
+ */
+ callout *cpnew = alloc_callout();
+ cpnew->c_closure = closure;
+ cpnew->c_fn = fn;
+ cpnew->c_time = t;
+ cpnew->c_id = CID_ALLOC(struct );
+
+ 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);
+#ifdef DEBUG
+ dlog("rescheduling job %d back %d seconds", cp->c_id, cp->c_time - now);
+#endif /* DEBUG */
+ 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();
+
+ /*
+ * While there are more callouts waiting...
+ */
+ while ((cp = callouts.c_next) && cp->c_time <= now) {
+ /*
+ * Extract first from list, save fn & closure 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
+ */
+ void (*fn) (voidp) = cp->c_fn;
+ voidp closure = cp->c_closure;
+
+ callouts.c_next = cp->c_next;
+ free_callout(cp);
+ (*fn) (closure);
+ }
+
+ } 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/contrib/amd/amd/conf.c b/contrib/amd/amd/conf.c
new file mode 100644
index 0000000..a97b1b1
--- /dev/null
+++ b/contrib/amd/amd/conf.c
@@ -0,0 +1,939 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: conf.c,v 5.2.2.1 1992/02/09 15:08:23 jsp beta $
+ *
+ */
+
+/*
+ * 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_dir(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_options(const char *val);
+static int gopt_dismount_interval(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_local_domain(const char *val);
+static int gopt_log_file(const char *val);
+static int gopt_log_options(const char *val);
+static int gopt_map_options(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_nfs_retransmit_counter(const char *val);
+static int gopt_nfs_retry_interval(const char *val);
+static int gopt_nis_domain(const char *val);
+static int gopt_normalize_hostnames(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_on_default(const char *val);
+static int gopt_show_statfs_entries(const char *val);
+static int gopt_unmount_on_exit(const char *val);
+static int process_global_option(const char *key, const char *val);
+static int process_regular_map(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_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 reset_cf_map(cf_map_t *cfm);
+
+
+/*
+ * STATIC VARIABLES:
+ */
+static cf_map_t cur_map;
+static struct _func_map glob_functable[] = {
+ {"arch", gopt_arch},
+ {"auto_dir", gopt_auto_dir},
+ {"browsable_dirs", gopt_browsable_dirs},
+ {"cache_duration", gopt_cache_duration},
+ {"cluster", gopt_cluster},
+ {"debug_options", gopt_debug_options},
+ {"dismount_interval", gopt_dismount_interval},
+ {"fully_qualified_hosts", gopt_fully_qualified_hosts},
+ {"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},
+ {"local_domain", gopt_local_domain},
+ {"log_file", gopt_log_file},
+ {"log_options", gopt_log_options},
+ {"map_options", gopt_map_options},
+ {"map_type", gopt_map_type},
+ {"mount_type", gopt_mount_type},
+ {"pid_file", gopt_pid_file},
+ {"portmap_program", gopt_portmap_program},
+ {"nfs_retransmit_counter", gopt_nfs_retransmit_counter},
+ {"nfs_retry_interval", gopt_nfs_retry_interval},
+ {"nis_domain", gopt_nis_domain},
+ {"normalize_hostnames", gopt_normalize_hostnames},
+ {"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_on_default},
+ {"show_statfs_entries", gopt_show_statfs_entries},
+ {"unmount_on_exit", gopt_unmount_on_exit},
+ {NULL, NULL}
+};
+
+
+/*
+ * Reset a map.
+ */
+static void
+reset_cf_map(cf_map_t *cfm)
+{
+ if (!cfm)
+ return;
+
+ if (cfm->cfm_dir) {
+ XFREE(cfm->cfm_dir);
+ cfm->cfm_dir = NULL;
+ }
+
+ if (cfm->cfm_name) {
+ XFREE(cfm->cfm_name);
+ cfm->cfm_name = NULL;
+ }
+
+ if (cfm->cfm_tag) {
+ XFREE(cfm->cfm_tag);
+ cfm->cfm_tag = NULL;
+ }
+
+ /*
+ * reset/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): Also be careful not to free() a global option.
+ * (3): I'm doing direct char* pointer comparison, and not strcmp(). This
+ * is correct!
+ */
+
+ /* initialize map_type from [global] */
+ if (cfm->cfm_type && cfm->cfm_type != gopt.map_type)
+ XFREE(cfm->cfm_type);
+ cfm->cfm_type = gopt.map_type;
+
+ /* initialize map_opts from [global] */
+ if (cfm->cfm_opts && cfm->cfm_opts != gopt.map_options)
+ XFREE(cfm->cfm_opts);
+ cfm->cfm_opts = gopt.map_options;
+
+ /* initialize search_path from [global] */
+ if (cfm->cfm_search_path && cfm->cfm_search_path != gopt.search_path)
+ XFREE(cfm->cfm_search_path);
+ 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_ENABLE_DEFAULT_SELECTORS);
+}
+
+
+/*
+ * Process configuration file options.
+ * 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 them one at a time.
+ */
+ if (STREQ(section, "global")) {
+ /*
+ * Check if a regular map was configured before "global",
+ * and process it as needed.
+ */
+ if (cur_map.cfm_dir) {
+ fprintf(stderr,"processing regular map \"%s\" before global one.\n",
+ section);
+ ret = process_regular_map(&cur_map); /* will reset map */
+ if (ret != 0)
+ return ret;
+ }
+
+ /* process the global option first */
+ ret = process_global_option(key, val);
+
+ /* reset default options for regular maps from just updated globals */
+ if (ret == 0)
+ reset_cf_map(&cur_map);
+
+ /* return status from the processing of the global option */
+ return ret;
+ }
+
+ /*
+ * otherwise save options and process a single map all at once.
+ */
+
+ /* check if we found a new map, so process one already collected */
+ if (cur_map.cfm_dir && !STREQ(cur_map.cfm_dir, section)) {
+ ret = process_regular_map(&cur_map); /* will reset map */
+ if (ret != 0)
+ return ret;
+ }
+
+ /* 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_dir(const char *val)
+{
+ gopt.auto_dir = strdup((char *)val);
+ return 0;
+}
+
+
+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_options(const char *val)
+{
+#ifdef DEBUG
+ usage += debug_option(strdup((char *)val));
+ return 0;
+#else /* not DEBUG */
+ fprintf(stderr, "%s: not compiled with DEBUG option -- sorry.\n",
+ 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_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_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 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 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_log_file(const char *val)
+{
+ gopt.logfile = strdup((char *)val);
+ return 0;
+}
+
+
+static int
+gopt_log_options(const char *val)
+{
+ usage += switch_option(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_type(const char *val)
+{
+ 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;
+ 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 = atoi(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 numver \"%s\"\n", val);
+ return 1;
+ }
+
+ set_amd_program_number(gopt.portmap_program);
+ return 0; /* all is OK */
+}
+
+
+static int
+gopt_nfs_retransmit_counter(const char *val)
+{
+ gopt.amfs_auto_retrans = atoi(val);
+ return 0;
+}
+
+
+static int
+gopt_nfs_retry_interval(const char *val)
+{
+ gopt.amfs_auto_timeo = atoi(val);
+ return 0;
+}
+
+
+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_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")) {
+ fputs(get_version_string(), stderr);
+ 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_on_default(const char *val)
+{
+ if (STREQ(val, "yes")) {
+ gopt.flags |= CFM_ENABLE_DEFAULT_SELECTORS;
+ return 0;
+ } else if (STREQ(val, "no")) {
+ gopt.flags &= ~CFM_ENABLE_DEFAULT_SELECTORS;
+ 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_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 */
+}
+
+
+/*
+ * 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_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_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)
+{
+ 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;
+ 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_regular_map(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);
+ }
+
+ reset_cf_map(cfm);
+ return 0;
+}
+
+
+/*
+ * Process last map in conf file (if any)
+ */
+int
+process_last_regular_map(void)
+{
+ /*
+ * If the amd.conf file only has a [global] section (pretty useless
+ * IMHO), do not try to process a map that does not exist.
+ */
+ if (!cur_map.cfm_dir)
+ return 0;
+ return process_regular_map(&cur_map);
+}
diff --git a/contrib/amd/amd/conf_parse.y b/contrib/amd/amd/conf_parse.y
new file mode 100644
index 0000000..2a9877a
--- /dev/null
+++ b/contrib/amd/amd/conf_parse.y
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: conf_parse.y,v 5.2.2.1 1992/02/09 15:09:35 jsp beta $
+ *
+ */
+
+%{
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+extern char *yytext;
+extern int yylineno;
+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), yylineno, (s))
+# define amu_return(v)
+#else
+# define dprintf(f,s)
+# define amu_return(v) return((v))
+#endif /* 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, yylineno,
+ (header_section ? header_section : "null"));
+ exit(1);
+ return 1; /* to full compilers that insist on a return statement */
+}
diff --git a/contrib/amd/amd/conf_tok.l b/contrib/amd/amd/conf_tok.l
new file mode 100644
index 0000000..0ec067b
--- /dev/null
+++ b/contrib/amd/amd/conf_tok.l
@@ -0,0 +1,186 @@
+%{
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: conf_tok.l,v 5.2.2.1 1992/02/09 15:09:36 jsp beta $
+ *
+ */
+
+/*
+ * 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 useing 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 */
+int yylineno = 0;
+#endif /* FLEX_SCANNER */
+
+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), yylineno, (s))
+# define amu_return(v)
+#else
+# define dprintf(f,s)
+# define amu_return(v) return((v))
+#endif /* TOK_DEBUG */
+
+/* no need to use yyunput() or yywrap() */
+#define YY_NO_UNPUT
+#define YY_SKIP_YYWRAP
+
+%}
+
+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 {
+ yylineno++;
+ 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 */
+ yylineno++;
+ 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/contrib/amd/amd/get_args.c b/contrib/amd/amd/get_args.c
new file mode 100644
index 0000000..b365ff7
--- /dev/null
+++ b/contrib/amd/amd/get_args.c
@@ -0,0 +1,389 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: get_args.c,v 5.2.2.1 1992/02/09 15:08:23 jsp beta $
+ *
+ */
+
+/*
+ * 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 *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 */
+#ifdef DEBUG
+int debug_flags = D_AMQ /* Register AMQ */
+ | D_DAEMON; /* Enter daemon mode */
+#endif /* DEBUG */
+
+
+/*
+ * Return the version string (dynamic buffer)
+ */
+char *
+get_version_string(void)
+{
+ static char *vers = NULL;
+ char tmpbuf[1024];
+ char *wire_buf;
+ int wire_buf_len = 0;
+
+ /* first get dynamic string listing all known networks */
+ wire_buf = print_wires();
+ if (wire_buf)
+ wire_buf_len = strlen(wire_buf);
+
+ vers = xmalloc(2048 + wire_buf_len);
+ sprintf(vers, "%s\n%s\n%s\n%s\n",
+ "Copyright (c) 1997-1998 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.");
+ sprintf(tmpbuf, "%s version %s (build %d).\n",
+ PACKAGE, VERSION, AMU_BUILD_VERSION);
+ strcat(vers, tmpbuf);
+ sprintf(tmpbuf, "Built by %s@%s on date %s.\n",
+ USER_NAME, HOST_NAME, CONFIG_DATE);
+ strcat(vers, tmpbuf);
+ sprintf(tmpbuf, "cpu=%s (%s-endian), arch=%s, karch=%s.\n",
+ cpu, endian, gopt.arch, gopt.karch);
+ strcat(vers, tmpbuf);
+ sprintf(tmpbuf, "full_os=%s, os=%s, osver=%s, vendor=%s.\n",
+ HOST_OS, gopt.op_sys, gopt.op_sys_ver, HOST_VENDOR);
+ strcat(vers, tmpbuf);
+
+ strcat(vers, "Map support for: ");
+ mapc_showtypes(tmpbuf);
+ strcat(vers, tmpbuf);
+ strcat(vers, ".\nAMFS: ");
+ ops_showamfstypes(tmpbuf);
+ strcat(vers, tmpbuf);
+ strcat(vers, ".\nFS: ");
+ ops_showfstypes(tmpbuf);
+ strcat(vers, tmpbuf);
+
+ /* append list of networks if available */
+ if (wire_buf) {
+ strcat(vers, wire_buf);
+ XFREE(wire_buf);
+ }
+
+ return vers;
+}
+
+
+void
+get_args(int argc, char *argv[])
+{
+ int opt_ch;
+ FILE *fp = stdin;
+
+ /* 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, "nprvSa:c:d:k:l:o:t:w:x:y:C:D:F:T:O:H")) != EOF)
+ switch (opt_ch) {
+
+ case 'a':
+ if (*optarg != '/') {
+ fprintf(stderr, "%s: -a option must begin with a '/'\n",
+ 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 */
+ {
+ char *dot = strchr(optarg, '.');
+ if (dot)
+ *dot = '\0';
+ if (*optarg) {
+ gopt.amfs_auto_timeo = atoi(optarg);
+ }
+ if (dot) {
+ gopt.amfs_auto_retrans = atoi(dot + 1);
+ *dot = '.';
+ }
+ }
+ break;
+
+ case 'v':
+ fputs(get_version_string(), stderr);
+ exit(0);
+ 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 '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", progname);
+#endif /* not DEBUG */
+ break;
+
+ case 'F':
+ conf_file = optarg;
+ use_conf_file = 1;
+ break;
+
+ case 'H':
+ goto show_usage;
+ 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 && conf_file) {
+ fp = fopen(conf_file, "r");
+ if (!fp) {
+ char buf[128];
+ sprintf(buf, "Amd configuration file (%s)", conf_file);
+ perror(buf);
+ exit(1);
+ }
+ yyin = fp;
+ yyparse();
+ fclose(fp);
+ if (process_last_regular_map() != 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 && conf_file)
+ plog(XLOG_INFO, "using configuration file %s", 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)
+ goto show_usage;
+
+ 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++;
+ strcat(hostd, ".");
+ strcat(hostd, hostdomain);
+
+#ifdef MOUNT_TABLE_ON_FILE
+# ifdef DEBUG
+ if (debug_flags & D_MTAB)
+ mnttab_file_name = DEBUG_MNTTAB;
+ else
+# endif /* DEBUG */
+ mnttab_file_name = MNTTAB_FILE_NAME;
+#else /* not MOUNT_TABLE_ON_FILE */
+# ifdef DEBUG
+ if (debug_flags & D_MTAB)
+ dlog("-D mtab option ignored");
+# endif /* DEBUG */
+#endif /* not MOUNT_TABLE_ON_FILE */
+
+ if (switch_to_logfile(gopt.logfile) != 0)
+ plog(XLOG_USER, "Cannot switch logfile");
+
+ /*
+ * If the kernel architecture was not specified
+ * then use the machine architecture.
+ */
+ if (gopt.karch == 0)
+ gopt.karch = gopt.arch;
+
+ if (gopt.cluster == 0)
+ gopt.cluster = hostdomain;
+
+ if (gopt.amfs_auto_timeo <= 0)
+ gopt.amfs_auto_timeo = AMFS_AUTO_TIMEO;
+ if (gopt.amfs_auto_retrans <= 0)
+ gopt.amfs_auto_retrans = AMFS_AUTO_RETRANS;
+ if (gopt.amfs_auto_retrans <= 0)
+ gopt.amfs_auto_retrans = 3; /* XXX */
+ return;
+ }
+
+show_usage:
+ 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] [-C cluster_name]\n\
+\t[-o op_sys_ver] [-O op_sys_name]\n\
+\t[-F conf_file] [-T conf_tag]", 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");
+ exit(1);
+}
diff --git a/contrib/amd/amd/info_file.c b/contrib/amd/amd/info_file.c
new file mode 100644
index 0000000..55c24bd
--- /dev/null
+++ b/contrib/amd/amd/info_file.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: info_file.c,v 5.2.2.1 1992/02/09 15:08:28 jsp beta $
+ *
+ */
+
+/*
+ * 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(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);
+int file_mtime(mnt_map *m, char *map, 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));
+
+ return done;
+}
+
+
+/*
+ * Try to locate a key in a file
+ */
+static int
+search_or_reload_file(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;
+#ifdef DEBUG
+ dlog("%s returns %s", key, dc);
+#endif /* DEBUG */
+ }
+ 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();
+ else
+ *tp = stb.st_mtime;
+ }
+ return mapf;
+}
+
+
+int
+file_init(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 = search_or_reload_file(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 = search_or_reload_file(mapf, map, key, pval, 0, 0);
+ }
+ (void) fclose(mapf);
+ return error;
+ }
+ return errno;
+}
+
+
+int
+file_mtime(mnt_map *m, char *map, time_t *tp)
+{
+ FILE *mapf = file_open(map, tp);
+
+ if (mapf) {
+ (void) fclose(mapf);
+ return 0;
+ }
+ return errno;
+}
diff --git a/contrib/amd/amd/info_hesiod.c b/contrib/amd/amd/info_hesiod.c
new file mode 100644
index 0000000..a4607e1
--- /dev/null
+++ b/contrib/amd/amd/info_hesiod.c
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: info_hesiod.c,v 1.1 1997-1998/07/11 08:34:52 danny Exp danny $
+ *
+ */
+
+/*
+ * 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);
+
+static voidp hesiod_context;
+#endif /* HAVE_HESIOD_INIT */
+
+/*
+ * 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)
+{
+#ifdef DEBUG
+ dlog("amu_hesiod_init(%s)", map);
+#endif /* DEBUG */
+ *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 */
+
+#ifdef DEBUG
+ dlog("hesiod_search(m=%x, map=%s, key=%s, pval=%x tp=%x)", m, map, key, pval, tp);
+#endif /* DEBUG */
+
+ sprintf(hes_key, "%s.%s", key, map + HES_PREFLEN);
+
+ /*
+ * Call the resolver
+ */
+#ifdef DEBUG
+ dlog("Hesiod base is: %s\n", gopt.hesiod_base);
+ dlog("hesiod_search: hes_resolve(%s, %s)", hes_key, gopt.hesiod_base);
+ if (debug_flags & D_INFO)
+ _res.options |= RES_DEBUG;
+#endif /* 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
+ */
+# ifdef DEBUG
+ dlog("hesiod_search: Error: %d", hes_error());
+# endif /* DEBUG */
+ 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;
+ }
+# ifdef DEBUG
+ dlog("hesiod_search: Returning: %d", error);
+# endif /* DEBUG */
+ return error;
+#endif /* not HAVE_HESIOD_INIT */
+}
diff --git a/contrib/amd/amd/info_ldap.c b/contrib/amd/amd/info_ldap.c
new file mode 100644
index 0000000..e2f0367
--- /dev/null
+++ b/contrib/amd/amd/info_ldap.c
@@ -0,0 +1,465 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: info_ldap.c,v 5.2.2.1 1992/02/09 15:08:29 jsp beta $
+ *
+ */
+
+
+/*
+ * Get info from LDAP (Lightweight Directory Access Protocol)
+ * LDAP Home Page: http://www.umich.edu/~rsug/ldap/
+ */
+
+#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;
+
+/*
+ * STRUCTURES:
+ */
+struct ald_ent {
+ LDAP *ldap;
+ HE *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(LDAP *ld, char *map, time_t *ts);
+
+
+/*
+ * FUNCTIONS:
+ */
+
+static void
+he_free(HE *h)
+{
+ XFREE(h->host);
+ if (h->next != NULL)
+ he_free(h->next);
+ XFREE(h);
+}
+
+
+static HE *
+string2he(char *s)
+{
+ char *c, *p;
+ HE *new, *old = NULL;
+
+ if (s == NULL)
+ return (NULL);
+ for (p = s; p; p = strchr(p, ',')) {
+ if (old != NULL) {
+ new = (HE *) xmalloc(sizeof(HE));
+ old->next = new;
+ old = new;
+ } else {
+ old = (HE *) xmalloc(sizeof(HE));
+ 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);
+
+ }
+ return (old);
+}
+
+
+static void
+cr_free(CR *c)
+{
+ XFREE(c->who);
+ XFREE(c->pw);
+ XFREE(c);
+}
+
+
+static void
+ald_free(ALD *a)
+{
+ he_free(a->hostent);
+ cr_free(a->credentials);
+ if (a->ldap != NULL)
+ ldap_unbind(a->ldap);
+ XFREE(a);
+}
+
+
+int
+amu_ldap_init(mnt_map *m, char *map, time_t *ts)
+{
+ ALD *aldh;
+ CR *creds;
+
+ if (!STREQ(gopt.map_type, AMD_LDAP_TYPE)) {
+ return (ENOENT);
+ }
+#ifdef DEBUG
+ else {
+ dlog("Map %s is ldap\n", map);
+ }
+#endif /* DEBUG */
+
+ aldh = (ALD *) xmalloc(sizeof(ALD));
+ creds = (CR *) xmalloc(sizeof(CR));
+
+ 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, map);
+ return (ENOENT);
+ }
+ creds->who = "";
+ creds->pw = "";
+ creds->method = LDAP_AUTH_SIMPLE;
+ aldh->credentials = creds;
+ aldh->timestamp = 0;
+#ifdef DEBUG
+ dlog("Trying for %s:%d\n", aldh->hostent->host, aldh->hostent->port);
+#endif /* DEBUG */
+ if (amu_ldap_rebind(aldh)) {
+ ald_free(aldh);
+ return (ENOENT);
+ }
+ m->map_data = (void *) aldh;
+#ifdef DEBUG
+ dlog("Bound to %s:%d\n", aldh->hostent->host, aldh->hostent->port);
+#endif /* DEBUG */
+ if (get_ldap_timestamp(aldh->ldap, map, ts))
+ return (ENOENT);
+#ifdef DEBUG
+ dlog("Got timestamp for map %s: %d\n", map, *ts);
+#endif /* DEBUG */
+
+ return (0);
+}
+
+
+static int
+amu_ldap_rebind(ALD *a)
+{
+ LDAP *ld;
+ HE *h;
+ CR *c = a->credentials;
+ time_t now = clocktime();
+
+ if (a->ldap != NULL) {
+ if ((a->timestamp - now) > AMD_LDAP_TTL) {
+#ifdef DEBUG
+ dlog("Reestablishing ldap connection\n");
+#endif /* DEBUG */
+ ldap_unbind(a->ldap);
+ a->timestamp = now;
+ } else
+ return (0);
+ }
+
+ while (TRUE) {
+ 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_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) {
+ ldap_enable_cache(ld, gopt.ldap_cache_seconds, gopt.ldap_cache_maxmem);
+ a->ldap = ld;
+ a->timestamp = now;
+ return (0);
+ }
+ }
+ plog(XLOG_WARNING, "Exausted 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(LDAP * ld, char *map, time_t *ts)
+{
+ struct timeval tv;
+ char **vals, *end;
+ char filter[MAXPATHLEN];
+ int i, err, nentries = 0;
+ LDAPMessage *res, *entry;
+
+ tv.tv_sec = 3;
+ tv.tv_usec = 0;
+ sprintf(filter, AMD_LDAP_TSFILTER, map);
+#ifdef DEBUG
+ dlog("Getting timestamp for map %s\n", map);
+ dlog("Filter is: %s\n", filter);
+ dlog("Base is: %s\n", gopt.ldap_base);
+#endif /* DEBUG */
+ for (i = 0; i < AMD_LDAP_RETRIES; i++) {
+ err = ldap_search_st(ld,
+ gopt.ldap_base,
+ LDAP_SCOPE_SUBTREE,
+ filter,
+ 0,
+ 0,
+ &tv,
+ &res);
+ if (err == LDAP_SUCCESS)
+ break;
+ dlog("Timestamp search timed out, trying again...\n");
+ }
+
+ if (err != LDAP_SUCCESS) {
+ *ts = 0;
+ plog(XLOG_USER, "LDAP timestamp search failed: %s\n",
+ ldap_err2string(ld->ld_errno));
+ return (ENOENT);
+ }
+
+ nentries = ldap_count_entries(ld, 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(ld, res);
+ vals = ldap_get_values(ld, 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);
+ ldap_msgfree(entry);
+ return (ENOENT);
+ }
+#ifdef DEBUG
+ dlog("TS value is:%s:\n", vals[0]);
+#endif /* DEBUG */
+
+ 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 %d for map %s\n",
+ *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);
+ ldap_msgfree(entry);
+#ifdef DEBUG
+ dlog("The timestamp for %s is %d (err=%d)\n", map, *ts, err);
+#endif /* DEBUG */
+ return (err);
+}
+
+
+int
+amu_ldap_search(mnt_map *m, char *map, char *key, char **pval, time_t *ts)
+{
+ char **vals, filter[MAXPATHLEN];
+ struct timeval tv;
+ int i, err, nvals = 0, nentries = 0;
+ LDAPMessage *entry, *res;
+ ALD *a = (ALD *) (m->map_data);
+
+ 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);
+
+ sprintf(filter, AMD_LDAP_FILTER, map, key);
+#ifdef DEBUG
+ dlog("Search with filter: %s\n", filter);
+#endif /* DEBUG */
+ 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;
+ }
+
+ switch (err) {
+ case LDAP_SUCCESS:
+ break;
+ case LDAP_NO_SUCH_OBJECT:
+#ifdef DEBUG
+ dlog("No object\n");
+#endif /* DEBUG */
+ ldap_msgfree(res);
+ return (ENOENT);
+ default:
+ plog(XLOG_USER, "LDAP search failed: %s\n",
+ ldap_err2string(a->ldap->ld_errno));
+ ldap_msgfree(res);
+ return (EIO);
+ }
+
+ nentries = ldap_count_entries(a->ldap, res);
+#ifdef DEBUG
+ dlog("Search found %d entries\n", nentries);
+#endif /* DEBUG */
+ 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);
+ ldap_msgfree(entry);
+ return (EIO);
+ }
+#ifdef DEBUG
+ dlog("Map %s, %s => %s\n", map, key, vals[0]);
+#endif /* DEBUG */
+ 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_msgfree(entry);
+ 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->ldap, map, ts)) {
+ return (ENOENT);
+ }
+ return (0);
+}
diff --git a/contrib/amd/amd/info_ndbm.c b/contrib/amd/amd/info_ndbm.c
new file mode 100644
index 0000000..0b93fa6
--- /dev/null
+++ b/contrib/amd/amd/info_ndbm.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: info_ndbm.c,v 5.2.2.1 1992/02/09 15:08:31 jsp beta $
+ *
+ */
+
+/*
+ * 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];
+
+ strcpy(dbfilename, map);
+ strcat(dbfilename, DBM_SUFFIX);
+ 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];
+
+ strcpy(dbfilename, map);
+ strcat(dbfilename, DBM_SUFFIX);
+ error = stat(dbfilename, &stb);
+#else /* not DBM_SUFFIX */
+ error = fstat(dbm_pagfno(db), &stb);
+#endif /* not DBM_SUFFIX */
+ if (error < 0)
+ *tp = clocktime();
+ 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/contrib/amd/amd/info_nis.c b/contrib/amd/amd/info_nis.c
new file mode 100644
index 0000000..eceb73a
--- /dev/null
+++ b/contrib/amd/amd/info_nis.c
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: info_nis.c,v 5.2.2.1 1992/02/09 15:08:32 jsp beta $
+ *
+ */
+
+/*
+ * 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()
+ */
+static int has_yp_order = FALSE;
+
+/* 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_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);
+
+#ifdef DEBUG
+ plog(XLOG_ERROR, "yp enumeration of %s: %s, status=%d, e=%d",
+ ncdp->ncd_map, yperr_string(e), status, e);
+#else /* not DEBUG */
+ plog(XLOG_ERROR, "yp enumeration of %s: %s", ncdp->ncd_map, yperr_string(e));
+#endif /* not DEBUG */
+ }
+ 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;
+ static int last_status = 1; /* assume up by default */
+
+ if (has_yp_order) {
+ error = yp_order(gopt.nis_domain, map, &order);
+ if (error != 0) {
+ plog(XLOG_ERROR,
+ "nis_isup: error getting the order of map of %s: %s",
+ map, yperr_string(ypprot_err(error)));
+ last_status = 0;
+ return 0; /* NIS is down */
+ }
+ }
+ if (last_status == 0) { /* if was down before */
+ time_t dummy;
+ plog(XLOG_INFO, "nis_isup: NIS came back up for map %s", map);
+ /* XXX: do we really need to reinitialize nis? */
+ error = nis_init(m, map, &dummy);
+ if (!error)
+ 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 initialised
+ */
+ if (!gopt.nis_domain) {
+ int error = determine_nis_domain();
+ if (error)
+ return error;
+ }
+
+
+ if (has_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;
+ }
+ } else {
+ /*
+ * 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);
+ }
+ }
+
+ /*
+ * 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, "%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:
+ has_yp_order = TRUE;
+ *tp = (time_t) order;
+#ifdef DEBUG
+ dlog("NIS master for %s@%s has order %d", map, gopt.nis_domain, order);
+#endif /* DEBUG */
+ break;
+ case YPERR_YPERR:
+ /* NIS+ server found ! */
+ has_yp_order = FALSE;
+ /* try yp_master() instead */
+ if (yp_master(gopt.nis_domain, map, &master)) {
+ return ENOENT;
+ } else {
+#ifdef DEBUG
+ dlog("NIS master for %s@%s is a NIS+ server", map, gopt.nis_domain);
+#endif /* DEBUG */
+ /* Use fake timestamps */
+ *tp = time(NULL);
+ }
+ break;
+ default:
+ 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 enevtually 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;
+ outkey_old = outkey;
+ outkeylen_old = outkeylen;
+ i = yp_next(indomain,
+ inmap,
+ outkey_old,
+ outkeylen_old,
+ &outkey,
+ &outkeylen,
+ &outval,
+ &outvallen);
+ } while (!i);
+#ifdef DEBUG
+ if (i) {
+ dlog("yp_next() returned error: %s\n", yperr_string(i));
+ }
+#endif /* DEBUG */
+ if (i == YPERR_NOMORE)
+ return 0;
+ return i;
+}
+#endif /* HAVE_BAD_YP_ALL */
diff --git a/contrib/amd/amd/info_nisplus.c b/contrib/amd/amd/info_nisplus.c
new file mode 100644
index 0000000..71f29e9
--- /dev/null
+++ b/contrib/amd/amd/info_nisplus.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: info_nisplus.c,v 5.2.2.1 1992/02/09 15:08:32 jsp beta $
+ *
+ */
+
+/*
+ * 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;
+
+#ifdef DEBUG
+ dlog("NISplus callback for <%s,%s>", kp, vp);
+#endif /* DEBUG */
+
+ (*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;
+
+ org = strstr(map, NISPLUS_ORGDIR);
+ if (org == NULL)
+ org = NISPLUS_ORGDIR;
+ else
+ org = "";
+
+ /* make some room for the NIS map_name */
+ map_name = xmalloc(strlen(map) + sizeof(NISPLUS_ORGDIR));
+ if (map_name == NULL) {
+ plog(XLOG_ERROR, "Unable to create map_name %s: %s",
+ map, strerror(ENOMEM));
+ return ENOMEM;
+ }
+ sprintf(map_name, "%s%s", map, org);
+
+ data.ncd_m = m;
+ data.ncd_map = map_name;
+ data.ncd_fn = fn;
+
+#ifdef DEBUG
+ dlog("NISplus reload for %s", map);
+#endif /* DEBUG */
+
+ 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;
+
+#ifdef DEBUG
+ dlog("NISplus search callback for <%s>", ENTRY_VAL(value, 0));
+ dlog("NISplus search callback value <%s>", ENTRY_VAL(value, 1));
+#endif /* DEBUG */
+
+ 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 */
+
+ org = strstr(map, NISPLUS_ORGDIR);
+ if (org == NULL)
+ org = NISPLUS_ORGDIR;
+ else
+ org = "";
+
+ /* make some room for the NIS index */
+ index = xmalloc(sizeof('[') /* for opening selection criteria */
+ +sizeof(NISPLUS_KEY)
+ + strlen(key)
+ + sizeof(']') /* for closing selection criteria */
+ +sizeof(',') /* + 1 for , separator */
+ +strlen(map)
+ + sizeof(NISPLUS_ORGDIR)
+ );
+ if (index == NULL) {
+ plog(XLOG_ERROR,
+ "Unable to create index %s: %s",
+ map,
+ strerror(ENOMEM));
+ return ENOMEM;
+ }
+ sprintf(index, "[%s%s],%s%s", NISPLUS_KEY, key, map, org);
+
+ data.key = key;
+ data.value = NULL;
+
+#ifdef DEBUG
+ dlog("NISplus search for %s", index);
+#endif /* DEBUG */
+
+ 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, "%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;
+#ifdef DEBUG
+ dlog("NISplus search found <nothing>");
+ dlog("NISplus search for %s: %s(%d)",
+ map, nis_sperrno(result->status), result->status);
+#endif /* DEBUG */
+
+ if (value != NULL)
+ data.value = strnsave(ENTRY_VAL(value, 1), ENTRY_LEN(value, 1));
+ }
+ *val = data.value;
+
+ if (*val) {
+ error = 0;
+#ifdef DEBUG
+ dlog("NISplus search found %s", *val);
+#endif /* DEBUG */
+ } else {
+ error = ENOENT;
+#ifdef DEBUG
+ dlog("NISplus search found nothing");
+#endif /* DEBUG */
+ }
+
+ *tp = 0;
+ break;
+
+ case NIS_NOSUCHNAME:
+#ifdef DEBUG
+ dlog("NISplus search returned %d", result->status);
+#endif /* DEBUG */
+ error = ENOENT;
+ break;
+
+ default:
+ plog(XLOG_ERROR, "%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;
+
+ org = strstr(map, NISPLUS_ORGDIR);
+ if (org == NULL)
+ org = NISPLUS_ORGDIR;
+ else
+ org = "";
+
+ /* make some room for the NIS map_name */
+ map_name = xmalloc(strlen(map) + sizeof(NISPLUS_ORGDIR));
+ if (map_name == NULL) {
+ plog(XLOG_ERROR,
+ "Unable to create map_name %s: %s",
+ map,
+ strerror(ENOMEM));
+ return ENOMEM;
+ }
+ sprintf(map_name, "%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) {
+#ifdef DEBUG
+ dlog("NISplus init <%s>: %s (%d)",
+ map, nis_sperrno(result->status), result->status);
+#endif /* DEBUG */
+
+ 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/contrib/amd/amd/info_passwd.c b/contrib/amd/amd/info_passwd.c
new file mode 100644
index 0000000..32d92f4
--- /dev/null
+++ b/contrib/amd/amd/info_passwd.c
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: info_passwd.c,v 5.2.2.1 1992/02/09 15:08:33 jsp beta $
+ *
+ */
+
+/*
+ * 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) {
+ strcat(rhost, q + 1);
+ strcat(rhost, ".");
+ *q = '\0';
+ } else {
+ strcat(rhost, p);
+ }
+ } 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}";
+ sprintf(val, "var0:=%s;var1:=%s;var2:=%s;var3:=%s;%s",
+ dir+1, rhost, user, pw->pw_dir, p);
+#ifdef DEBUG
+ dlog("passwd_search: map=%s key=%s -> %s", map, key, val);
+#endif /* DEBUG */
+ if (q)
+ *q = '.';
+ *pval = strdup(val);
+ return 0;
+ }
+
+enoent:
+ if (dir)
+ XFREE(dir);
+
+ return ENOENT;
+}
diff --git a/contrib/amd/amd/info_union.c b/contrib/amd/amd/info_union.c
new file mode 100644
index 0000000..7f757f9
--- /dev/null
+++ b/contrib/amd/amd/info_union.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: info_union.c,v 5.2.2.1 1992/02/09 15:08:34 jsp beta $
+ *
+ */
+
+/*
+ * 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;
+
+ for (p = v; p[1]; p++) ;
+ *pval = xmalloc(strlen(*p) + 5);
+ sprintf(*pval, "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++) {
+ int dlen;
+ struct dirent *dp;
+
+ DIR *dirp = opendir(*dir);
+ if (!dirp) {
+ plog(XLOG_USER, "Cannot read directory %s: %m", *dir);
+ continue;
+ }
+ dlen = strlen(*dir);
+
+#ifdef DEBUG
+ dlog("Reading directory %s...", *dir);
+#endif /* DEBUG */
+ while ((dp = readdir(dirp))) {
+ char *val, *dpname = &dp->d_name[0];
+ if (dpname[0] == '.' &&
+ (dpname[1] == '\0' ||
+ (dpname[1] == '.' && dpname[2] == '\0')))
+ continue;
+
+#ifdef DEBUG
+ dlog("... gives %s", dp->d_name);
+#endif /* DEBUG */
+ val = xmalloc(dlen + 5);
+ sprintf(val, "fs:=%s", *dir);
+ (*fn) (m, strdup(dp->d_name), val);
+ }
+ closedir(dirp);
+ }
+
+ /*
+ * Add wildcard entry
+ */
+ {
+ char *val = xmalloc(strlen(dir[-1]) + 5);
+
+ sprintf(val, "fs:=%s", dir[-1]);
+ (*fn) (m, strdup("*"), val);
+ }
+ XFREE(mapd);
+ XFREE(v);
+ return 0;
+}
diff --git a/contrib/amd/amd/map.c b/contrib/amd/amd/map.c
new file mode 100644
index 0000000..20320d9
--- /dev/null
+++ b/contrib/amd/amd/map.c
@@ -0,0 +1,1112 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: map.c,v 5.2.2.2 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+#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 NEVER (time_t) 0
+#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.
+ */
+static u_int am_gen = 2; /* Initial generation number */
+static int timeout_mp_id; /* Id from last call to timeout */
+
+am_node *root_node; /* The root of the mount tree */
+am_node **exported_ap = (am_node **) 0;
+int exported_ap_size = 0;
+int first_free_map = 0; /* First available free slot */
+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(am_node *mp);
+static void exported_ap_free(am_node *mp);
+static void remove_am(am_node *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;
+}
+
+
+/*
+ * Allocate a new mount slot and create
+ * a new node.
+ * Fills in the map number of the node,
+ * but leaves everything else uninitialised.
+ */
+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(*mp));
+
+ 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
+ */
+ 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;
+}
+
+
+/*
+ * 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.nt_seconds = mp->am_ttl;
+ mp->am_ttl += mp->am_timeo; /* sun's -tl option */
+}
+
+
+void
+mk_fattr(am_node *mp, nfsftype vntype)
+{
+ switch (vntype) {
+ case NFDIR:
+ mp->am_fattr.na_type = NFDIR;
+ mp->am_fattr.na_mode = NFSMODE_DIR | 0555;
+ mp->am_fattr.na_nlink = 2;
+ mp->am_fattr.na_size = 512;
+ break;
+ case NFLNK:
+ mp->am_fattr.na_type = NFLNK;
+ mp->am_fattr.na_mode = NFSMODE_LNK | 0777;
+ mp->am_fattr.na_nlink = 1;
+ mp->am_fattr.na_size = 0;
+ break;
+ default:
+ plog(XLOG_FATAL, "Unknown fattr type %d - ignored", vntype);
+ break;
+ }
+}
+
+
+/*
+ * Initialise 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_name = strdup(dir);
+ mp->am_path = strdup(dir);
+ mp->am_gen = new_gen();
+
+ 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 = 0;
+ mp->am_fattr.na_atime.nt_seconds = clocktime();
+ mp->am_fattr.na_atime.nt_useconds = 0;
+ 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;
+}
+
+
+/*
+ * 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);
+
+ exported_ap_free(mp);
+}
+
+
+/*
+ * Convert from file handle to automount node.
+ */
+am_node *
+fh_to_mp3(am_nfs_fh *fhp, int *rp, int c_or_d)
+{
+ struct am_fh *fp = (struct am_fh *) fhp;
+ am_node *ap = 0;
+
+ /*
+ * 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->fhh_pid != mypid)
+ goto drop;
+
+ /*
+ * Make sure the index is valid before
+ * exported_ap is referenced.
+ */
+ if (fp->fhh_id < 0 || fp->fhh_id >= exported_ap_size)
+ goto drop;
+
+ /*
+ * Get hold of the supposed mount node
+ */
+ ap = exported_ap[fp->fhh_id];
+
+ /*
+ * If it exists then maybe...
+ */
+ if (ap) {
+ /*
+ * 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->am_gen != fp->fhh_gen) {
+ ap = 0;
+ goto drop;
+ }
+ /*
+ * 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;
+
+#ifdef DEBUG
+ dlog("fh_to_mp3: %s (%s) is hung:- call lookup",
+ orig_ap->am_path, orig_ap->am_mnt->mf_info);
+#endif /* DEBUG */
+
+ /*
+ * Update modify time of parent node.
+ * With any luck the kernel will re-stat
+ * the child node and get new information.
+ */
+ orig_ap->am_fattr.na_mtime.nt_seconds = clocktime();
+
+ /*
+ * 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 (c_or_d == VLOOK_CREATE) {
+ ap = (*orig_ap->am_parent->am_mnt->mf_ops->lookuppn)
+ (orig_ap->am_parent, orig_ap->am_name, &error, c_or_d);
+ } 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);
+
+ }
+
+ /*
+ * 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;
+}
+
+
+am_node *
+fh_to_mp(am_nfs_fh *fhp)
+{
+ int dummy;
+
+ return fh_to_mp2(fhp, &dummy);
+}
+
+
+/*
+ * Convert from automount node to file handle.
+ */
+void
+mp_to_fh(am_node *mp, am_nfs_fh *fhp)
+{
+ struct am_fh *fp = (struct am_fh *) fhp;
+
+ memset((char *) fhp, 0, sizeof(am_nfs_fh));
+
+ /*
+ * Take the process id
+ */
+ fp->fhh_pid = mypid;
+
+ /*
+ * ... the map number
+ */
+ fp->fhh_id = mp->am_mapno;
+
+ /*
+ * ... and the generation number
+ */
+ fp->fhh_gen = mp->am_gen;
+
+ /*
+ * ... 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).
+ */
+}
+
+
+am_node *
+find_ap2(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_ap2(dir, mp->am_osib);
+ if (mp2)
+ return mp2;
+ return find_ap2(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_ap2(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 *
+root_fh(char *dir)
+{
+ static am_nfs_fh nfh;
+ am_node *mp = root_ap(dir, TRUE);
+ if (mp) {
+ mp_to_fh(mp, &nfh);
+ /*
+ * Patch up PID to match main server...
+ */
+ if (!foreground) {
+ long pid = getppid();
+ ((struct am_fh *) &nfh)->fhh_pid = pid;
+#ifdef DEBUG
+ dlog("root_fh substitutes pid %d", pid);
+#endif /* DEBUG */
+ }
+ return &nfh;
+ }
+
+ /*
+ * Should never get here...
+ */
+ plog(XLOG_ERROR, "Can't find root filehandle for %s", dir);
+
+ return 0;
+}
+
+
+am_node *
+root_ap(char *dir, int path)
+{
+ 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();
+ 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, voidp arg)
+{
+ int error = 0;
+
+ (void) amfs_auto_ops.lookuppn((am_node *) arg, dir, &error, VLOOK_CREATE);
+ 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((void (*)P((char *, voidp))) 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;
+
+ /*
+ * Initialise 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);
+}
+
+
+/*
+ * 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];
+
+ if (mp) {
+ mntfs *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->mf_ops->fs_flags & FS_DIRECTORY)) {
+ /*
+ * When shutting down this had better
+ * look like a directory, otherwise it
+ * can't be unmounted!
+ */
+ mk_fattr(mp, 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) {
+ plog(XLOG_INFO, "on-exit attempt to unmount %s", mf->mf_mount);
+ unmount_node(mp);
+ }
+ am_unmounted(mp);
+
+ } 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;
+ }
+ }
+ }
+}
+
+
+static int
+unmount_node(am_node *mp)
+{
+ mntfs *mf = mp->am_mnt;
+ int error;
+
+ if ((mf->mf_flags & MFF_ERROR) || mf->mf_refc > 1) {
+ /*
+ * Just unlink
+ */
+#ifdef DEBUG
+ if (mf->mf_flags & MFF_ERROR)
+ dlog("No-op unmount of error node %s", mf->mf_info);
+#endif /* DEBUG */
+ error = 0;
+ } else {
+#ifdef DEBUG
+ dlog("Unmounting %s (%s)", mf->mf_mount, mf->mf_info);
+#endif /* DEBUG */
+ error = (*mf->mf_ops->umount_fs) (mp);
+ }
+
+ if (error) {
+ errno = error; /* XXX */
+#ifdef DEBUG
+ dlog("%s: unmount: %m", mf->mf_mount);
+#endif /* DEBUG */
+ }
+
+ return error;
+}
+
+
+static int
+unmount_node_wrap(voidp vp)
+{
+ return unmount_node((am_node *) vp);
+
+ /*
+ * Below is the comment left from the old code
+ * that was dependent on the macro FLUSH_KERNEL_NAME_CACHE
+ */
+ /*
+ * This code should just say:
+ * return unmount_node((am_node *) vp);
+ *
+ * However...
+ * The kernel keeps a cached copy of filehandles,
+ * and doesn't ever uncache them (apparently). So
+ * when Amd times out a node the kernel will have a
+ * stale filehandle. When the kernel next uses the
+ * filehandle it gets ESTALE.
+ *
+ * The workaround:
+ * Arrange that when a node is removed an unlink or
+ * rmdir is done on that path so that the kernel
+ * cache is done. Yes - yuck.
+ *
+ * This can all be removed (and the background
+ * unmount flag in amfs_link_ops) if/when the kernel does
+ * something smarter.
+ *
+ * If the unlink or rmdir failed then just log a warning,
+ * don't fail the unmount. This can occur if the kernel
+ * client code decides that the object is still referenced
+ * and should be renamed rather than discarded.
+ *
+ * There is still a race condition here...
+ * if another process is trying to access the same
+ * filesystem at the time we get here, then
+ * it will block, since the MF_UNMOUNTING flag will
+ * be set. That may, or may not, cause the entire
+ * system to deadlock. Hmmm...
+ */
+}
+
+
+static void
+free_map_if_success(int rc, int term, voidp closure)
+{
+ am_node *mp = (am_node *) closure;
+ mntfs *mf = mp->am_mnt;
+
+ /*
+ * Not unmounting any more
+ */
+ mf->mf_flags &= ~MFF_UNMOUNTING;
+
+ /*
+ * If a timeout was defered 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 */
+ amd_stats.d_uerr++;
+ } else if (rc) {
+ if (rc == EBUSY) {
+ plog(XLOG_STATS, "\"%s\" on %s still active", mp->am_path, mf->mf_mount);
+ } else {
+ errno = rc; /* XXX */
+ plog(XLOG_ERROR, "%s: unmount: %m", mp->am_path);
+ }
+ amd_stats.d_uerr++;
+ } else {
+ am_unmounted(mp);
+ }
+
+ /*
+ * Wakeup anything waiting for this mount
+ */
+ wakeup((voidp) mf);
+}
+
+
+static 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", mp->am_path, mp->am_mnt->mf_mount);
+#endif /* notdef */
+
+ if ((mf->mf_ops->fs_flags & FS_UBACKGROUND) &&
+ (mf->mf_flags & MFF_MOUNTED)) {
+ 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;
+ }
+ } else {
+ /* Clear logdown flag - since the server must be up */
+ mf->mf_flags &= ~MFF_LOGDOWN;
+#ifdef DEBUG
+ dlog("\"%s\" on %s timed out", mp->am_path, mp->am_mnt->mf_mount);
+ /* dlog("Will background the unmount attempt"); */
+#endif /* DEBUG */
+ /*
+ * Note that we are unmounting this node
+ */
+ mf->mf_flags |= MFF_UNMOUNTING;
+ run_task(unmount_node_wrap, (voidp) mp,
+ free_map_if_success, (voidp) mp);
+ was_backgrounded = 1;
+#ifdef DEBUG
+ dlog("unmount attempt backgrounded");
+#endif /* DEBUG */
+ }
+ } else {
+#ifdef DEBUG
+ dlog("\"%s\" on %s timed out", mp->am_path, mp->am_mnt->mf_mount);
+ dlog("Trying unmount in foreground");
+#endif /* DEBUG */
+ mf->mf_flags |= MFF_UNMOUNTING;
+ free_map_if_success(unmount_node(mp), 0, (voidp) mp);
+#ifdef DEBUG
+ dlog("unmount attempt done");
+#endif /* DEBUG */
+ }
+
+ return was_backgrounded;
+}
+
+
+void
+timeout_mp(voidp v)
+{
+ int i;
+ time_t t = NEVER;
+ time_t now = clocktime();
+ int backoff = NumChild / 4;
+
+#ifdef DEBUG
+ dlog("Timing out automount points...");
+#endif /* DEBUG */
+
+ for (i = last_used_map; i >= 0; --i) {
+ am_node *mp = exported_ap[i];
+ mntfs *mf;
+
+ /*
+ * Just continue if nothing mounted, or can't be timed out.
+ */
+ if (!mp || (mp->am_flags & AMF_NOTIMEOUT))
+ continue;
+
+ /*
+ * Pick up mounted filesystem
+ */
+ mf = mp->am_mnt;
+ if (!mf)
+ 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) {
+#ifdef DEBUG
+ dlog("No further timeouts");
+#endif /* DEBUG */
+ 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;
+#ifdef DEBUG
+ dlog("Next mount timeout in %ds", t - now);
+#endif /* DEBUG */
+
+ 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/contrib/amd/amd/mapc.c b/contrib/amd/amd/mapc.c
new file mode 100644
index 0000000..de95e13
--- /dev/null
+++ b/contrib/amd/amd/mapc.c
@@ -0,0 +1,1205 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: mapc.c,v 5.2.2.2 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+/*
+ * 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
+
+#define MAX_CHAIN 2048
+
+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; /* Initialisation */
+ 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 *);
+
+/* 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_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 */
+
+/* FILE MAPS */
+#ifdef HAVE_MAP_FILE
+extern int file_init(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 *);
+extern int file_mtime(mnt_map *, 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_ALL
+ },
+#endif /* HAVE_MAP_PASSWD */
+#ifdef HAVE_MAP_HESIOD
+ {
+ "hesiod",
+ amu_hesiod_init,
+ error_reload,
+ NULL, /* isup function */
+ hesiod_search,
+ error_mtime,
+ MAPC_ALL
+ },
+#endif /* HAVE_MAP_HESIOD */
+#ifdef HAVE_MAP_LDAP
+ {
+ "ldap",
+ amu_ldap_init,
+ error_reload,
+ NULL, /* isup function */
+ amu_ldap_search,
+ amu_ldap_mtime,
+ MAPC_ALL
+ },
+#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_ALL
+ },
+#endif /* HAVE_MAP_NDBM */
+#ifdef HAVE_MAP_FILE
+ {
+ "file",
+ file_init,
+ file_reload,
+ NULL, /* isup function */
+ file_search,
+ file_mtime,
+ MAPC_ALL
+ },
+#endif /* HAVE_MAP_FILE */
+ {
+ "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)
+{
+ map_type *mt;
+ char *sep = "";
+
+ buf[0] = '\0';
+ for (mt = maptypes; mt < maptypes + sizeof(maptypes) / sizeof(maptypes[0]); mt++) {
+ strcat(buf, sep);
+ strcat(buf, mt->name);
+ sep = ", ";
+ }
+}
+
+
+/*
+ * 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 */
+
+#ifdef DEBUG
+ dlog("add_kv: %s -> %s", key, val);
+#endif /* DEBUG */
+
+#ifdef HAVE_REGEXEC
+ if (MAPC_ISRE(m)) {
+ char pattern[MAXPATHLEN];
+ int retval;
+
+ /*
+ * Make sure the string is bound to the start and end
+ */
+ sprintf(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
+ */
+static int
+mapc_reload_map(mnt_map *m)
+{
+ int error;
+
+#ifdef DEBUG
+ dlog("calling map reload on %s", m->map_name);
+#endif /* DEBUG */
+ error = (*m->reload) (m, m->map_name, mapc_add_kv);
+ if (error)
+ return error;
+ m->wildcard = 0;
+
+#ifdef DEBUG
+ dlog("calling mapc_search for wildcard");
+#endif /* DEBUG */
+ error = mapc_search(m, wildcard, &m->wildcard);
+ if (error)
+ m->wildcard = 0;
+
+ return 0;
+}
+
+
+/*
+ * 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;
+ 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++) {
+#ifdef DEBUG
+ dlog("trying to initialize map %s of type %s ...", map, mt->name);
+#endif /* DEBUG */
+ 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;
+ /* fallthrough... */
+ 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 */
+ }
+
+#ifdef DEBUG
+ dlog("Map for %s coming from maptype %s", map, mt->name);
+#endif /* DEBUG */
+
+ 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;
+
+ /*
+ * 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(voidp v)
+{
+ mnt_map *m = v;
+
+ /*
+ * 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) {
+ m->modify = t;
+ plog(XLOG_INFO, "Map %s is out of date", m->map_name);
+ mapc_sync(m);
+ }
+ }
+
+ if (!MAPC_ISRE(m)) {
+ /*
+ * Compute the hash table offset
+ */
+ k = m->kvhash[kvhash_of(key)];
+
+ /*
+ * Scan the linked list for the key
+ */
+ while (k && !FSTREQ(k->key, key))
+ k = k->next;
+
+ }
+
+#ifdef 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 / *"
+ */
+ strcpy(wildname, key);
+ while (error && (subp = strrchr(wildname, '/'))) {
+ strcpy(subp, "/*");
+#ifdef DEBUG
+ dlog("mapc recurses on %s", wildname);
+#endif /* DEBUG */
+ 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)
+{
+ if (m->alloc != MAPC_ROOT) {
+
+ /* 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;
+ }
+ }
+
+ mapc_clear(m);
+
+ if (m->alloc >= MAPC_ALL)
+ if (mapc_reload_map(m))
+ m->alloc = MAPC_INC;
+ /*
+ * Attempt to find the wildcard entry
+ */
+ if (m->alloc < MAPC_ALL)
+ mapc_find_wildcard(m);
+ }
+}
+
+
+/*
+ * 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();
+ 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) {
+ sprintf(str, "cache:=mapdefault;type:=%s;fs:=\"%s\"",
+ cfm->cfm_flags & CFM_MOUNT_TYPE_AUTOFS ? "autofs" : "toplvl",
+ get_full_path(map, cfm->cfm_search_path, cfm->cfm_type));
+ if (opts && opts[0] != '\0') {
+ strcat(str, ";");
+ strcat(str, opts);
+ }
+ if (cfm->cfm_flags & CFM_BROWSABLE_DIRS_FULL)
+ strcat(str, ";opts:=rw,fullybrowsable");
+ if (cfm->cfm_flags & CFM_BROWSABLE_DIRS)
+ strcat(str, ";opts:=rw,browsable");
+ if (cfm->cfm_type) {
+ strcat(str, ";maptype:=");
+ strcat(str, cfm->cfm_type);
+ }
+ } else {
+ strcpy(str, opts);
+ }
+ } else {
+ if (map)
+ sprintf(str, "cache:=mapdefault;type:=toplvl;fs:=\"%s\";%s",
+ map, opts ? opts : "");
+ else
+ strcpy(str, opts);
+ }
+ mapc_repl_kv(root_map, strdup((char *)dir), strdup(str));
+}
+
+
+int
+mapc_keyiter(mnt_map *m, void (*fn) (char *, voidp), voidp 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.
+ * Finally throw away the root map.
+ */
+int
+root_keyiter(void (*fn)(char *, voidp), voidp arg)
+{
+ if (root_map) {
+ int c = mapc_keyiter(root_map, fn, arg);
+ return c;
+ }
+
+ return 0;
+}
+
+
+/*
+ * 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>
+ */
+nfsentry *
+make_entry_chain(am_node *mp, const nfsentry *current_chain, int fully_browsable)
+{
+ static u_int last_cookie = ~(u_int) 0 - 1;
+ static nfsentry chain[MAX_CHAIN];
+ static int max_entries = MAX_CHAIN;
+ char *key;
+ int num_entries = 0, preflen = 0, i;
+ 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;
+ }
+
+ /* 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 '*' */
+ 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 (preflen) {
+ 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;
+}
+
+
+/*
+ * 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 */
+ strcpy(component, path);
+
+ str = strtok(component, ":");
+ do {
+ strcpy(full_path, str);
+ len = strlen(full_path);
+ if (full_path[len - 1] != '/') /* add trailing "/" if needed */
+ strcat(full_path, "/");
+ strcat(full_path, map);
+ 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/contrib/amd/amd/mntfs.c b/contrib/amd/amd/mntfs.c
new file mode 100644
index 0000000..31fa331
--- /dev/null
+++ b/contrib/amd/amd/mntfs.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: mntfs.c,v 5.2.2.2 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+#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_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_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;
+}
+
+
+mntfs *
+find_mntfs(am_ops *ops, am_opts *mo, char *mp, char *info, char *auto_opts, char *mopts, char *remopts)
+{
+ mntfs *mf;
+
+#ifdef DEBUG
+ dlog("Locating mntfs reference to %s", mp);
+#endif /* DEBUG */
+
+ ITER(mf, mntfs, &mfhead) {
+ if (STREQ(mf->mf_mount, mp)) {
+ /*
+ * 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;
+ else
+ return dup_mntfs(mf);
+ } else { /* ops != &amfs_error_ops */
+ /*
+ * If the existing ops are amfs_error_ops
+ * then continue...
+ */
+ if (mf->mf_ops == &amfs_error_ops)
+ continue;
+ }
+
+ if ((mf->mf_flags & MFF_RESTART) && amd_state == Run) {
+ /*
+ * Restart a previously mounted filesystem.
+ */
+ mntfs *mf2 = alloc_mntfs(&amfs_inherit_ops, mo, mp, info, auto_opts, mopts, remopts);
+#ifdef DEBUG
+ dlog("Restarting filesystem %s", mf->mf_mount);
+#endif /* DEBUG */
+
+ /*
+ * Remember who we are restarting
+ */
+ mf2->mf_private = (voidp) dup_mntfs(mf);
+ mf2->mf_prfree = free_mntfs;
+ return mf2;
+ }
+
+ mf->mf_fo = mo;
+ 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);
+ }
+ }
+
+ 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, int rmd)
+{
+ 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);
+
+ /*
+ * Clean up any directories that were made
+ */
+ if (rmd && (mf->mf_flags & MFF_MKMNT))
+ rmdirs(mf->mf_mount);
+ /* free mf_mount _AFTER_ removing the directories */
+ 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, TRUE);
+ 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(voidp v)
+{
+ mntfs *mf = v;
+
+ 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_ops->fs_flags & FS_DISCARD) {
+#ifdef DEBUG
+ dlog("Immediately discarding mntfs for %s", mf->mf_mount);
+#endif /* DEBUG */
+ discard_mntfs(mf);
+
+ } else {
+
+#ifdef DEBUG
+ 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);
+#endif /* DEBUG */
+ 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_ops == &amfs_inherit_ops && 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/contrib/amd/amd/nfs_prot_svc.c b/contrib/amd/amd/nfs_prot_svc.c
new file mode 100644
index 0000000..e2b1c70
--- /dev/null
+++ b/contrib/amd/amd/nfs_prot_svc.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: nfs_prot_svc.c,v 5.2.2.1 1992/02/09 15:09:30 jsp beta $
+ *
+ */
+
+#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 *nfs_program_2_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;
+
+ nfs_program_2_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().
+ */
+ nfs_program_2_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",
+ rqstp->rq_prog, rqstp->rq_vers, rqstp->rq_proc);
+ svcerr_decode(transp);
+ return;
+ }
+ result = (*local) (&argument, rqstp);
+
+ nfs_program_2_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/contrib/amd/amd/nfs_start.c b/contrib/amd/amd/nfs_start.c
new file mode 100644
index 0000000..e5740f6
--- /dev/null
+++ b/contrib/amd/amd/nfs_start.c
@@ -0,0 +1,472 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: nfs_start.c,v 5.2.2.1 1992/02/09 15:08:51 jsp beta $
+ *
+ */
+
+#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;
+u_short nfs_port;
+
+#ifdef HAVE_FS_AUTOFS
+SVCXPRT *autofsxprt = NULL;
+u_short autofs_port = 0;
+#endif /* HAVE_FS_AUTOFS */
+
+#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("%#x bytes of memory allocated; total is %#x (%ld pages)",
+ next_mem - max_mem, next_mem,
+ ((long) next_mem + getpagesize() - 1) / getpagesize());
+#else /* not HAVE_GETPAGESIZE */
+ dlog("%#x bytes of memory allocated; total is %#x",
+ next_mem - max_mem, next_mem);
+#endif /* not HAVE_GETPAGESIZE */
+ max_mem = next_mem;
+
+ }
+}
+#endif /* 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;
+ /*
+ * Invalidate the current clock value
+ */
+ clock_valid = 0;
+ /*
+ * 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()) {
+ mapc_reload();
+ do_mapc_reload = clocktime() + ONE_HOUR;
+ }
+ return nsel;
+}
+
+
+/*
+ * Determine whether anything is left in
+ * the RPC input queue.
+ */
+static int
+rpc_pending_now(void)
+{
+ struct timeval tvv;
+ int nsel;
+#ifdef FD_SET
+ fd_set readfds;
+
+ FD_ZERO(&readfds);
+ FD_SET(fwd_sock, &readfds);
+#else /* not FD_SET */
+ int readfds = (1 << fwd_sock);
+#endif /* not FD_SET */
+
+ tvv.tv_sec = tvv.tv_usec = 0;
+ nsel = select(FD_SETSIZE, &readfds, (fd_set *) 0, (fd_set *) 0, &tvv);
+ if (nsel < 1)
+ return (0);
+#ifdef FD_SET
+ if (FD_ISSET(fwd_sock, &readfds))
+ return (1);
+#else /* not FD_SET */
+ if (readfds & (1 << fwd_sock))
+ return (1);
+#endif /* not FD_SET */
+
+ 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();
+
+ 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;
+#ifdef HAVE_SVC_GETREQSET
+ fd_set readfds;
+
+ memmove(&readfds, &svc_fdset, sizeof(svc_fdset));
+ FD_SET(fwd_sock, &readfds);
+#else /* not HAVE_SVC_GETREQSET */
+# ifdef FD_SET
+ fd_set readfds;
+ FD_ZERO(&readfds);
+ readfds.fds_bits[0] = svc_fds;
+ FD_SET(fwd_sock, &readfds);
+# else /* not FD_SET */
+ int readfds = svc_fds | (1 << fwd_sock);
+# endif /* not FD_SET */
+#endif /* not HAVE_SVC_GETREQSET */
+
+#ifdef DEBUG
+ checkup();
+#endif /* DEBUG */
+
+ /*
+ * If the full timeout code is not called,
+ * then recompute the time delta manually.
+ */
+ now = clocktime();
+
+ 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 && last_used_map < 0) {
+ flush_mntfs();
+ amd_state = Quit;
+ break;
+ }
+ if (tvv.tv_sec <= 0)
+ tvv.tv_sec = SELECT_MAXWAIT;
+#ifdef DEBUG
+ if (tvv.tv_sec) {
+ dlog("Select waits for %ds", tvv.tv_sec);
+ } else {
+ dlog("Select waits for Godot");
+ }
+#endif /* DEBUG */
+
+ nsel = do_select(smask, FD_SETSIZE, &readfds, &tvv);
+
+ switch (nsel) {
+ case -1:
+ if (errno == EINTR) {
+#ifdef DEBUG
+ dlog("select interrupted");
+#endif /* DEBUG */
+ continue;
+ }
+ perror("select");
+ break;
+
+ case 0:
+ break;
+
+ default:
+ /*
+ * Read all pending NFS responses at once to avoid having responses.
+ * queue up as a consequence of retransmissions.
+ */
+#ifdef FD_SET
+ if (FD_ISSET(fwd_sock, &readfds)) {
+ FD_CLR(fwd_sock, &readfds);
+#else /* not FD_SET */
+ if (readfds & (1 << fwd_sock)) {
+ readfds &= ~(1 << fwd_sock);
+#endif /* not FD_SET */
+ --nsel;
+ do {
+ fwd_reply();
+ } while (rpc_pending_now() > 0);
+ }
+
+ if (nsel) {
+ /*
+ * Anything left must be a normal
+ * RPC request.
+ */
+#ifdef HAVE_SVC_GETREQSET
+ svc_getreqset(&readfds);
+#else /* not HAVE_SVC_GETREQSET */
+# ifdef FD_SET
+ svc_getreq(readfds.fds_bits[0]);
+# else /* not FD_SET */
+ svc_getreq(readfds);
+# endif /* not FD_SET */
+#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;
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ struct netconfig *udp_amqncp, *tcp_amqncp;
+#endif /* HAVE_TRANSPORT_TYPE_TLI */
+#ifdef HAVE_FS_AUTOFS
+ int soAUTOFS;
+#endif /* HAVE_FS_AUTOFS */
+
+ /*
+ * Create the nfs service for amd
+ */
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ ret = create_nfs_service(&soNFS, &nfs_port, &nfsxprt, nfs_program_2);
+ if (ret != 0)
+ return ret;
+ ret = create_amq_service(&udp_soAMQ, &udp_amqp, &udp_amqncp, &tcp_soAMQ, &tcp_amqp, &tcp_amqncp);
+#else /* not HAVE_TRANSPORT_TYPE_TLI */
+ ret = create_nfs_service(&soNFS, &nfs_port, &nfsxprt, nfs_program_2);
+ if (ret != 0)
+ return ret;
+ ret = create_amq_service(&udp_soAMQ, &udp_amqp, &tcp_soAMQ, &tcp_amqp);
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+ if (ret != 0)
+ return ret;
+
+#ifdef HAVE_FS_AUTOFS
+ /*
+ * Create the autofs service for amd.
+ */
+ plog(XLOG_INFO, "creating autofs service listener");
+ ret = create_autofs_service(&soAUTOFS, &autofs_port, &autofsxprt, autofs_program_1);
+ /* if autofs service fails it is OK if using a test amd */
+ if (ret != 0 && gopt.portmap_program == AMQ_PROGRAM)
+ return ret;
+#endif /* HAVE_FS_AUTOFS */
+
+ /*
+ * 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();
+
+ /*
+ * 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;
+ }
+
+#ifdef DEBUG
+ amuDebug(D_AMQ) {
+#endif /* DEBUG */
+ /*
+ * Complete registration of amq (first TCP service then UDP)
+ */
+ unregister_amq();
+
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ ret = svc_reg(tcp_amqp, get_amd_program_number(), AMQ_VERSION,
+ amq_program_1, tcp_amqncp);
+#else /* not HAVE_TRANSPORT_TYPE_TLI */
+ ret = svc_register(tcp_amqp, get_amd_program_number(), AMQ_VERSION,
+ amq_program_1, IPPROTO_TCP);
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+ if (ret != 1) {
+ plog(XLOG_FATAL, "unable to register (AMQ_PROGRAM=%d, AMQ_VERSION, tcp)", get_amd_program_number());
+ return 3;
+ }
+
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ ret = svc_reg(udp_amqp, get_amd_program_number(), AMQ_VERSION,
+ amq_program_1, udp_amqncp);
+#else /* not HAVE_TRANSPORT_TYPE_TLI */
+ ret = svc_register(udp_amqp, get_amd_program_number(), AMQ_VERSION,
+ amq_program_1, IPPROTO_UDP);
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+ if (ret != 1) {
+ plog(XLOG_FATAL, "unable to register (AMQ_PROGRAM=%d, AMQ_VERSION, udp)", get_amd_program_number());
+ return 4;
+ }
+
+#ifdef DEBUG
+ }
+#endif /* DEBUG */
+
+ /*
+ * 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/contrib/amd/amd/nfs_subr.c b/contrib/amd/amd/nfs_subr.c
new file mode 100644
index 0000000..3de0861
--- /dev/null
+++ b/contrib/amd/amd/nfs_subr.c
@@ -0,0 +1,610 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: nfs_subr.c,v 5.2.2.1 1992/02/09 15:08:53 jsp beta $
+ *
+ */
+
+#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 */
+
+/* forward declarations */
+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, nfsattrstat **attrpp)
+{
+ char *ln;
+
+ /*
+ * If there is a readlink method, then use
+ * that, 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;
+ }
+ if (attrpp)
+ *attrpp = &mp->am_attr;
+
+ 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;
+
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "getattr:");
+#endif /* DEBUG */
+
+ mp = fh_to_mp2(argp, &retry);
+ if (mp == 0) {
+
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "\tretry=%d", retry);
+#endif /* DEBUG */
+
+ if (retry < 0)
+ return 0;
+ res.ns_status = nfs_error(retry);
+ } else {
+ nfsattrstat *attrp = &mp->am_attr;
+
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "\tstat(%s), size = %d", mp->am_path,
+ attrp->ns_u.ns_attr_u.na_size);
+#endif /* DEBUG */
+
+ mp->am_stats.s_getattr++;
+ return attrp;
+ }
+
+#ifndef MNT2_NFS_OPT_SYMTTL
+ /*
+ * This code is needed to defeat Solaris 2.4's (and newer) symlink values
+ * cache. It forces the last-modifed 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.
+ */
+ if (++res.ns_u.ns_attr_u.na_mtime.nt_useconds == 0)
+ ++res.ns_u.ns_attr_u.na_mtime.nt_seconds;
+#endif /* not MNT2_NFS_OPT_SYMTTL */
+
+ 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;
+
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "lookup:");
+#endif /* DEBUG */
+
+ mp = fh_to_mp2(&argp->da_fhandle, &retry);
+ if (mp == 0) {
+ if (retry < 0)
+ return 0;
+ res.dr_status = nfs_error(retry);
+ } else {
+ int error;
+ am_node *ap;
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "\tlookuppn(%s, %s)", mp->am_path, argp->da_name);
+#endif /* DEBUG */
+ ap = (*mp->am_mnt->mf_ops->lookuppn) (mp, argp->da_name, &error, VLOOK_CREATE);
+ if (ap == 0) {
+ if (error < 0) {
+#ifdef DEBUG
+ dlog("Not sending RPC reply");
+#endif /* DEBUG */
+ amd_stats.d_drops++;
+ return 0;
+ }
+ res.dr_status = nfs_error(error);
+ } else {
+ 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
+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(transp);
+ mp->am_transp = NULL;
+#ifdef DEBUG
+ dlog("Quick reply sent for %s", mp->am_mnt->mf_mount);
+#endif /* DEBUG */
+ }
+}
+
+
+nfsreadlinkres *
+nfsproc_readlink_2_svc(am_nfs_fh *argp, struct svc_req *rqstp)
+{
+ static nfsreadlinkres res;
+ am_node *mp;
+ int retry;
+
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "readlink:");
+#endif /* DEBUG */
+
+ mp = fh_to_mp2(argp, &retry);
+ if (mp == 0) {
+ readlink_retry:
+ if (retry < 0)
+ return 0;
+ res.rlr_status = nfs_error(retry);
+ } else {
+ char *ln = do_readlink(mp, &retry, (nfsattrstat **) 0);
+ if (ln == 0)
+ goto readlink_retry;
+ res.rlr_status = NFS_OK;
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ if (ln)
+ plog(XLOG_DEBUG, "\treadlink(%s) = %s", mp->am_path, ln);
+#endif /* DEBUG */
+ 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)
+ return 0;
+ res = nfs_error(retry);
+ goto out;
+ }
+
+ if (mp->am_fattr.na_type != NFDIR) {
+ res = nfs_error(ENOTDIR);
+ goto out;
+ }
+
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "\tremove(%s, %s)", mp->am_path, argp->da_name);
+#endif /* DEBUG */
+
+ mp = (*mp->am_mnt->mf_ops->lookuppn) (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;
+
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "readdir:");
+#endif /* DEBUG */
+
+ mp = fh_to_mp2(&argp->rda_fhandle, &retry);
+ if (mp == 0) {
+ if (retry < 0)
+ return 0;
+ res.rdr_status = nfs_error(retry);
+ } else {
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "\treaddir(%s)", mp->am_path);
+#endif /* DEBUG */
+ 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;
+
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "statfs:");
+#endif /* DEBUG */
+
+ mp = fh_to_mp2(argp, &retry);
+ if (mp == 0) {
+ if (retry < 0)
+ return 0;
+ res.sfr_status = nfs_error(retry);
+ } else {
+ nfsstatfsokres *fp;
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ plog(XLOG_DEBUG, "\tstat_fs(%s)", mp->am_path);
+#endif /* DEBUG */
+
+ /*
+ * 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 (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;
+}
diff --git a/contrib/amd/amd/ops_TEMPLATE.c b/contrib/amd/amd/ops_TEMPLATE.c
new file mode 100644
index 0000000..7a60206
--- /dev/null
+++ b/contrib/amd/amd/ops_TEMPLATE.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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_TEMPLATE.c,v 5.2.2.3 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+/*
+ * 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);
+static int foofs_fmount(mntfs *mf);
+static int foofs_umount(am_node *mp);
+static int foofs_fumount(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, int count);
+static am_node * foofs_readlink(am_node *mp, int *error_return);
+static void foofs_mounted(mntfs *mf);
+static void foofs_umounted(am_node *mp);
+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_fmount, /* mount vfs */
+ foofs_umount, /* unmount vnode */
+ foofs_fumount, /* unmount VFS */
+ foofs_lookuppn, /* lookup 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 */
+ FS_MKMNT | FS_BACKGROUND | FS_AMQINFO /* flags */
+};
+
+
+/*
+ * 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, 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.
+ */
+fserver *
+foofs_ffserver(mntfs *mf)
+{
+ plog(XLOG_INFO, "entering foofs_ffserver...");
+
+ return NULL;
+}
diff --git a/contrib/amd/amd/ops_autofs.c b/contrib/amd/amd/ops_autofs.c
new file mode 100644
index 0000000..a566fc4
--- /dev/null
+++ b/contrib/amd/amd/ops_autofs.c
@@ -0,0 +1,1275 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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 5.2.2.3 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+/*
+ * Automounter filesystem
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/*
+ * CLUDGE: wrap whole file in HAVE_FS_AUTOFS, becasue
+ * 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);
+
+/* externam 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_AM_FS_UNION
+ else if (mf->mf_ops == &amfs_union_ops)
+ mnttype = "union";
+#endif /* HAVE_AM_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", foreground ? mypid : getppid(),
+ 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",
+ rqstp->rq_prog, rqstp->rq_vers, 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:%u:%lu:0x%x",
+ ur->isdirect, ur->devid, ur->rdevid, ur->next);
+#else /* HAVE_FIELD_UMNTREQUEST_RDEVID */
+ plog(XLOG_INFO, "XXX: autofs_unmount_1_svc: %d:%u:0x%x",
+ ur->isdirect, ur->devid, 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 = initialise 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 occured 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 failed to 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%x",
+ cp->fs_opts.opt_fs, 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 initialised, 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);
+
+ mp->am_fattr.na_fileid = mp->am_gen;
+
+ 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 %ds", 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]; /* authofs 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 occured then return it.
+ */
+ if (error) {
+#ifdef DEBUG
+ errno = error; /* XXX */
+ dlog("Returning error: %m", error);
+#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_ENABLE_DEFAULT_SELECTORS) {
+ /*
+ * 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);
+ if (pt == &amfs_error_ops) {
+ plog(XLOG_MAP, "failed to match defaults for \"%s\"", *sp);
+ } else {
+ dfl = strip_selectors(*sp, "/defaults");
+ plog(XLOG_MAP, "matched default selectors \"%s\"", dfl);
+ break;
+ }
+ ++sp;
+ }
+ }
+ } else { /* not enable_default_selectors */
+ /*
+ * 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_ENABLE_DEFAULT_SELECTORS) && 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/contrib/amd/amd/ops_cachefs.c b/contrib/amd/amd/ops_cachefs.c
new file mode 100644
index 0000000..0c40085
--- /dev/null
+++ b/contrib/amd/amd/ops_cachefs.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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_cachefs.c,v 5.2.2.3 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+/*
+ * 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_fmount(mntfs *mf);
+static int cachefs_fumount(mntfs *mf);
+
+
+/*
+ * Ops structure
+ */
+am_ops cachefs_ops =
+{
+ "cachefs",
+ cachefs_match,
+ cachefs_init,
+ amfs_auto_fmount,
+ cachefs_fmount,
+ amfs_auto_fumount,
+ cachefs_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* cachefs_readlink */
+ 0, /* post-mount actions */
+ 0, /* post-umount actions */
+ find_amfs_auto_srvr,
+ FS_MKMNT | FS_NOTIMEOUT | FS_UBACKGROUND | FS_AMQINFO
+};
+
+
+/*
+ * 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;
+ }
+
+#ifdef DEBUG
+ dlog("CACHEFS: using cache directory \"%s\"", fo->opt_cachedir);
+#endif /* DEBUG */
+
+ /* 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_refc == 1) {
+ 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 *mntpt, char *backdir, char *cachedir, char *opts)
+{
+ 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 = mntpt;
+ mnt.mnt_fsname = backdir;
+ mnt.mnt_type = MNTTAB_TYPE_CACHEFS;
+ mnt.mnt_opts = opts;
+
+ flags = compute_mount_flags(&mnt);
+
+ /* 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 */
+ sprintf(ca.cfs_cacheid, "%s:%s", ca.cfs_fsid, mntpt);
+ /* 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);
+}
+
+
+static int
+cachefs_fmount(mntfs *mf)
+{
+ int error;
+
+ error = mount_cachefs(mf->mf_mount,
+ mf->mf_fo->opt_rfs,
+ mf->mf_fo->opt_cachedir,
+ mf->mf_mopts);
+ 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_fumount(mntfs *mf)
+{
+ int error;
+
+ error = UMOUNT_FS(mf->mf_mount, mnttab_file_name);
+
+ /*
+ * 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);
+ sprintf(cmd, "fsck -F cachefs %s", cachedir);
+ system(cmd);
+ }
+
+ return error;
+}
diff --git a/contrib/amd/amd/ops_cdfs.c b/contrib/amd/amd/ops_cdfs.c
new file mode 100644
index 0000000..3a143e2
--- /dev/null
+++ b/contrib/amd/amd/ops_cdfs.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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_cdfs.c,v 5.2.2.1 1992/02/09 15:09:08 jsp beta $
+ *
+ */
+
+/*
+ * 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_fmount(mntfs *mf);
+static int cdfs_fumount(mntfs *mf);
+
+/*
+ * Ops structure
+ */
+am_ops cdfs_ops =
+{
+ "cdfs",
+ cdfs_match,
+ 0, /* cdfs_init */
+ amfs_auto_fmount,
+ cdfs_fmount,
+ amfs_auto_fumount,
+ cdfs_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* cdfs_readlink */
+ 0, /* cdfs_mounted */
+ 0, /* cdfs_umounted */
+ find_amfs_auto_srvr,
+ FS_MKMNT | FS_UBACKGROUND | FS_AMQINFO
+};
+
+
+/*
+ * 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;
+ }
+#ifdef DEBUG
+ dlog("CDFS: mounting device \"%s\" on \"%s\"",
+ fo->opt_dev, fo->opt_fs);
+#endif /* DEBUG */
+
+ /*
+ * Determine magic cookie to put in mtab
+ */
+ return strdup(fo->opt_dev);
+}
+
+
+static int
+mount_cdfs(char *dir, char *fs_name, char *opts)
+{
+ cdfs_args_t cdfs_args;
+ mntent_t mnt;
+ int genflags, cdfs_flags;
+
+ /*
+ * 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 = dir;
+ 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 (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 (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 (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 (hasmntopt(&mnt, MNTTAB_OPT_RRIP))
+ cdfs_flags |= MNT2_CDFS_OPT_RRIP;
+#endif /* defined(MNT2_CDFS_OPT_RRIP) && defined(MNTTAB_OPT_RRIP) */
+
+ genflags = compute_mount_flags(&mnt);
+
+#ifdef HAVE_FIELD_CDFS_ARGS_T_FLAGS
+ cdfs_args.flags = cdfs_flags;
+#endif /* HAVE_FIELD_CDFS_ARGS_T_FLAGS */
+
+#ifdef HAVE_FIELD_CDFS_ARGS_T_ISO_FLAGS
+ cdfs_args.iso_flags = genflags | cdfs_flags;
+#endif /* HAVE_FIELD_CDFS_ARGS_T_ISO_FLAGS */
+
+#ifdef HAVE_FIELD_CDFS_ARGS_T_ISO_PGTHRESH
+ cdfs_args.iso_pgthresh = hasmntval(&mnt, MNTTAB_OPT_PGTHRESH);
+#endif /* HAVE_FIELD_CDFS_ARGS_T_ISO_PGTHRESH */
+
+#ifdef HAVE_FIELD_CDFS_ARGS_T_FSPEC
+ cdfs_args.fspec = fs_name;
+#endif /* HAVE_FIELD_CDFS_ARGS_T_FSPEC */
+
+#ifdef HAVE_FIELD_CDFS_ARGS_T_NORRIP
+ /* XXX: need to provide norrip mount opt */
+ cdfs_args.norrip = 0; /* use Rock-Ridge Protocol extensions */
+#endif /* HAVE_FIELD_CDFS_ARGS_T_NORRIP */
+
+#ifdef HAVE_FIELD_CDFS_ARGS_T_SSECTOR
+ /* XXX: need to provide ssector mount option */
+ cdfs_args.ssector = 0; /* use 1st session on disk */
+#endif /* HAVE_FIELD_CDFS_ARGS_T_SSECTOR */
+
+ /*
+ * Call generic mount routine
+ */
+ return mount_fs(&mnt, genflags, (caddr_t) &cdfs_args, 0, type, 0, NULL, mnttab_file_name);
+}
+
+
+static int
+cdfs_fmount(mntfs *mf)
+{
+ int error;
+
+ error = mount_cdfs(mf->mf_mount, mf->mf_info, mf->mf_mopts);
+ if (error) {
+ errno = error;
+ plog(XLOG_ERROR, "mount_cdfs: %m");
+ return error;
+ }
+ return 0;
+}
+
+
+static int
+cdfs_fumount(mntfs *mf)
+{
+ return UMOUNT_FS(mf->mf_mount, mnttab_file_name);
+}
diff --git a/contrib/amd/amd/ops_efs.c b/contrib/amd/amd/ops_efs.c
new file mode 100644
index 0000000..4f915f7
--- /dev/null
+++ b/contrib/amd/amd/ops_efs.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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_efs.c,v 5.2.2.1 1992/02/09 15:09:08 jsp beta $
+ *
+ */
+
+/*
+ * 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_fmount(mntfs *mf);
+static int efs_fumount(mntfs *mf);
+
+/*
+ * Ops structure
+ */
+am_ops efs_ops =
+{
+ "efs",
+ efs_match,
+ 0, /* efs_init */
+ amfs_auto_fmount,
+ efs_fmount,
+ amfs_auto_fumount,
+ efs_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* efs_readlink */
+ 0, /* efs_mounted */
+ 0, /* efs_umounted */
+ find_amfs_auto_srvr,
+ FS_MKMNT | FS_NOTIMEOUT | FS_UBACKGROUND | FS_AMQINFO
+};
+
+
+/*
+ * 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;
+ }
+
+#ifdef DEBUG
+ dlog("EFS: mounting device \"%s\" on \"%s\"", fo->opt_dev, fo->opt_fs);
+#endif /* DEBUG */
+
+ /*
+ * Determine magic cookie to put in mtab
+ */
+ return strdup(fo->opt_dev);
+}
+
+
+static int
+mount_efs(char *dir, char *fs_name, char *opts)
+{
+ 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 = dir;
+ mnt.mnt_fsname = fs_name;
+ mnt.mnt_type = MNTTAB_TYPE_EFS;
+ mnt.mnt_opts = opts;
+
+ flags = compute_mount_flags(&mnt);
+
+#ifdef HAVE_FIELD_EFS_ARGS_T_FLAGS
+ efs_args.flags = 0; /* XXX: fix this to correct flags */
+#endif /* HAVE_FIELD_EFS_ARGS_T_FLAGS */
+#ifdef HAVE_FIELD_EFS_ARGS_T_FSPEC
+ efs_args.fspec = fs_name;
+#endif /* HAVE_FIELD_EFS_ARGS_T_FSPEC */
+
+ /*
+ * Call generic mount routine
+ */
+ return mount_fs(&mnt, flags, (caddr_t) &efs_args, 0, type, 0, NULL, mnttab_file_name);
+}
+
+
+static int
+efs_fmount(mntfs *mf)
+{
+ int error;
+
+ error = mount_efs(mf->mf_mount, mf->mf_info, mf->mf_mopts);
+ if (error) {
+ errno = error;
+ plog(XLOG_ERROR, "mount_efs: %m");
+ return error;
+ }
+
+ return 0;
+}
+
+
+static int
+efs_fumount(mntfs *mf)
+{
+ return UMOUNT_FS(mf->mf_mount, mnttab_file_name);
+}
diff --git a/contrib/amd/amd/ops_lofs.c b/contrib/amd/amd/ops_lofs.c
new file mode 100644
index 0000000..6555db5
--- /dev/null
+++ b/contrib/amd/amd/ops_lofs.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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_lofs.c,v 5.2.2.1 1992/02/09 15:09:08 jsp beta $
+ *
+ */
+
+/*
+ * 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_fmount(mntfs *mf);
+static int lofs_fumount(mntfs *mf);
+static int mount_lofs(char *dir, char *fs_name, char *opts);
+
+
+/*
+ * Ops structure
+ */
+am_ops lofs_ops =
+{
+ "lofs",
+ lofs_match,
+ 0, /* lofs_init */
+ amfs_auto_fmount,
+ lofs_fmount,
+ amfs_auto_fumount,
+ lofs_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* lofs_readlink */
+ 0, /* lofs_mounted */
+ 0, /* lofs_umounted */
+ find_amfs_auto_srvr,
+ FS_MKMNT | FS_NOTIMEOUT | FS_UBACKGROUND | FS_AMQINFO
+};
+
+
+/*
+ * 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;
+ }
+#ifdef DEBUG
+ dlog("LOFS: mounting fs \"%s\" on \"%s\"",
+ fo->opt_rfs, fo->opt_fs);
+#endif /* DEBUG */
+
+ /*
+ * Determine magic cookie to put in mtab
+ */
+ return strdup(fo->opt_rfs);
+}
+
+
+static int
+mount_lofs(char *dir, char *fs_name, char *opts)
+{
+ 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 = dir;
+ mnt.mnt_fsname = fs_name;
+ mnt.mnt_type = MNTTAB_TYPE_LOFS;
+ mnt.mnt_opts = opts;
+
+ flags = compute_mount_flags(&mnt);
+
+ /*
+ * Call generic mount routine
+ */
+ return mount_fs(&mnt, flags, NULL, 0, type, 0, NULL, mnttab_file_name);
+}
+
+
+static int
+lofs_fmount(mntfs *mf)
+{
+ int error;
+
+ error = mount_lofs(mf->mf_mount, mf->mf_info, mf->mf_mopts);
+ if (error) {
+ errno = error;
+ plog(XLOG_ERROR, "mount_lofs: %m");
+ return error;
+ }
+ return 0;
+}
+
+
+static int
+lofs_fumount(mntfs *mf)
+{
+ return UMOUNT_FS(mf->mf_mount, mnttab_file_name);
+}
diff --git a/contrib/amd/amd/ops_mfs.c b/contrib/amd/amd/ops_mfs.c
new file mode 100644
index 0000000..f93c30b
--- /dev/null
+++ b/contrib/amd/amd/ops_mfs.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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_mfs.c,v 5.2.2.3 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+/*
+ * 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 INPLEMENT THIS... :-) */
diff --git a/contrib/amd/amd/ops_nfs.c b/contrib/amd/amd/ops_nfs.c
new file mode 100644
index 0000000..a7006b4
--- /dev/null
+++ b/contrib/amd/amd/ops_nfs.c
@@ -0,0 +1,799 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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_nfs.c,v 5.2.2.3 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+/*
+ * 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(struct) (++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 */
+ voidp 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 */
+ struct sockaddr_in fh_sin; /* Address of mountd */
+ fserver *fh_fs; /* Server holding filesystem */
+ char *fh_path; /* Filesystem on host */
+};
+
+/* forward definitions */
+static int call_mountd(fh_cache *fp, u_long proc, fwd_fun f, voidp 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,
+ amfs_auto_fmount,
+ nfs_fmount,
+ amfs_auto_fumount,
+ nfs_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* nfs_readlink */
+ 0, /* nfs_mounted */
+ nfs_umounted,
+ find_nfs_srvr,
+ FS_MKMNT | FS_BACKGROUND | FS_AMQINFO
+};
+
+
+static fh_cache *
+find_nfs_fhandle_cache(voidp idv, int done)
+{
+ fh_cache *fp, *fp2 = 0;
+ int id = (long) idv; /* for 64-bit archs */
+
+ ITER(fp, fh_cache, &fh_head) {
+ if (fp->fh_id == id) {
+ fp2 = fp;
+ break;
+ }
+ }
+
+#ifdef DEBUG
+ if (fp2) {
+ dlog("fh cache gives fp %#x, fs %s", fp2, fp2->fh_path);
+ } else {
+ dlog("fh cache search failed");
+ }
+#endif /* DEBUG */
+
+ if (fp2 && !done) {
+ fp2->fh_error = ETIMEDOUT;
+ return 0;
+ }
+
+ return fp2;
+}
+
+
+/*
+ * Called when a filehandle appears
+ */
+static void
+got_nfs_fh(voidp pkt, int len, struct sockaddr_in * sa, struct sockaddr_in * ia, voidp idv, int done)
+{
+ fh_cache *fp;
+
+ fp = find_nfs_fhandle_cache(idv, 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)
+ fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &fp->fh_nfs_handle.v3,
+ (XDRPROC_T_TYPE) xdr_mountres3);
+ else
+#endif /* HAVE_FS_NFS3 */
+ fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &fp->fh_nfs_handle.v2,
+ (XDRPROC_T_TYPE) xdr_fhstatus);
+
+ if (!fp->fh_error) {
+#ifdef DEBUG
+ dlog("got filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path);
+#endif /* DEBUG */
+
+ /*
+ * Wakeup anything sleeping on this filehandle
+ */
+ if (fp->fh_wchan) {
+#ifdef DEBUG
+ dlog("Calling wakeup on %#x", fp->fh_wchan);
+#endif /* DEBUG */
+ 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 == 0) {
+ fp->fh_sin.sin_port = (u_short) 0;
+ fp->fh_error = -1;
+ }
+ }
+}
+
+
+static void
+discard_fh(voidp v)
+{
+ fh_cache *fp = v;
+
+ rem_que(&fp->fh_q);
+ if (fp->fh_fs) {
+#ifdef DEBUG
+ dlog("Discarding filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path);
+#endif /* DEBUG */
+ 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, voidp wchan)
+{
+ fh_cache *fp, *fp_save = 0;
+ int error;
+ int reuse_id = FALSE;
+
+#ifdef DEBUG
+ dlog("Searching cache for %s:%s", fs->fs_host, path);
+#endif /* DEBUG */
+
+ /*
+ * First search the cache
+ */
+ ITER(fp, fh_cache, &fh_head) {
+ if (fs == fp->fh_fs && STREQ(path, fp->fh_path)) {
+ switch (fp->fh_error) {
+ case 0:
+ plog(XLOG_INFO, "prime_nfs_fhandle_cache: NFS version %d", fp->fh_nfs_version);
+#ifdef HAVE_FS_NFS3
+ if (fp->fh_nfs_version == NFS_VERSION3)
+ error = fp->fh_error = unx_error(fp->fh_nfs_handle.v3.fhs_status);
+ else
+#endif /* HAVE_FS_NFS3 */
+ error = fp->fh_error = unx_error(fp->fh_nfs_handle.v2.fhs_status);
+ if (error == 0) {
+ 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, (voidp) 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;
+ }
+ break;
+ }
+ }
+
+ /*
+ * 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(struct );
+ fp->fh_wchan = wchan;
+ fp->fh_error = -1;
+ fp->fh_cid = timeout(FH_TTL, discard_fh, (voidp) 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;
+ }
+
+ /*
+ * If the address has changed then don't try to re-use the
+ * port information
+ */
+ if (fp->fh_sin.sin_addr.s_addr != fs->fs_ip->sin_addr.s_addr) {
+ fp->fh_sin = *fs->fs_ip;
+ fp->fh_sin.sin_port = 0;
+ fp->fh_nfs_version = fs->fs_version;
+ }
+ fp->fh_fs = dup_srvr(fs);
+ fp->fh_path = strdup(path);
+
+ error = call_mountd(fp, MOUNTPROC_MNT, got_nfs_fh, wchan);
+ 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, (voidp) 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 f, voidp wchan)
+{
+ struct rpc_msg mnt_msg;
+ int len;
+ char iobuf[8192];
+ 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 port;
+ error = nfs_srvr_port(fp->fh_fs, &port, wchan);
+ if (error)
+ return error;
+ fp->fh_sin.sin_port = port;
+ }
+
+ /* find the right version of the mount protocol */
+#ifdef HAVE_FS_NFS3
+ if (fp->fh_nfs_version == NFS_VERSION3)
+ mnt_version = MOUNTVERS3;
+ else
+#endif /* HAVE_FS_NFS3 */
+ mnt_version = MOUNTVERS;
+ plog(XLOG_INFO, "call_mountd: NFS version %d, mount version %d",
+ fp->fh_nfs_version, 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),
+ (voidp) iobuf,
+ len,
+ &fp->fh_sin,
+ &fp->fh_sin,
+ (voidp) ((long) fp->fh_id), /* for 64-bit archs */
+ f);
+ } 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;
+}
+
+
+/*
+ * NFS needs the local filesystem, remote filesystem
+ * remote hostname.
+ * Local filesystem defaults to remote and vice-versa.
+ */
+char *
+nfs_match(am_opts *fo)
+{
+ char *xmtab;
+
+ 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
+ */
+ xmtab = (char *) xmalloc(strlen(fo->opt_rhost) + strlen(fo->opt_rfs) + 2);
+ sprintf(xmtab, "%s:%s", fo->opt_rhost, fo->opt_rfs);
+#ifdef DEBUG
+ dlog("NFS: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"",
+ fo->opt_rhost, fo->opt_rfs, fo->opt_fs);
+#endif /* DEBUG */
+
+ return xmtab;
+}
+
+
+/*
+ * Initialize am structure for nfs
+ */
+int
+nfs_init(mntfs *mf)
+{
+ int error;
+ am_nfs_handle_t fhs;
+ char *colon;
+
+ if (mf->mf_private)
+ return 0;
+
+ colon = strchr(mf->mf_info, ':');
+ if (colon == 0)
+ return ENOENT;
+
+ error = prime_nfs_fhandle_cache(colon + 1, mf->mf_server, &fhs, (voidp) mf);
+ if (!error) {
+ mf->mf_private = (voidp) ALLOC(am_nfs_handle_t);
+ mf->mf_prfree = (void (*)(voidp)) free;
+ memmove(mf->mf_private, (voidp) &fhs, sizeof(fhs));
+ }
+ return error;
+}
+
+
+int
+mount_nfs_fh(am_nfs_handle_t *fhp, char *dir, char *fs_name, char *opts, mntfs *mf)
+{
+ MTYPE_TYPE type;
+ char *colon;
+ char *xopts;
+ 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 error;
+ int genflags;
+ int retry;
+ 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 */
+ strncpy(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)
+ strcpy(host + MAXHOSTNAMELEN - 3, "..");
+#endif /* MAXHOSTNAMELEN */
+
+ if (mf->mf_remopts && *mf->mf_remopts && !islocalnet(fs->fs_ip->sin_addr.s_addr))
+ xopts = strdup(mf->mf_remopts);
+ else
+ xopts = strdup(opts);
+
+ memset((voidp) &mnt, 0, sizeof(mnt));
+ mnt.mnt_dir = dir;
+ 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", nfs_version);
+#if defined(HAVE_FS_NFS3) || defined(HAVE_TRANSPORT_TYPE_TLI)
+ plog(XLOG_INFO, "mount_nfs_fh: using NFS transport %s", nfs_proto);
+#endif /* defined(HAVE_FS_NFS3) || defined(HAVE_TRANSPORT_TYPE_TLI) */
+
+ retry = hasmntval(&mnt, MNTTAB_OPT_RETRY);
+ if (retry <= 0)
+ retry = 1; /* XXX */
+
+ genflags = compute_mount_flags(&mnt);
+
+ /* setup the many fields and flags within nfs_args */
+#ifdef HAVE_TRANSPORT_TYPE_TLI
+ compute_nfs_args(&nfs_args,
+ &mnt,
+ genflags,
+ NULL, /* struct netconfig *nfsncp */
+ fs->fs_ip,
+ nfs_version,
+ nfs_proto,
+ fhp,
+ host,
+ fs_name);
+#else /* not HAVE_TRANSPORT_TYPE_TLI */
+ compute_nfs_args(&nfs_args,
+ &mnt,
+ genflags,
+ fs->fs_ip,
+ nfs_version,
+ nfs_proto,
+ fhp,
+ host,
+ fs_name);
+#endif /* not HAVE_TRANSPORT_TYPE_TLI */
+
+ /* finally call the mounting function */
+#ifdef DEBUG
+ amuDebug(D_TRACE)
+ print_nfs_args(&nfs_args, nfs_version);
+#endif /* DEBUG */
+ error = mount_fs(&mnt, genflags, (caddr_t) &nfs_args, retry, type,
+ nfs_version, nfs_proto, mnttab_file_name);
+ 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
+mount_nfs(char *dir, char *fs_name, char *opts, mntfs *mf)
+{
+ if (!mf->mf_private) {
+ plog(XLOG_ERROR, "Missing filehandle for %s", fs_name);
+ return EINVAL;
+ }
+
+ return mount_nfs_fh((am_nfs_handle_t *) mf->mf_private, dir, fs_name, opts, mf);
+}
+
+
+int
+nfs_fmount(mntfs *mf)
+{
+ int error = 0;
+
+ error = mount_nfs(mf->mf_mount, mf->mf_info, mf->mf_mopts, mf);
+
+#ifdef DEBUG
+ if (error) {
+ errno = error;
+ dlog("mount_nfs: %m");
+ }
+#endif /* DEBUG */
+
+ return error;
+}
+
+
+int
+nfs_fumount(mntfs *mf)
+{
+ int error = UMOUNT_FS(mf->mf_mount, mnttab_file_name);
+
+ /*
+ * 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] == '/') {
+ UMOUNT_FS(new_mf->mf_mount, mnttab_file_name);
+ didsome = 1;
+ }
+ }
+ if (didsome)
+ error = UMOUNT_FS(mf->mf_mount, mnttab_file_name);
+ }
+ if (error)
+ return error;
+
+ return 0;
+}
+
+
+void
+nfs_umounted(am_node *mp)
+{
+ /*
+ * Don't bother to inform remote mountd that we are finished. Until a
+ * full track of filehandles is maintained the mountd unmount callback
+ * cannot be done correctly anyway...
+ */
+ mntfs *mf = mp->am_mnt;
+ fserver *fs;
+ char *colon, *path;
+
+ if (mf->mf_error || mf->mf_refc > 1)
+ return;
+
+ fs = mf->mf_server;
+
+ /*
+ * Call the mount daemon on the server to announce that we are not using
+ * the fs any more.
+ *
+ * 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...
+ */
+ colon = path = strchr(mf->mf_info, ':');
+ if (fs && colon) {
+ fh_cache f;
+
+#ifdef DEBUG
+ dlog("calling mountd for %s", mf->mf_info);
+#endif /* DEBUG */
+ *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, (voidp) mf);
+ call_mountd(&f, MOUNTPROC_UMNT, (fwd_fun) 0, (voidp) 0);
+ *colon = ':';
+ }
+}
diff --git a/contrib/amd/amd/ops_nfs3.c b/contrib/amd/amd/ops_nfs3.c
new file mode 100644
index 0000000..5db0713
--- /dev/null
+++ b/contrib/amd/amd/ops_nfs3.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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_nfs3.c,v 5.2.2.3 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+/*
+ * 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 INPLEMENT THIS... :-) */
diff --git a/contrib/amd/amd/ops_nullfs.c b/contrib/amd/amd/ops_nullfs.c
new file mode 100644
index 0000000..bf2009f
--- /dev/null
+++ b/contrib/amd/amd/ops_nullfs.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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_nullfs.c,v 5.2.2.3 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+/*
+ * 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 INPLEMENT THIS... :-) */
diff --git a/contrib/amd/amd/ops_pcfs.c b/contrib/amd/amd/ops_pcfs.c
new file mode 100644
index 0000000..e46b711
--- /dev/null
+++ b/contrib/amd/amd/ops_pcfs.c
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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_pcfs.c,v 5.2.2.1 1992/02/09 15:09:08 jsp beta $
+ *
+ */
+
+/*
+ * 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_fmount(mntfs *mf);
+static int pcfs_fumount(mntfs *mf);
+
+/*
+ * Ops structure
+ */
+am_ops pcfs_ops =
+{
+ "pcfs",
+ pcfs_match,
+ 0, /* pcfs_init */
+ amfs_auto_fmount,
+ pcfs_fmount,
+ amfs_auto_fumount,
+ pcfs_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* pcfs_readlink */
+ 0, /* pcfs_mounted */
+ 0, /* pcfs_umounted */
+ find_amfs_auto_srvr,
+ FS_MKMNT | FS_UBACKGROUND | FS_AMQINFO
+};
+
+
+
+/*
+ * 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;
+ }
+#ifdef DEBUG
+ dlog("PCFS: mounting device \"%s\" on \"%s\"", fo->opt_dev, fo->opt_fs);
+#endif /* DEBUG */
+
+ /*
+ * Determine magic cookie to put in mtab
+ */
+ return strdup(fo->opt_dev);
+}
+
+
+static int
+mount_pcfs(char *dir, char *fs_name, char *opts)
+{
+ pcfs_args_t pcfs_args;
+ mntent_t mnt;
+ int flags;
+
+ /*
+ * 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 = dir;
+ mnt.mnt_fsname = fs_name;
+ mnt.mnt_type = MNTTAB_TYPE_PCFS;
+ mnt.mnt_opts = opts;
+
+ flags = compute_mount_flags(&mnt);
+
+#ifdef HAVE_FIELD_PCFS_ARGS_T_FSPEC
+ pcfs_args.fspec = fs_name;
+#endif /* HAVE_FIELD_PCFS_ARGS_T_FSPEC */
+
+#ifdef HAVE_FIELD_PCFS_ARGS_T_MASK
+ pcfs_args.mask = 0777; /* this may be the msdos file modes */
+#endif /* HAVE_FIELD_PCFS_ARGS_T_MASK */
+
+#ifdef HAVE_FIELD_PCFS_ARGS_T_UID
+ pcfs_args.uid = 0; /* root */
+#endif /* HAVE_FIELD_PCFS_ARGS_T_UID */
+
+#ifdef HAVE_FIELD_PCFS_ARGS_T_GID
+ pcfs_args.gid = 0; /* wheel */
+#endif /* HAVE_FIELD_PCFS_ARGS_T_GID */
+
+#ifdef HAVE_FIELD_PCFS_ARGS_T_SECONDSWEST
+ pcfs_args.secondswest = 0; /* XXX: fill in correct values */
+#endif /* HAVE_FIELD_PCFS_ARGS_T_SECONDSWEST */
+#ifdef HAVE_FIELD_PCFS_ARGS_T_DSTTIME
+ pcfs_args.dsttime = 0; /* XXX: fill in correct values */
+#endif /* HAVE_FIELD_PCFS_ARGS_T_DSTTIME */
+
+ /*
+ * Call generic mount routine
+ */
+ return mount_fs(&mnt, flags, (caddr_t) & pcfs_args, 0, type, 0, NULL, mnttab_file_name);
+}
+
+
+static int
+pcfs_fmount(mntfs *mf)
+{
+ int error;
+
+ error = mount_pcfs(mf->mf_mount, mf->mf_info, mf->mf_mopts);
+ if (error) {
+ errno = error;
+ plog(XLOG_ERROR, "mount_pcfs: %m");
+ return error;
+ }
+
+ return 0;
+}
+
+
+static int
+pcfs_fumount(mntfs *mf)
+{
+ return UMOUNT_FS(mf->mf_mount, mnttab_file_name);
+}
diff --git a/contrib/amd/amd/ops_tfs.c b/contrib/amd/amd/ops_tfs.c
new file mode 100644
index 0000000..97cd18c
--- /dev/null
+++ b/contrib/amd/amd/ops_tfs.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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_tfs.c,v 5.2.2.3 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+/*
+ * Translucent file system
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/* FEEL FREE TO INPLEMENT THIS... :-) */
diff --git a/contrib/amd/amd/ops_tmpfs.c b/contrib/amd/amd/ops_tmpfs.c
new file mode 100644
index 0000000..ce1b4fd
--- /dev/null
+++ b/contrib/amd/amd/ops_tmpfs.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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_tmpfs.c,v 5.2.2.3 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+/*
+ * 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 INPLEMENT THIS... :-) */
diff --git a/contrib/amd/amd/ops_ufs.c b/contrib/amd/amd/ops_ufs.c
new file mode 100644
index 0000000..9883af1
--- /dev/null
+++ b/contrib/amd/amd/ops_ufs.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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_ufs.c,v 5.2.2.1 1992/02/09 15:09:08 jsp beta $
+ *
+ */
+
+/*
+ * 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_fmount(mntfs *mf);
+static int ufs_fumount(mntfs *mf);
+
+/*
+ * Ops structure
+ */
+am_ops ufs_ops =
+{
+ "ufs",
+ ufs_match,
+ 0, /* ufs_init */
+ amfs_auto_fmount,
+ ufs_fmount,
+ amfs_auto_fumount,
+ ufs_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* ufs_readlink */
+ 0, /* ufs_mounted */
+ 0, /* ufs_umounted */
+ find_amfs_auto_srvr,
+ FS_MKMNT | FS_NOTIMEOUT | FS_UBACKGROUND | FS_AMQINFO
+};
+
+
+/*
+ * 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;
+ }
+
+#ifdef DEBUG
+ dlog("UFS: mounting device \"%s\" on \"%s\"", fo->opt_dev, fo->opt_fs);
+#endif /* DEBUG */
+
+ /*
+ * Determine magic cookie to put in mtab
+ */
+ return strdup(fo->opt_dev);
+}
+
+
+static int
+mount_ufs(char *dir, char *fs_name, char *opts)
+{
+ 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 = dir;
+ mnt.mnt_fsname = fs_name;
+ mnt.mnt_type = MNTTAB_TYPE_UFS;
+ mnt.mnt_opts = opts;
+
+ genflags = compute_mount_flags(&mnt);
+
+#ifdef HAVE_FIELD_UFS_ARGS_T_FLAGS
+ ufs_args.flags = genflags; /* XXX: is this correct? */
+#endif /* HAVE_FIELD_UFS_ARGS_T_FLAGS */
+
+#ifdef HAVE_FIELD_UFS_ARGS_T_UFS_FLAGS
+ ufs_args.ufs_flags = genflags; /* XXX: is this correct? */
+#endif /* HAVE_FIELD_UFS_ARGS_T_UFS_FLAGS */
+
+#ifdef HAVE_FIELD_UFS_ARGS_T_FSPEC
+ ufs_args.fspec = fs_name;
+#endif /* HAVE_FIELD_UFS_ARGS_T_FSPEC */
+
+#ifdef HAVE_FIELD_UFS_ARGS_T_UFS_PGTHRESH
+ ufs_args.ufs_pgthresh = hasmntval(&mnt, MNTTAB_OPT_PGTHRESH);
+#endif /* HAVE_FIELD_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);
+}
+
+
+static int
+ufs_fmount(mntfs *mf)
+{
+ int error;
+
+ error = mount_ufs(mf->mf_mount, mf->mf_info, mf->mf_mopts);
+ if (error) {
+ errno = error;
+ plog(XLOG_ERROR, "mount_ufs: %m");
+ return error;
+ }
+
+ return 0;
+}
+
+
+static int
+ufs_fumount(mntfs *mf)
+{
+ return UMOUNT_FS(mf->mf_mount, mnttab_file_name);
+}
diff --git a/contrib/amd/amd/ops_umapfs.c b/contrib/amd/amd/ops_umapfs.c
new file mode 100644
index 0000000..b2dcd72
--- /dev/null
+++ b/contrib/amd/amd/ops_umapfs.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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_umapfs.c,v 5.2.2.3 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+/*
+ * 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 INPLEMENT THIS... :-) */
diff --git a/contrib/amd/amd/ops_unionfs.c b/contrib/amd/amd/ops_unionfs.c
new file mode 100644
index 0000000..24f7e1f
--- /dev/null
+++ b/contrib/amd/amd/ops_unionfs.c
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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_unionfs.c,v 5.2.2.3 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+/*
+ * 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 INPLEMENT THIS... :-) */
diff --git a/contrib/amd/amd/ops_xfs.c b/contrib/amd/amd/ops_xfs.c
new file mode 100644
index 0000000..1b4aab4
--- /dev/null
+++ b/contrib/amd/amd/ops_xfs.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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_xfs.c,v 5.2.2.1 1992/02/09 15:09:08 jsp beta $
+ *
+ */
+
+/*
+ * 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_fmount(mntfs *mf);
+static int xfs_fumount(mntfs *mf);
+
+/*
+ * Ops structure
+ */
+am_ops xfs_ops =
+{
+ "xfs",
+ xfs_match,
+ 0, /* xfs_init */
+ amfs_auto_fmount,
+ xfs_fmount,
+ amfs_auto_fumount,
+ xfs_fumount,
+ amfs_error_lookuppn,
+ amfs_error_readdir,
+ 0, /* xfs_readlink */
+ 0, /* xfs_mounted */
+ 0, /* xfs_umounted */
+ find_amfs_auto_srvr,
+ FS_MKMNT | FS_NOTIMEOUT | FS_UBACKGROUND | FS_AMQINFO
+};
+
+
+/*
+ * 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;
+ }
+
+#ifdef DEBUG
+ dlog("XFS: mounting device \"%s\" on \"%s\"", fo->opt_dev, fo->opt_fs);
+#endif /* DEBUG */
+
+ /*
+ * Determine magic cookie to put in mtab
+ */
+ return strdup(fo->opt_dev);
+}
+
+
+static int
+mount_xfs(char *dir, char *fs_name, char *opts)
+{
+ 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 = dir;
+ mnt.mnt_fsname = fs_name;
+ mnt.mnt_type = MNTTAB_TYPE_XFS;
+ mnt.mnt_opts = opts;
+
+ flags = compute_mount_flags(&mnt);
+
+#ifdef HAVE_FIELD_XFS_ARGS_T_FLAGS
+ xfs_args.flags = 0; /* XXX: fix this to correct flags */
+#endif /* HAVE_FIELD_XFS_ARGS_T_FLAGS */
+#ifdef HAVE_FIELD_XFS_ARGS_T_FSPEC
+ xfs_args.fspec = fs_name;
+#endif /* HAVE_FIELD_XFS_ARGS_T_FSPEC */
+
+ /*
+ * Call generic mount routine
+ */
+ return mount_fs(&mnt, flags, (caddr_t) &xfs_args, 0, type, 0, NULL, mnttab_file_name);
+}
+
+
+static int
+xfs_fmount(mntfs *mf)
+{
+ int error;
+
+ error = mount_xfs(mf->mf_mount, mf->mf_info, mf->mf_mopts);
+ if (error) {
+ errno = error;
+ plog(XLOG_ERROR, "mount_xfs: %m");
+ return error;
+ }
+
+ return 0;
+}
+
+
+static int
+xfs_fumount(mntfs *mf)
+{
+ return UMOUNT_FS(mf->mf_mount, mnttab_file_name);
+}
diff --git a/contrib/amd/amd/opts.c b/contrib/amd/amd/opts.c
new file mode 100644
index 0000000..294cdb7
--- /dev/null
+++ b/contrib/amd/amd/opts.c
@@ -0,0 +1,1304 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: opts.c,v 5.2.2.4 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+#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 DEFINITSION:
+ */
+static int f_in_network(char *);
+static int f_netgrp(char *);
+static int f_exists(char *);
+static int f_false(char *);
+static int f_true(char *);
+
+/*
+ * STATICS:
+ */
+static struct am_opts fs_static; /* copy of the options to play with */
+static char NullStr[] = "<NULL>";
+static char nullstr[] = "";
+static char *opt_dkey = NullStr;
+static char *opt_host = 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;
+static char *vars[8];
+
+
+/*
+ * Options in something 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. flags */
+ { 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("autopref"),
+ &fs_static.opt_autopref,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("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("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("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 },
+ { "netgrp", f_netgrp },
+ { "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_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_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
+ */
+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 'a':
+ 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;
+}
+
+
+static int
+eval_opts(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 *f;
+
+ /*
+ * For each user-specified option
+ */
+ while (*(f = opt(&o))) {
+ struct opt *op;
+ enum vs_opt vs_opt = VarAss;
+ char *eq = strchr(f, '=');
+ char *fx;
+ IntFuncPtr func;
+ char *opt = NULL;
+
+ if (!eq || eq[1] == '\0' || eq == f) {
+ /*
+ * No value, is it a function call?
+ */
+ char *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';
+
+ /*
+ * look up f in functable and pass it arg.
+ * func must return 0 on failure, and 1 on success.
+ */
+ if ((func = functable_lookup(f))) {
+ if (!(*func) (arg)) {
+ return (0);
+ }
+ continue;
+ } else if (NSTREQ(f, "!", 1) && (func = functable_lookup(&f[1]))) {
+ /* then this is a negated prefixed function such as "!exists" */
+ plog(XLOG_USER, "executing negated function %s", &f[1]);
+ if ((*func) (arg)) {
+ return (0);
+ }
+ continue;
+ } else {
+ plog(XLOG_USER, "key %s: unknown function \"%s\"", mapkey, f);
+ return (0);
+ }
+
+ }
+
+ /*
+ * 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] == ':') { /* := */
+ vs_opt = VarAss;
+ eq[-1] = '\0';
+ opt = eq + 1;
+ } 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;
+ }
+
+ /*
+ * For each recognized option
+ */
+ for (op = opt_fields; op->name; op++) {
+ /*
+ * Check whether they match
+ */
+ if (FSTREQ(op->name, f)) {
+ int selok;
+ switch (vs_opt) {
+ case SelEQ:
+ case SelNE:
+ if ((selok = (op->sel_p != NULL))) {
+ if (op->case_insensitive) {
+ selok = (STRCEQ(*op->sel_p, opt) == (vs_opt == SelNE));
+ } else {
+ selok = (STREQ(*op->sel_p, opt) == (vs_opt == SelNE));
+ }
+ }
+ 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);
+ return 0;
+ }
+ /* check if to apply a function */
+ if (op->fxn_p &&
+ ((*op->fxn_p)(opt) == (vs_opt == SelNE))) {
+ plog(XLOG_MAP, "key %s: map function %s did not %smatch %s",
+ mapkey,
+ op->name,
+ vs_opt == SelNE ? "mis" : "",
+ opt);
+ return 0;
+ }
+ break;
+
+ case VarAss:
+ if (op->sel_p) {
+ plog(XLOG_USER, "key %s: Can't assign to a selector (%s)",
+ mapkey, op->name);
+ return 0;
+ }
+ *op->optp = opt;
+ break;
+
+ } /* end of "switch (vs_opt)" statement */
+ break; /* break out of for loop */
+ }
+ }
+
+ if (!op->name)
+ plog(XLOG_USER, "key %s: Unrecognized key/option \"%s\"", mapkey, f);
+ }
+
+ return 1;
+}
+
+
+/*
+ * 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 */
+#ifdef DEBUG
+ dlog("found first assignment past selectors \"%s\"", o);
+#endif /* DEBUG */
+ 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 FALSE;
+
+ status = is_network_member(arg);
+#ifdef DEBUG
+ plog(XLOG_USER, "%s is %son a local network",
+ arg, (status ? "" : "not "));
+#endif /* DEBUG */
+ return status;
+}
+
+
+/* test if this host is in netgroup (arg) */
+static int
+f_netgrp(char *arg)
+{
+ int status;
+
+ status = innetgr(arg, opt_host, NULL, NULL);
+#ifdef DEBUG
+ plog(XLOG_USER, "netgrp = %s status = %d host = %s", arg, status, opt_host);
+#endif /* DEBUG */
+ 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);
+ *p->opt = 0;
+ }
+}
+
+
+/*
+ * Normalize slashes in the string.
+ */
+void
+normalize_slash(char *p)
+{
+ char *f = strchr(p, '/');
+ char *f0 = f;
+
+ 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 != '/') {
+ *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 is true then old expand selectors, otherwise
+ * don't expand selectors.
+ */
+static void
+expand_op(opt_apply *p, int sel_p)
+{
+ static char expand_error[] = "No space to expand \"%s\"";
+ char expbuf[MAXPATHLEN + 1];
+ char nbuf[NLEN + 1];
+ char *ep = expbuf;
+ char *cp = *p->opt;
+ char *dp;
+ struct opt *op;
+#ifdef DEBUG
+ char *cp_orig = *p->opt;
+#endif /* DEBUG */
+
+ while ((dp = strchr(cp, '$'))) {
+ char ch;
+ /*
+ * First copy up to the $
+ */
+ {
+ int len = dp - cp;
+
+ if (BUFSPACE(ep, len)) {
+ strncpy(ep, cp, len);
+ ep += len;
+ } else {
+ plog(XLOG_ERROR, expand_error, *p->opt);
+ goto out;
+ }
+ }
+
+ cp = dp + 1;
+ ch = *cp++;
+ if (ch == '$') {
+ if (BUFSPACE(ep, 1)) {
+ *ep++ = '$';
+ } else {
+ plog(XLOG_ERROR, expand_error, *p->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\"", *p->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.
+ */
+ 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
+ */
+ sprintf(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)) {
+ strcpy(ep, vptr);
+ ep += vlen;
+ } else {
+ plog(XLOG_ERROR, expand_error, *p->opt);
+ goto out;
+ }
+ }
+ /*
+ * Done with this variable
+ */
+ break;
+ }
+ }
+
+ /*
+ * Check that the search was succesful
+ */
+ 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)) {
+ strcpy(ep, env);
+ ep += vlen;
+ } else {
+ plog(XLOG_ERROR, expand_error, *p->opt);
+ goto out;
+ }
+#ifdef DEBUG
+ amuDebug(D_STR)
+ plog(XLOG_DEBUG, "Environment gave \"%s\" -> \"%s\"", nbuf, env);
+#endif /* DEBUG */
+ } else {
+ plog(XLOG_USER, "Unknown sequence \"${%s}\"", nbuf);
+ }
+ }
+ } else {
+ /*
+ * Error, error
+ */
+ plog(XLOG_USER, "Unknown $ sequence in \"%s\"", *p->opt);
+ }
+ }
+
+out:
+ /*
+ * Handle common case - no expansion
+ */
+ if (cp == *p->opt) {
+ *p->opt = strdup(cp);
+ } else {
+ /*
+ * Finish off the expansion
+ */
+ if (BUFSPACE(ep, strlen(cp))) {
+ strcpy(ep, cp);
+ /* ep += strlen(ep); */
+ } else {
+ plog(XLOG_ERROR, expand_error, *p->opt);
+ }
+
+ /*
+ * Save the exansion
+ */
+ *p->opt = strdup(expbuf);
+ }
+
+ normalize_slash(*p->opt);
+
+#ifdef DEBUG
+ amuDebug(D_STR) {
+ plog(XLOG_DEBUG, "Expansion of \"%s\"...", cp_orig);
+ plog(XLOG_DEBUG, "... is \"%s\"", *p->opt);
+ }
+#endif /* DEBUG */
+}
+
+
+/*
+ * Wrapper for expand_op
+ */
+static void
+expand_opts(opt_apply *p, int sel_p)
+{
+ if (*p->opt) {
+ expand_op(p, sel_p);
+ } else if (p->val) {
+ /*
+ * Do double expansion, remembering
+ * to free the string from the first
+ * expansion...
+ */
+ char *s = *p->opt = expand_key(p->val);
+ expand_op(p, 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 lookup key
+ */
+char *
+expand_key(char *key)
+{
+ opt_apply oa;
+
+ oa.opt = &key;
+ oa.val = 0;
+ expand_opts(&oa, TRUE);
+
+ return key;
+}
+
+
+/*
+ * Remove trailing /'s from a string
+ * unless the string is a single / (Steven Glassman)
+ * or unless it is two slashes // (Kevin D. Bond)
+ */
+void
+deslashify(char *s)
+{
+ 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 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_key(g_opts);
+
+ /*
+ * Expand local options
+ */
+ fs_static.fs_local = expand_key(opts);
+
+ /*
+ * Expand default (global) options
+ */
+ if (!eval_opts(fs_static.fs_glob, key))
+ ok = FALSE;
+
+ /*
+ * Expand local options
+ */
+ if (ok && !eval_opts(fs_static.fs_local, key))
+ ok = FALSE;
+
+ /*
+ * Normalise 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/contrib/amd/amd/restart.c b/contrib/amd/amd/restart.c
new file mode 100644
index 0000000..572df86
--- /dev/null
+++ b/contrib/amd/amd/restart.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: restart.c,v 5.2.2.2 1992/08/02 10:42:21 jsp Exp $
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+
+/*
+ * 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 will leave a
+ * dangling reference to that filesystems, so when the filesystem is
+ * finally inherited, an extra "free" must be done on it.
+ *
+ * This module relies on internal details of other components. If
+ * you change something else make *sure* restart() still works.
+ */
+void
+restart(void)
+{
+ /*
+ * Read the existing mount table
+ */
+ mntlist *ml, *mlp;
+
+ /*
+ * 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_UFS)) {
+ /*
+ * UFS entry
+ */
+ fs_ops = &ufs_ops;
+ } else 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")) {
+ plog(XLOG_WARNING, "%s is an existing automount point", me->mnt_dir);
+ fs_ops = &amfs_link_ops;
+ } else {
+ fs_ops = &nfs_ops;
+ }
+#ifdef MNTTAB_TYPE_NFS3
+ } else if (STREQ(me->mnt_type, MNTTAB_TYPE_NFS3)) {
+ fs_ops = &nfs_ops;
+#endif /* MNTTAB_TYPE_NFS3 */
+#ifdef MNTTAB_TYPE_LOFS
+ } else if (STREQ(me->mnt_type, MNTTAB_TYPE_LOFS)) {
+ fs_ops = &lofs_ops;
+#endif /* MNTTAB_TYPE_LOFS */
+#ifdef MNTTAB_TYPE_CDFS
+ } else if (STREQ(me->mnt_type, MNTTAB_TYPE_CDFS)) {
+ fs_ops = &cdfs_ops;
+#endif /* MNTTAB_TYPE_CDFS */
+#ifdef MNTTAB_TYPE_PCFS
+ } else if (STREQ(me->mnt_type, MNTTAB_TYPE_PCFS)) {
+ fs_ops = &pcfs_ops;
+#endif /* MNTTAB_TYPE_PCFS */
+#ifdef MNTTAB_TYPE_MFS
+ } else if (STREQ(me->mnt_type, MNTTAB_TYPE_MFS)) {
+ /*
+ * MFS entry. Fake with a symlink.
+ */
+ fs_ops = &amfs_link_ops;
+#endif /* MNTTAB_TYPE_MFS */
+ } else {
+ /*
+ * Catch everything else with symlinks to
+ * avoid recursive mounts. This is debatable...
+ */
+ fs_ops = &amfs_link_ops;
+ }
+
+ /*
+ * If we found something to do
+ */
+ if (fs_ops) {
+ mntfs *mf;
+ am_opts mo;
+ char *cp;
+ cp = strchr(me->mnt_fsname, ':');
+
+ /*
+ * Partially fake up an opts structure
+ */
+ mo.opt_rhost = 0;
+ mo.opt_rfs = 0;
+ if (cp) {
+ *cp = '\0';
+ mo.opt_rhost = strdup(me->mnt_fsname);
+ mo.opt_rfs = strdup(cp + 1);
+ *cp = ':';
+ } else if (fs_ops->ffserver == find_nfs_srvr) {
+ /*
+ * Prototype 4.4 BSD used to end up here -
+ * might as well keep the workaround for now
+ */
+ 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;
+ /*
+ * If the restarted type is a link then
+ * don't time out.
+ */
+ if (fs_ops == &amfs_link_ops || fs_ops == &ufs_ops)
+ 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",
+ me->mnt_fsname, fs_ops->fs_type, me->mnt_dir);
+ } 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);
+ }
+ }
+
+ /*
+ * Free the mount list
+ */
+ free_mntlist(ml);
+}
diff --git a/contrib/amd/amd/rpc_fwd.c b/contrib/amd/amd/rpc_fwd.c
new file mode 100644
index 0000000..7f3c59b
--- /dev/null
+++ b/contrib/amd/amd/rpc_fwd.c
@@ -0,0 +1,476 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: rpc_fwd.c,v 5.2.2.1 1992/02/09 15:09:01 jsp beta $
+ *
+ */
+
+/*
+ * 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(struct ) (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();
+ 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.
+ */
+#ifdef DEBUG
+ dlog("Re-using packet forwarding slot - id %#x", p->rf_xid);
+#endif /* DEBUG */
+ 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");
+
+ 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, voidp pkt, int len, struct sockaddr_in *fwdto, struct sockaddr_in *replyto, voidp i, 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.
+ */
+#ifdef DEBUG
+ switch (type_id & RPC_XID_MASK) {
+ case RPC_XID_PORTMAP:
+ dlog("Sending PORTMAP request");
+ break;
+ case RPC_XID_MOUNTD:
+ dlog("Sending MOUNTD request %#x", type_id);
+ break;
+ case RPC_XID_NFSPING:
+ dlog("Sending NFS ping");
+ break;
+ default:
+ dlog("UNKNOWN RPC XID");
+ break;
+ }
+#endif /* DEBUG */
+
+ if (type_id & ~RPC_XID_MASK) {
+ p = fwd_locate(type_id);
+ if (p) {
+#ifdef DEBUG
+ dlog("Discarding earlier rpc fwd handle");
+#endif /* DEBUG */
+ fwd_free(p);
+ }
+ } else {
+#ifdef DEBUG
+ dlog("Allocating a new xid...");
+#endif /* DEBUG */
+ type_id = MK_RPC_XID(type_id, XID_ALLOC(struct ));
+ }
+
+ p = fwd_alloc();
+ if (!p)
+ return ENOBUFS;
+
+ error = 0;
+
+ pkt_int = (u_int *) pkt;
+
+ /*
+ * Get the original packet id
+ */
+ p->rf_oldid = *pkt_int;
+
+ /*
+ * Replace with newly allocated id
+ */
+ p->rf_xid = *pkt_int = 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, 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 = i;
+
+ 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;
+ 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);
+ }
+#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;
+
+#ifdef DEBUG
+ switch (*pkt_int & RPC_XID_MASK) {
+ case RPC_XID_PORTMAP:
+ dlog("Receiving PORTMAP reply");
+ break;
+ case RPC_XID_MOUNTD:
+ dlog("Receiving MOUNTD reply %#x", *pkt_int);
+ break;
+ case RPC_XID_NFSPING:
+ dlog("Receiving NFS ping %#x", *pkt_int);
+ break;
+ default:
+ dlog("UNKNOWN RPC XID");
+ break;
+ }
+#endif /* DEBUG */
+
+ p = fwd_locate(*pkt_int);
+ if (!p) {
+#ifdef DEBUG
+ dlog("Can't forward reply id %#x", *pkt_int);
+#endif /* DEBUG */
+ goto out;
+ }
+
+ if (p->rf_fwd) {
+ /*
+ * Put the original message id back
+ * into the packet.
+ */
+ *pkt_int = 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/contrib/amd/amd/sched.c b/contrib/amd/amd/sched.c
new file mode 100644
index 0000000..ffbe2c8
--- /dev/null
+++ b/contrib/amd/amd/sched.c
@@ -0,0 +1,300 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: sched.c,v 5.2.2.1 1992/02/09 15:09:02 jsp beta $
+ *
+ */
+
+/*
+ * 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 */
+ voidp cb_closure; /* Closure for callback */
+ int w; /* everyone these days uses int, not a "union wait" */
+ voidp 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, voidp ca)
+{
+ pjob *p = ALLOC(struct pjob);
+
+ p->cb_fun = cf;
+ p->cb_closure = 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, voidp ta, cb_fun cf, voidp 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 = (voidp) p;
+
+#ifdef HAVE_SIGACTION
+ sigemptyset(&new); /* initialise 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 have returned to caller */
+
+ exit((*tf) (ta));
+ /* firewall... */
+ abort();
+}
+
+
+/*
+ * Schedule a task to be run when woken up
+ */
+void
+sched_task(cb_fun cf, voidp ca, voidp wchan)
+{
+ /*
+ * Allocate a new task
+ */
+ pjob *p = sched_job(cf, ca);
+
+#ifdef DEBUG
+ dlog("SLEEP on %#x", wchan);
+#endif /* DEBUG */
+ p->wchan = wchan;
+ p->pid = 0;
+ memset((voidp) &p->w, 0, sizeof(p->w));
+}
+
+
+static void
+wakeupjob(pjob *p)
+{
+ rem_que(&p->hdr);
+ ins_que(&p->hdr, &proc_list_head);
+ task_notify_todo++;
+}
+
+
+void
+wakeup(voidp wchan)
+{
+ pjob *p, *p2;
+
+ if (!foreground)
+ return;
+
+ /*
+ * Can't user 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, voidp cl)
+{
+ wakeup(cl);
+}
+
+
+/*
+ * 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 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 compatibity with strict POSIX.1 */
+ (*p->cb_fun) (WIFEXITED(p->w) ? WEXITSTATUS(p->w) : 0,
+ WIFSIGNALED(p->w) ? WTERMSIG(p->w) : 0,
+ p->cb_closure);
+ }
+ 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));
+#ifdef DEBUG
+ else
+ dlog("Process %d exited with status %d",
+ pid, WEXITSTATUS(w));
+#endif /* DEBUG */
+
+ 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 */
+
+#ifdef DEBUG
+ if (!p)
+ dlog("can't locate task block for pid %d", pid);
+#endif /* DEBUG */
+
+ /*
+ * Must count down children inside the while loop, otherwise we won't
+ * count them all, and NumChild (and later backoff) will be set
+ * incorrectly. SH/RUNIT 940519.
+ */
+ if (--NumChild < 0)
+ NumChild = 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/contrib/amd/amd/srvr_amfs_auto.c b/contrib/amd/amd/srvr_amfs_auto.c
new file mode 100644
index 0000000..e77c591
--- /dev/null
+++ b/contrib/amd/amd/srvr_amfs_auto.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: srvr_amfs_auto.c,v 5.2.2.1 1992/02/09 15:09:05 jsp beta $
+ *
+ */
+
+/*
+ * Automount FS server ("localhost") modeling
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+#include <am_defs.h>
+#include <amd.h>
+
+/* globals */
+qelem amfs_auto_srvr_list = {&amfs_auto_srvr_list, &amfs_auto_srvr_list};
+
+/* statics */
+static fserver *localhost;
+
+
+/*
+ * Find an nfs server for the local host
+ */
+fserver *
+find_amfs_auto_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 = 0;
+ fs->fs_flags = FSF_VALID;
+ 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) {
+#ifdef DEBUG
+ dlog("Deleting file server %s", fs->fs_host);
+#endif /* DEBUG */
+ 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 = (fs->fs_flags & (FSF_DOWN | FSF_ERROR)) ? 19 : AM_TTL;
+
+#ifdef DEBUG
+ dlog("Last hard reference to file server %s - will timeout in %ds", fs->fs_host, ttl);
+#endif /* DEBUG */
+ 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 %s", fs->fs_host, fs->fs_type, state);
+}
diff --git a/contrib/amd/amd/srvr_nfs.c b/contrib/amd/amd/srvr_nfs.c
new file mode 100644
index 0000000..22a2640
--- /dev/null
+++ b/contrib/amd/amd/srvr_nfs.c
@@ -0,0 +1,851 @@
+/*
+ * Copyright (c) 1997-1998 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 acknowledgement:
+ * 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: srvr_nfs.c,v 5.2.2.1 1992/02/09 15:09:06 jsp beta $
+ *
+ */
+
+/*
+ * 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 */
+
+#define NPXID_ALLOC(struct ) (++np_xid)
+
+/* 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 np_xid; /* For NFS pings */
+static int ping_len;
+static char ping_buf[sizeof(struct rpc_msg) + 32];
+
+#if defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3)
+/* protocols we know about, in order of preference */
+static char *protocols[] = { "tcp", "udp", NULL };
+#endif /* defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) */
+
+/* forward definitions */
+static void nfs_keepalive(voidp);
+
+
+
+/*
+ * Flush any cached data
+ */
+void
+flush_srvr_nfs_cache(void)
+{
+ fserver *fs = 0;
+
+ ITER(fs, fserver, &nfs_srvr_list) {
+ nfs_private *np = (nfs_private *) fs->fs_private;
+ if (np) {
+ np->np_mountd_inval = TRUE;
+ np->np_error = -1;
+ }
+ }
+}
+
+
+/*
+ * Startup the NFS ping for a particular version.
+ */
+static void
+start_ping(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, "start_ping: nfs_version = 0 fixed");
+ }
+ plog(XLOG_INFO, "start_ping: nfs_version: %d", nfs_version);
+
+ rpc_msg_init(&ping_msg, NFS_PROGRAM, nfs_version, NFSPROC_NULL);
+
+ /*
+ * Create an XDR endpoint
+ */
+ xdrmem_create(&ping_xdr, ping_buf, sizeof(ping_buf), 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 = 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) {
+#ifdef DEBUG
+ dlog("got port (%d) for mountd on %s", port, fs->fs_host);
+#endif /* DEBUG */
+ /*
+ * 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 {
+#ifdef DEBUG
+ dlog("Error fetching port for mountd on %s", fs->fs_host);
+ dlog("\t error=%d, port=%d", error, port);
+#endif /* DEBUG */
+ /*
+ * 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) {
+#ifdef DEBUG
+ dlog("Got portmap for old port request");
+#endif /* DEBUG */
+ } else {
+#ifdef DEBUG
+ dlog("portmap request timed out");
+#endif /* DEBUG */
+ }
+}
+
+
+/*
+ * 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, (voidp) 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;
+
+ 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", fs->fs_version);
+#ifdef HAVE_FS_NFS3
+ if (fs->fs_version == NFS_VERSION3)
+ mnt_version = MOUNTVERS3;
+ else
+#endif /* HAVE_FS_NFS3 */
+ mnt_version = MOUNTVERS;
+
+ plog(XLOG_INFO, "Using MOUNT version: %d", mnt_version);
+ call_portmap(fs, nfs_auth, MOUNTPROG, mnt_version, (u_long) IPPROTO_UDP);
+}
+
+
+/*
+ * 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_pinged(voidp pkt, int len, struct sockaddr_in * sp, struct sockaddr_in * tsp, voidp idv, int done)
+{
+ int xid = (long) idv; /* for 64-bit archs */
+ fserver *fs;
+#ifdef DEBUG
+ int found_map = 0;
+#endif /* DEBUG */
+
+ 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");
+#ifdef DEBUG
+ else
+ srvrlog(fs, "starts up");
+#endif /* DEBUG */
+ fs->fs_flags |= FSF_VALID;
+ }
+
+ map_flush_srvr(fs);
+ } else {
+ if (fs->fs_flags & FSF_VALID) {
+#ifdef DEBUG
+ dlog("file server %s type nfs is still up", fs->fs_host);
+#endif /* DEBUG */
+ } 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() +
+ (MAX_ALLOWED_PINGS - 1) * FAST_NFS_PING + fs->fs_pinger - 1;
+
+ /*
+ * New RPC xid...
+ */
+ np->np_xid = NPXID_ALLOC(struct );
+
+ /*
+ * Failed pings is zero...
+ */
+ np->np_ping = 0;
+
+ /*
+ * Recompute portmap information if not known
+ */
+ if (np->np_mountd_inval)
+ recompute_portmap(fs);
+
+#ifdef DEBUG
+ found_map++;
+#endif /* DEBUG */
+ break;
+ }
+ }
+
+#ifdef DEBUG
+ if (found_map == 0)
+ dlog("Spurious ping packet");
+#endif /* DEBUG */
+}
+
+
+/*
+ * Called when no ping-reply received
+ */
+static void
+nfs_timed_out(voidp v)
+{
+ fserver *fs = v;
+ nfs_private *np = (nfs_private *) fs->fs_private;
+
+ /*
+ * Another ping has failed
+ */
+ np->np_ping++;
+
+ /*
+ * Not known to be up any longer
+ */
+ if (FSRV_ISUP(fs)) {
+ fs->fs_flags &= ~FSF_VALID;
+ if (np->np_ping > 1)
+ srvrlog(fs, "not responding");
+ }
+
+ /*
+ * If ttl has expired then guess that it is dead
+ */
+ if (np->np_ttl < clocktime()) {
+ int oflags = fs->fs_flags;
+ 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;
+ } else {
+ /*
+ * Known to be down
+ */
+#ifdef DEBUG
+ if ((fs->fs_flags & FSF_VALID) == 0)
+ srvrlog(fs, "starts down");
+#endif /* DEBUG */
+ fs->fs_flags |= FSF_VALID;
+ }
+ if (oflags != fs->fs_flags && (fs->fs_flags & FSF_WANT))
+ wakeup_srvr(fs);
+ } else {
+#ifdef DEBUG
+ if (np->np_ping > 1)
+ dlog("%d pings to %s failed - at most %d allowed", np->np_ping, fs->fs_host, MAX_ALLOWED_PINGS);
+#endif /* DEBUG */
+ }
+
+ /*
+ * 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 == 0)
+ start_ping(fs->fs_version);
+
+ /*
+ * Queue the packet...
+ */
+ error = fwd_packet(MK_RPC_XID(RPC_XID_NFSPING, np->np_xid),
+ (voidp) ping_buf,
+ ping_len,
+ fs->fs_ip,
+ (struct sockaddr_in *) 0,
+ (voidp) ((long) np->np_xid), /* for 64-bit archs */
+ nfs_pinged);
+
+ /*
+ * See if a hard error occured
+ */
+ 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_timed_out
+ * whenever the server was thought to be up.
+ * See +++ below.
+ */
+ fstimeo = 0;
+ break;
+
+ case 0:
+#ifdef DEBUG
+ dlog("Sent NFS ping to %s", fs->fs_host);
+#endif /* DEBUG */
+ break;
+ }
+
+ /*
+ * Back off the ping interval if we are not getting replies and
+ * the remote system is know 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;
+ }
+
+#ifdef DEBUG
+ dlog("NFS timeout in %d seconds", fstimeo);
+#endif /* DEBUG */
+
+ fs->fs_cid = timeout(fstimeo, nfs_timed_out, (voidp) fs);
+}
+
+
+int
+nfs_srvr_port(fserver *fs, u_short * port, voidp wchan)
+{
+ int error = -1;
+ if ((fs->fs_flags & FSF_VALID) == FSF_VALID) {
+ if ((fs->fs_flags & FSF_DOWN) == 0) {
+ 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;
+ } else {
+ error = EWOULDBLOCK;
+ }
+ }
+ if (error < 0 && wchan && !(fs->fs_flags & FSF_WANT)) {
+ /*
+ * If a wait channel is supplied, and no
+ * error has yet occured, 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, (voidp) fs);
+ }
+ return error;
+}
+
+
+static void
+start_nfs_pings(fserver *fs, int pingval)
+{
+ if (!(fs->fs_flags & FSF_PINGING)) {
+ fs->fs_flags |= FSF_PINGING;
+ if (fs->fs_cid)
+ untimeout(fs->fs_cid);
+ if (pingval < 0) {
+ srvrlog(fs, "wired up");
+ fs->fs_flags |= FSF_VALID;
+ fs->fs_flags &= ~FSF_DOWN;
+ } else {
+ nfs_keepalive(fs);
+ }
+ } else {
+#ifdef DEBUG
+ dlog("Already running pings to %s", fs->fs_host);
+#endif /* DEBUG */
+ }
+}
+
+
+/*
+ * Find an nfs server for a host.
+ */
+fserver *
+find_nfs_srvr(mntfs *mf)
+{
+ char *host = mf->mf_fo->opt_rhost;
+ char *nfs_proto = NULL;
+ fserver *fs;
+ int pingval;
+ mntent_t mnt;
+ nfs_private *np;
+ struct hostent *hp = 0;
+ struct sockaddr_in *ip;
+ u_long nfs_version = 0; /* default is no version specified */
+#ifdef MNTTAB_OPT_PROTO
+ char *rfsname = mf->mf_fo->opt_rfs;
+#endif /* MNTTAB_OPT_PROTO */
+
+ /*
+ * 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");
+
+ /*
+ * 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 = hasmntopt(&mnt, MNTTAB_OPT_PROTO);
+ if (proto_opt) {
+ char **p;
+
+ proto_opt += sizeof(MNTTAB_OPT_PROTO) - 1; /* skip the "proto" */
+
+ for (p = protocols; *p; p ++)
+ if (proto_opt[0] == '=' &&
+ NSTREQ(&proto_opt[1], *p, strlen(*p))) {
+ nfs_proto = *p;
+ break;
+ }
+ if (*p == NULL)
+ plog(XLOG_WARNING, "ignoring unknown protocol option for %s:%s",
+ host, rfsname);
+ }
+ }
+#endif /* MNTTAB_OPT_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));
+ ip->sin_family = AF_INET;
+ memmove((voidp) &ip->sin_addr, (voidp) hp->h_addr, sizeof(ip->sin_addr));
+
+ ip->sin_port = htons(NFS_PORT);
+ break;
+
+ default:
+ ip = 0;
+ break;
+ }
+ } else {
+ plog(XLOG_USER, "Unknown host: %s", host);
+ ip = 0;
+ }
+
+ /*
+ * Get the NFS Version, and verify server is up. Probably no
+ * longer need to start server down below.
+ */
+ if (ip) {
+#ifdef HAVE_FS_NFS3
+ /*
+ * 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 (nfs_proto)
+ nfs_version = get_nfs_version(host, ip, nfs_version, nfs_proto);
+ else {
+ int best_nfs_version = 0;
+ 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_version = best_nfs_version;
+ }
+
+ if (!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 that is down. If when the
+ * server comes back up, it can support NFS V.3 and/or TCP, it will
+ * use those.
+ */
+ nfs_version = NFS_VERSION;
+ nfs_proto = "udp";
+ }
+#else /* not HAVE_FS_NFS3 */
+ nfs_version = NFS_VERSION;
+#endif /* not HAVE_FS_NFS3 */
+ }
+
+ if (!nfs_proto)
+ nfs_proto = "udp";
+
+ plog(XLOG_INFO, "Using NFS version %d, protocol %s on host %s",
+ nfs_version, nfs_proto, host);
+
+ /*
+ * Try to find an existing fs server stucture 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)) {
+ /*
+ * 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 = NPXID_ALLOC(struct );
+ 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() - 1;
+ }
+ /*
+ * 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)
+ memmove((voidp) &fs->fs_ip->sin_addr, (voidp) hp->h_addr, sizeof(fs->fs_ip->sin_addr));
+
+ start_nfs_pings(fs, pingval);
+ 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;
+ }
+ fs->fs_version = nfs_version;
+ fs->fs_proto = nfs_proto;
+ fs->fs_type = MNTTAB_TYPE_NFS;
+ fs->fs_pinger = AM_PINGER;
+ np = ALLOC(struct nfs_private);
+ memset((voidp) np, 0, sizeof(*np));
+ np->np_mountd_inval = TRUE;
+ np->np_xid = NPXID_ALLOC(struct );
+ 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() + MAX_ALLOWED_PINGS * FAST_NFS_PING - 1;
+ fs->fs_private = (voidp) np;
+ fs->fs_prfree = (void (*)(voidp)) free;
+
+ if (!(fs->fs_flags & FSF_ERROR)) {
+ /*
+ * Start of keepalive timer
+ */
+ start_nfs_pings(fs, pingval);
+ }
+
+ /*
+ * Add to list of servers
+ */
+ ins_que(&fs->fs_q, &nfs_srvr_list);
+
+ return fs;
+}
OpenPOWER on IntegriCloud