diff options
author | obrien <obrien@FreeBSD.org> | 2007-12-05 15:48:03 +0000 |
---|---|---|
committer | obrien <obrien@FreeBSD.org> | 2007-12-05 15:48:03 +0000 |
commit | ea691ecd980d2860f0b39e9e504de4da404806fd (patch) | |
tree | fa83b3bb0660890a21b550fa1f334b151bf098f9 /contrib/amd/amd | |
parent | c06a2e613662fc6ce10145b41762be8f8ab22ba4 (diff) | |
download | FreeBSD-src-ea691ecd980d2860f0b39e9e504de4da404806fd.zip FreeBSD-src-ea691ecd980d2860f0b39e9e504de4da404806fd.tar.gz |
Virgin import of AMD (am-utils) v6.1.5
Sponsored by: Juniper Networks
Diffstat (limited to 'contrib/amd/amd')
63 files changed, 7859 insertions, 4717 deletions
diff --git a/contrib/amd/amd/am_ops.c b/contrib/amd/amd/am_ops.c index 3097aee..5a5c336 100644 --- a/contrib/amd/amd/am_ops.c +++ b/contrib/amd/amd/am_ops.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1989 Jan-Simon Pendry * Copyright (c) 1989 Imperial College of Science, Technology & Medicine * Copyright (c) 1989 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: am_ops.c,v 1.6.2.7 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/am_ops.c * */ @@ -87,9 +86,6 @@ static am_ops *vops[] = #ifdef HAVE_AMU_FS_UNION &amfs_union_ops, /* union F/S */ #endif /* HAVE_AMU_FS_UNION */ -#ifdef HAVE_AMU_FS_INHERIT - &amfs_inherit_ops, /* inheritance F/S */ -#endif /* HAVE_AMU_FS_INHERIT */ /* * A few more native filesystems. @@ -126,7 +122,7 @@ static am_ops *vops[] = #endif /* HAVE_FS_UMAPFS */ /* - * These 5 should be last, in the order: + * These 4 should be last, in the order: * (1) amfs_auto * (2) amfs_direct * (3) amfs_toplvl @@ -149,107 +145,111 @@ static am_ops *vops[] = void -ops_showamfstypes(char *buf) +ops_showamfstypes(char *buf, size_t l) { struct am_ops **ap; - int l = 0; + int linesize = 0; buf[0] = '\0'; for (ap = vops; *ap; ap++) { - strcat(buf, (*ap)->fs_type); + xstrlcat(buf, (*ap)->fs_type, l); if (ap[1]) - strcat(buf, ", "); - l += strlen((*ap)->fs_type) + 2; - if (l > 62) { - l = 0; - strcat(buf, "\n "); + xstrlcat(buf, ", ", l); + linesize += strlen((*ap)->fs_type) + 2; + if (linesize > 62) { + linesize = 0; + xstrlcat(buf, "\n ", l); } } } static void -ops_show1(char *buf, int *lp, const char *name) +ops_show1(char *buf, size_t l, int *linesizep, const char *name) { - strcat(buf, name); - strcat(buf, ", "); - *lp += strlen(name) + 2; - if (*lp > 60) { - strcat(buf, "\t\n"); - *lp = 0; + xstrlcat(buf, name, l); + xstrlcat(buf, ", ", l); + *linesizep += strlen(name) + 2; + if (*linesizep > 60) { + xstrlcat(buf, "\t\n", l); + *linesizep = 0; } } void -ops_showfstypes(char *buf) +ops_showfstypes(char *buf, size_t l) { - int l = 0; + int linesize = 0; buf[0] = '\0'; +#ifdef MNTTAB_TYPE_AUTOFS + ops_show1(buf, l, &linesize, MNTTAB_TYPE_AUTOFS); +#endif /* MNTTAB_TYPE_AUTOFS */ + #ifdef MNTTAB_TYPE_CACHEFS - ops_show1(buf, &l, MNTTAB_TYPE_CACHEFS); + ops_show1(buf, l, &linesize, MNTTAB_TYPE_CACHEFS); #endif /* MNTTAB_TYPE_CACHEFS */ #ifdef MNTTAB_TYPE_CDFS - ops_show1(buf, &l, MNTTAB_TYPE_CDFS); + ops_show1(buf, l, &linesize, MNTTAB_TYPE_CDFS); #endif /* MNTTAB_TYPE_CDFS */ #ifdef MNTTAB_TYPE_CFS - ops_show1(buf, &l, MNTTAB_TYPE_CFS); + ops_show1(buf, l, &linesize, MNTTAB_TYPE_CFS); #endif /* MNTTAB_TYPE_CFS */ #ifdef MNTTAB_TYPE_LOFS - ops_show1(buf, &l, MNTTAB_TYPE_LOFS); + ops_show1(buf, l, &linesize, MNTTAB_TYPE_LOFS); #endif /* MNTTAB_TYPE_LOFS */ #ifdef MNTTAB_TYPE_EFS - ops_show1(buf, &l, MNTTAB_TYPE_EFS); + ops_show1(buf, l, &linesize, MNTTAB_TYPE_EFS); #endif /* MNTTAB_TYPE_EFS */ #ifdef MNTTAB_TYPE_MFS - ops_show1(buf, &l, MNTTAB_TYPE_MFS); + ops_show1(buf, l, &linesize, MNTTAB_TYPE_MFS); #endif /* MNTTAB_TYPE_MFS */ #ifdef MNTTAB_TYPE_NFS - ops_show1(buf, &l, MNTTAB_TYPE_NFS); + ops_show1(buf, l, &linesize, MNTTAB_TYPE_NFS); #endif /* MNTTAB_TYPE_NFS */ #ifdef MNTTAB_TYPE_NFS3 - ops_show1(buf, &l, "nfs3"); /* always hard-code as nfs3 */ + ops_show1(buf, l, &linesize, "nfs3"); /* always hard-code as nfs3 */ #endif /* MNTTAB_TYPE_NFS3 */ #ifdef MNTTAB_TYPE_NULLFS - ops_show1(buf, &l, MNTTAB_TYPE_NULLFS); + ops_show1(buf, l, &linesize, MNTTAB_TYPE_NULLFS); #endif /* MNTTAB_TYPE_NULLFS */ #ifdef MNTTAB_TYPE_PCFS - ops_show1(buf, &l, MNTTAB_TYPE_PCFS); + ops_show1(buf, l, &linesize, MNTTAB_TYPE_PCFS); #endif /* MNTTAB_TYPE_PCFS */ #ifdef MNTTAB_TYPE_TFS - ops_show1(buf, &l, MNTTAB_TYPE_TFS); + ops_show1(buf, l, &linesize, MNTTAB_TYPE_TFS); #endif /* MNTTAB_TYPE_TFS */ #ifdef MNTTAB_TYPE_TMPFS - ops_show1(buf, &l, MNTTAB_TYPE_TMPFS); + ops_show1(buf, l, &linesize, MNTTAB_TYPE_TMPFS); #endif /* MNTTAB_TYPE_TMPFS */ #ifdef MNTTAB_TYPE_UFS - ops_show1(buf, &l, MNTTAB_TYPE_UFS); + ops_show1(buf, l, &linesize, MNTTAB_TYPE_UFS); #endif /* MNTTAB_TYPE_UFS */ #ifdef MNTTAB_TYPE_UMAPFS - ops_show1(buf, &l, MNTTAB_TYPE_UMAPFS); + ops_show1(buf, l, &linesize, MNTTAB_TYPE_UMAPFS); #endif /* MNTTAB_TYPE_UMAPFS */ #ifdef MNTTAB_TYPE_UNIONFS - ops_show1(buf, &l, MNTTAB_TYPE_UNIONFS); + ops_show1(buf, l, &linesize, MNTTAB_TYPE_UNIONFS); #endif /* MNTTAB_TYPE_UNIONFS */ #ifdef MNTTAB_TYPE_XFS - ops_show1(buf, &l, MNTTAB_TYPE_XFS); + ops_show1(buf, l, &linesize, MNTTAB_TYPE_XFS); #endif /* MNTTAB_TYPE_XFS */ /* terminate with a period, newline, and NULL */ @@ -257,7 +257,7 @@ ops_showfstypes(char *buf) buf[strlen(buf) - 4] = '\0'; else buf[strlen(buf) - 2] = '\0'; - strcat(buf, ".\n"); + xstrlcat(buf, ".\n", l); } @@ -289,11 +289,11 @@ reverse_option(const char *opt) /* check if string starts with 'no' and chop it */ if (NSTREQ(opt, "no", 2)) { - strcpy(buf, &opt[2]); + xstrlcpy(buf, &opt[2], sizeof(buf)); } else { /* finally return a string prepended with 'no' */ - strcpy(buf, "no"); - strcat(buf, opt); + xstrlcpy(buf, "no", sizeof(buf)); + xstrlcat(buf, opt, sizeof(buf)); } return buf; } @@ -315,7 +315,7 @@ merge_opts(const char *opts1, const char *opts2) char *eq; /* pointer to whatever follows '=' 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 */ + size_t len = strlen(opts1) + strlen(opts2) + 2; /* space for "," and NULL */ char *s1 = strdup(opts1); /* copy of opts1 to munge */ /* initialization */ @@ -327,31 +327,30 @@ merge_opts(const char *opts1, const char *opts2) tmpstr; tmpstr = strtok(NULL, ",")) { /* copy option to temp buffer */ - strncpy(oneopt, tmpstr, 80); - oneopt[79] = '\0'; + xstrlcpy(oneopt, tmpstr, 80); /* if option has a value such as rsize=1024, chop the value part */ if ((eq = haseq(oneopt))) *eq = '\0'; /* find reverse option of oneopt */ revoneopt = reverse_option(oneopt); /* if option orits reverse exist in opts2, ignore it */ - if (hasmntopt(&mnt2, oneopt) || hasmntopt(&mnt2, revoneopt)) + if (amu_hasmntopt(&mnt2, oneopt) || amu_hasmntopt(&mnt2, revoneopt)) continue; /* add option to returned string */ - if (newstr && newstr[0]) { - strcat(newstr, ","); - strcat(newstr, tmpstr); + if (newstr[0]) { + xstrlcat(newstr, ",", len); + xstrlcat(newstr, tmpstr, len); } else { - strcpy(newstr, tmpstr); + xstrlcpy(newstr, tmpstr, len); } } /* finally, append opts2 itself */ - if (newstr && newstr[0]) { - strcat(newstr, ","); - strcat(newstr, opts2); + if (newstr[0]) { + xstrlcat(newstr, ",", len); + xstrlcat(newstr, opts2, len); } else { - strcpy(newstr, opts2); + xstrlcpy(newstr, opts2, len); } XFREE(s1); @@ -360,10 +359,22 @@ merge_opts(const char *opts1, const char *opts2) am_ops * -ops_match(am_opts *fo, char *key, char *g_key, char *path, char *keym, char *map) +ops_search(char *type) { am_ops **vp; am_ops *rop = 0; + for (vp = vops; (rop = *vp); vp++) + if (STREQ(rop->fs_type, type)) + break; + return rop; +} + + +am_ops * +ops_match(am_opts *fo, char *key, char *g_key, char *path, char *keym, char *map) +{ + am_ops *rop = 0; + char *link_dir; /* * First crack the global opts and the local opts @@ -377,9 +388,7 @@ ops_match(am_opts *fo, char *key, char *g_key, char *path, char *keym, char *map /* * Next find the correct filesystem type */ - for (vp = vops; (rop = *vp); vp++) - if (STREQ(rop->fs_type, fo->opt_type)) - break; + rop = ops_search(fo->opt_type); if (!rop) { plog(XLOG_USER, "fs type \"%s\" not recognized", fo->opt_type); rop = &amfs_error_ops; @@ -434,17 +443,33 @@ ops_match(am_opts *fo, char *key, char *g_key, char *path, char *keym, char *map } /* + * Initialize opt_mount_type to "nfs", if it's not initialized already + */ + if (!fo->opt_mount_type) + fo->opt_mount_type = "nfs"; + + /* Normalize the sublink and make it absolute */ + link_dir = fo->opt_sublink; + if (link_dir && link_dir[0] && link_dir[0] != '/') { + link_dir = str3cat((char *) 0, fo->opt_fs, "/", link_dir); + normalize_slash(link_dir); + XFREE(fo->opt_sublink); + fo->opt_sublink = link_dir; + } + + /* * Check the filesystem is happy */ if (fo->fs_mtab) XFREE(fo->fs_mtab); - if ((fo->fs_mtab = (*rop->fs_match) (fo))) + fo->fs_mtab = rop->fs_match(fo); + if (fo->fs_mtab) return rop; /* * Return error file system */ - fo->fs_mtab = (*amfs_error_ops.fs_match) (fo); + 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 index a3967a6..565a550 100644 --- a/contrib/amd/amd/amd.8 +++ b/contrib/amd/amd/amd.8 @@ -1,5 +1,5 @@ .\" -.\" Copyright (c) 1997-2004 Erez Zadok +.\" Copyright (c) 1997-2006 Erez Zadok .\" Copyright (c) 1989 Jan-Simon Pendry .\" Copyright (c) 1989 Imperial College of Science, Technology & Medicine .\" Copyright (c) 1989 The Regents of the University of California. @@ -38,7 +38,7 @@ .\" .\" %W% (Berkeley) %G% .\" -.\" $Id: amd.8,v 1.4.2.5 2004/01/06 03:15:16 ezk Exp $ +.\" $Id: amd.8,v 1.14.2.1 2006/01/02 18:48:23 ezk Exp $ .\" .TH AMD 8 "3 November 1989" .SH NAME @@ -76,6 +76,8 @@ amd \- automatically mount file systems ] [ .BI \-y " YP-domain" ] [ +.BI \-A " arch" +] [ .BI \-C " cluster-name" ] [ .BI \-D " option" @@ -244,6 +246,11 @@ The default is the system domain name. This option is ignored if NIS support is not available. .TP +.BI \-A " arch" +Specifies the OS architecture. This is used solely to set the ${arch} +selector. + +.TP .BI \-C " cluster-name" Specify an alternative HP-UX cluster name to use. @@ -310,14 +317,12 @@ Map entries that are tagged with a tag other than 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 @@ -345,12 +350,21 @@ the features. .BR mtab (5), .BR syslog (3). .LP +``am-utils'' +.BR info (1) +entry. +.LP +.I "Linux NFS and Automounter Administration" +by Erez Zadok, ISBN 0-7821-2739-8, (Sybex, 2001). +.LP +.I http://www.am-utils.org +.LP .I "Amd \- The 4.4 BSD Automounter" .SH AUTHORS Jan-Simon Pendry <jsp@doc.ic.ac.uk>, Department of Computing, Imperial College, London, UK. .P -Erez Zadok <ezk@cs.columbia.edu>, Department of Computer Science, Columbia -University, New York, USA. +Erez Zadok <ezk@cs.sunysb.edu>, Computer Science Department, Stony Brook +University, Stony Brook, New York, USA. .P Other authors and contributors to am-utils are listed in the .B AUTHORS diff --git a/contrib/amd/amd/amd.c b/contrib/amd/amd/amd.c index e2631ae..37eab5d 100644 --- a/contrib/amd/amd/amd.c +++ b/contrib/amd/amd/amd.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1989 Jan-Simon Pendry * Copyright (c) 1989 Imperial College of Science, Technology & Medicine * Copyright (c) 1989 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: amd.c,v 1.8.2.6 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/amd.c * */ @@ -54,9 +53,10 @@ struct amu_global_options gopt; /* where global options are stored */ -char pid_fsname[16 + MAXHOSTNAMELEN]; /* "kiska.southseas.nz:(pid%d)" */ +char pid_fsname[SIZEOF_PID_FSNAME]; /* "kiska.southseas.nz:(pid%d)" */ char *hostdomain = "unknown.domain"; -char hostd[2 * MAXHOSTNAMELEN + 1]; /* Host+domain */ +#define SIZEOF_HOSTD (2 * MAXHOSTNAMELEN + 1) /* Host+domain */ +char hostd[SIZEOF_HOSTD]; /* Host+domain */ char *endian = ARCH_ENDIAN; /* Big or Little endian */ char *cpu = HOST_CPU; /* CPU type */ char *PrimNetName; /* name of primary network */ @@ -71,6 +71,10 @@ struct amd_stats amd_stats; /* Server statistics */ struct in_addr myipaddr; /* (An) IP address of this host */ time_t do_mapc_reload = 0; /* mapc_reload() call required? */ +#ifdef HAVE_FS_AUTOFS +int amd_use_autofs = 0; +#endif /* HAVE_FS_AUTOFS */ + #ifdef HAVE_SIGACTION sigset_t masked_sigs; #endif /* HAVE_SIGACTION */ @@ -118,10 +122,8 @@ sighup(int sig) 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 */ @@ -133,7 +135,26 @@ sighup(int sig) static RETSIGTYPE parent_exit(int sig) { - exit(0); + /* + * This signal handler is called during Amd initialization. The parent + * forks a child to do all the hard automounting work, and waits for a + * SIGQUIT signal from the child. When the parent gets the signal it's + * supposed to call this handler and exit(3), thus completing the + * daemonizing process. Alas, on some systems, especially Linux 2.4/2.6 + * with Glibc, exit(3) doesn't always terminate the parent process. + * Worse, the parent process now refuses to accept any more SIGQUIT + * signals -- they are blocked. What's really annoying is that this + * doesn't happen all the time, suggesting a race condition somewhere. + * (This happens even if I change the logic to use another signal.) I + * traced this to something which exit(3) does in addition to exiting the + * process, probably some atexit() stuff or other side-effects related to + * signal handling. Either way, since at this stage the parent process + * just needs to terminate, I'm simply calling _exit(2). Note also that + * the OpenGroup doesn't list exit(3) as a recommended "Base Interface" + * but they do list _exit(2) as one. This fix seems to work reliably all + * the time. -Erez (2/27/2005) + */ + _exit(0); } @@ -145,6 +166,7 @@ daemon_mode(void) #ifdef HAVE_SIGACTION struct sigaction sa, osa; + memset(&sa, 0, sizeof(sa)); sa.sa_handler = parent_exit; sa.sa_flags = 0; sigemptyset(&(sa.sa_mask)); @@ -217,6 +239,7 @@ init_global_options(void) #if defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME) static struct utsname un; #endif /* defined(HAVE_SYS_UTSNAME_H) && defined(HAVE_UNAME) */ + int i; memset(&gopt, 0, sizeof(struct amu_global_options)); @@ -226,9 +249,15 @@ init_global_options(void) /* automounter temp dir */ gopt.auto_dir = "/a"; + /* toplevel attribute cache timeout */ + gopt.auto_attrcache = 0; + /* cluster name */ gopt.cluster = NULL; + /* executable map timeout */ + gopt.exec_map_timeout = AMFS_EXEC_MAP_TIMEOUT; + /* * kernel architecture: this you must get from uname() if possible. */ @@ -260,11 +289,11 @@ init_global_options(void) /* local domain */ gopt.sub_domain = NULL; - /* NFS retransmit counter */ - gopt.amfs_auto_retrans = -1; - - /* NFS retry interval */ - gopt.amfs_auto_timeo = -1; + /* reset NFS (and toplvl) retransmit counter and retry interval */ + for (i=0; i<AMU_TYPE_MAX; ++i) { + gopt.amfs_auto_retrans[i] = -1; /* -1 means "never set before" */ + gopt.amfs_auto_timeo[i] = -1; /* -1 means "never set before" */ + } /* cache duration */ gopt.am_timeo = AM_TTL; @@ -272,11 +301,13 @@ init_global_options(void) /* dismount interval */ gopt.am_timeo_w = AM_TTL_W; + /* map reload intervl */ + gopt.map_reload_interval = ONE_HOUR; + /* - * various CFM_* flags. - * by default, only the "plock" option is on (if available). + * various CFM_* flags that are on by default. */ - gopt.flags = CFM_PROCESS_LOCK; + gopt.flags = CFM_DEFAULT_FLAGS; #ifdef HAVE_MAP_HESIOD /* Hesiod rhs zone */ @@ -293,6 +324,9 @@ init_global_options(void) /* LDAP cache */ gopt.ldap_cache_seconds = 0; gopt.ldap_cache_maxmem = 131072; + + /* LDAP protocol version */ + gopt.ldap_proto_version = 2; #endif /* HAVE_MAP_LDAP */ #ifdef HAVE_MAP_NIS @@ -302,17 +336,58 @@ init_global_options(void) } +/* + * Lock process text and data segment in memory (after forking the daemon) + */ +static void +do_memory_locking(void) +{ +#if defined(HAVE_PLOCK) || defined(HAVE_MLOCKALL) + int locked_ok = 0; +#else /* not HAVE_PLOCK and not HAVE_MLOCKALL */ + plog(XLOG_WARNING, "Process memory locking not supported by the OS"); +#endif /* not HAVE_PLOCK and not HAVE_MLOCKALL */ +#ifdef HAVE_PLOCK +# ifdef _AIX + /* + * On AIX you must lower the stack size using ulimit() before calling + * plock. Otherwise plock will reserve a lot of memory space based on + * your maximum stack size limit. Since it is not easily possible to + * tell what should the limit be, I print a warning before calling + * plock(). See the manual pages for ulimit(1,3,4) on your AIX system. + */ + plog(XLOG_WARNING, "AIX: may need to lower stack size using ulimit(3) before calling plock"); +# endif /* _AIX */ + if (!locked_ok && plock(PROCLOCK) != 0) + plog(XLOG_WARNING, "Couldn't lock process pages in memory using plock(): %m"); + else + locked_ok = 1; +#endif /* HAVE_PLOCK */ +#ifdef HAVE_MLOCKALL + if (!locked_ok && mlockall(MCL_CURRENT|MCL_FUTURE) != 0) + plog(XLOG_WARNING, "Couldn't lock process pages in memory using mlockall(): %m"); + else + locked_ok = 1; +#endif /* HAVE_MLOCKALL */ +#if defined(HAVE_PLOCK) || defined(HAVE_MLOCKALL) + if (locked_ok) + plog(XLOG_INFO, "Locked process pages in memory"); +#endif /* HAVE_PLOCK || HAVE_MLOCKALL */ + +#if defined(HAVE_MADVISE) && defined(MADV_PROTECT) + madvise(0, 0, MADV_PROTECT); /* may be redundant of the above worked out */ +#endif /* defined(HAVE_MADVISE) && defined(MADV_PROTECT) */ +} + + int main(int argc, char *argv[]) { - char *domdot, *verstr; + char *domdot, *verstr, *vertmp; int ppid = 0; int error; char *progname = NULL; /* "amd" */ char hostname[MAXHOSTNAMELEN + 1] = "localhost"; /* Hostname */ -#ifdef HAVE_SIGACTION - struct sigaction sa; -#endif /* HAVE_SIGACTION */ /* * Make sure some built-in assumptions are true before we start @@ -363,11 +438,6 @@ main(int argc, char *argv[]) going_down(1); } -#ifdef DEBUG - /* initialize debugging flags (Register AMQ, Enter daemon mode) */ - debug_flags = D_AMQ | D_DAEMON; -#endif /* DEBUG */ - /* * Initialize global options structure. */ @@ -386,73 +456,31 @@ main(int argc, char *argv[]) *domdot++ = '\0'; hostdomain = domdot; } - strcpy(hostd, hostname); + xstrlcpy(hostd, hostname, sizeof(hostd)); am_set_hostname(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 + * Setup signal handlers */ -#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 */ - + /* SIGINT: trap interrupts for shutdowns */ + setup_sighandler(SIGINT, sigterm); + /* SIGTERM: trap terminate so we can shutdown cleanly (some chance) */ + setup_sighandler(SIGTERM, sigterm); + /* SIGHUP: hangups tell us to reload the cache */ + setup_sighandler(SIGHUP, sighup); /* - * Trap Death-of-a-child. These allow us to - * pick up the exit status of backgrounded mounts. - * See "sched.c". + * SIGCHLD: trap Death-of-a-child. These allow us to pick up the exit + * status of backgrounded mounts. See "sched.c". */ + setup_sighandler(SIGCHLD, sigchld); #ifdef HAVE_SIGACTION - 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 - */ + /* construct global "masked_sigs" used in nfs_start.c */ sigemptyset(&masked_sigs); + sigaddset(&masked_sigs, SIGINT); + sigaddset(&masked_sigs, SIGTERM); sigaddset(&masked_sigs, SIGHUP); sigaddset(&masked_sigs, SIGCHLD); - sigaddset(&masked_sigs, SIGTERM); - sigaddset(&masked_sigs, SIGINT); -#else /* not HAVE_SIGACTION */ - (void) signal(SIGCHLD, sigchld); -#endif /* not HAVE_SIGACTION */ +#endif /* HAVE_SIGACTION */ /* * Fix-up any umask problems. Most systems default @@ -473,18 +501,21 @@ main(int argc, char *argv[]) /* * Log version information. */ - verstr = strtok(get_version_string(), "\n"); + vertmp = get_version_string(); + verstr = strtok(vertmp, "\n"); plog(XLOG_INFO, "AM-UTILS VERSION INFORMATION:"); while (verstr) { plog(XLOG_INFO, "%s", verstr); verstr = strtok(NULL, "\n"); } + XFREE(vertmp); /* - * Get our own IP address so that we - * can mount the automounter. + * Get our own IP address so that we can mount the automounter. We pass + * localhost_address which could be used as the default localhost + * name/address in amu_get_myaddress(). */ - amu_get_myaddress(&myipaddr); + amu_get_myaddress(&myipaddr, gopt.localhost_address); plog(XLOG_INFO, "My ip addr is %s", inet_ntoa(myipaddr)); /* avoid hanging on other NFS servers if started elsewhere */ @@ -499,29 +530,6 @@ main(int argc, char *argv[]) going_down(1); } - /* - * Lock process text and data segment in memory. - */ -#ifdef HAVE_PLOCK - if (gopt.flags & CFM_PROCESS_LOCK) { -# ifdef _AIX - /* - * On AIX you must lower the stack size using ulimit() before calling - * plock. Otherwise plock will reserve a lot of memory space based on - * your maximum stack size limit. Since it is not easily possible to - * tell what should the limit be, I print a warning before calling - * plock(). See the manual pages for ulimit(1,3,4) on your AIX system. - */ - plog(XLOG_WARNING, "AIX: may need to lower stack size using ulimit(3) before calling plock"); -# endif /* _AIX */ - if (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 @@ -534,14 +542,17 @@ main(int argc, char *argv[]) } #endif /* HAVE_MAP_NIS */ -#ifdef DEBUG - amuDebug(D_DAEMON) -#endif /* DEBUG */ + if (!amuDebug(D_DAEMON)) ppid = daemon_mode(); - sprintf(pid_fsname, "%s:(pid%ld)", am_get_hostname(), (long) am_mypid); + /* + * Lock process text and data segment in memory. + */ + if (gopt.flags & CFM_PROCESS_LOCK) { + do_memory_locking(); + } - do_mapc_reload = clocktime() + ONE_HOUR; + do_mapc_reload = clocktime(NULL) + gopt.map_reload_interval; /* * Register automounter with system. @@ -549,6 +560,16 @@ main(int argc, char *argv[]) error = mount_automounter(ppid); if (error && ppid) kill(ppid, SIGALRM); + +#ifdef HAVE_FS_AUTOFS + /* + * XXX this should be part of going_down(), but I can't move it there + * because it would be calling non-library code from the library... ugh + */ + if (amd_use_autofs) + destroy_autofs_service(); +#endif /* HAVE_FS_AUTOFS */ + going_down(error); abort(); diff --git a/contrib/amd/amd/amd.h b/contrib/amd/amd/amd.h index e5e4e44..0e371b7 100644 --- a/contrib/amd/amd/amd.h +++ b/contrib/amd/amd/amd.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: amd.h,v 1.8.2.8 2004/01/21 04:04:58 ib42 Exp $ + * File: am-utils/amd/amd.h * */ @@ -50,33 +49,160 @@ * MACROS: */ +/* + * Define a default debug mtab path for systems + * that support mtab on file. + */ +#ifdef MOUNT_TABLE_ON_FILE +# define DEBUG_MNTTAB_FILE "/tmp/mnttab" +#endif /* MOUNT_TABLE_ON_FILE */ + /* options for amd.conf */ -#define CFM_BROWSABLE_DIRS 0x0001 -#define CFM_MOUNT_TYPE_AUTOFS 0x0002 -#define CFM_SELECTORS_IN_DEFAULTS 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 */ -#define CFM_NFS_INSECURE_PORT 0x0800 +#define CFM_BROWSABLE_DIRS 0x00000001 +#define CFM_MOUNT_TYPE_AUTOFS 0x00000002 /* use kernel autofs support */ +#define CFM_SELECTORS_IN_DEFAULTS 0x00000004 +#define CFM_NORMALIZE_HOSTNAMES 0x00000008 +#define CFM_PROCESS_LOCK 0x00000010 +#define CFM_PRINT_PID 0x00000020 +#define CFM_RESTART_EXISTING_MOUNTS 0x00000040 +#define CFM_SHOW_STATFS_ENTRIES 0x00000080 +#define CFM_FULLY_QUALIFIED_HOSTS 0x00000100 +#define CFM_BROWSABLE_DIRS_FULL 0x00000200 /* allow '/' in readdir() */ +#define CFM_UNMOUNT_ON_EXIT 0x00000400 /* when amd finishing */ +#define CFM_USE_TCPWRAPPERS 0x00000800 +#define CFM_AUTOFS_USE_LOFS 0x00001000 +#define CFM_NFS_INSECURE_PORT 0x00002000 +#define CFM_DOMAIN_STRIP 0x00004000 +#define CFM_NORMALIZE_SLASHES 0x00008000 /* normalize slashes? */ +#define CFM_FORCED_UNMOUNTS 0x00010000 /* forced unmounts? */ +#define CFM_TRUNCATE_LOG 0x00020000 /* truncate log file? */ +#if 0 +/* XXX: reserved to sync up with am-utils-6.2 */ +#define CFM_SUN_MAP_SYNTAX 0x00040000 /* Sun map syntax? */ +#endif +#define CFM_NFS_ANY_INTERFACE 0x00080000 /* all interfaces are acceptable */ + +/* defaults global flags: plock, tcpwrappers, and autofs/lofs */ +#define CFM_DEFAULT_FLAGS (CFM_PROCESS_LOCK|CFM_USE_TCPWRAPPERS|CFM_AUTOFS_USE_LOFS|CFM_DOMAIN_STRIP|CFM_NORMALIZE_SLASHES) + +/* + * macro definitions for automounter vfs/vnode operations. + */ +#define VLOOK_CREATE 0x1 +#define VLOOK_DELETE 0x2 +#define VLOOK_LOOKUP 0x3 + +/* + * macro definitions for automounter vfs capabilities + */ +#define FS_DIRECTORY 0x0001 /* This looks like a dir, not a link */ +#define FS_MBACKGROUND 0x0002 /* Should background this mount */ +#define FS_NOTIMEOUT 0x0004 /* Don't bother with timeouts */ +#define FS_MKMNT 0x0008 /* Need to make the mount point */ +#define FS_UBACKGROUND 0x0010 /* Unmount in background */ +#define FS_BACKGROUND (FS_MBACKGROUND|FS_UBACKGROUND) +#define FS_DISCARD 0x0020 /* Discard immediately on last reference */ +#define FS_AMQINFO 0x0040 /* Amq is interested in this fs type */ +#define FS_AUTOFS 0x0080 /* This filesystem can be an autofs f/s */ +#define FS_DIRECT 0x0100 /* Direct mount */ +#define FS_ON_AUTOFS 0x0200 /* This filesystem can be mounted directly + onto an autofs mountpoint */ + +/* + * macros for struct am_node (map of auto-mount points). + */ +#define AMF_NOTIMEOUT 0x0001 /* This node never times out */ +#define AMF_ROOT 0x0002 /* This is a root node */ +#define AMF_AUTOFS 0x0004 /* This node is part of an autofs filesystem */ +#define AMF_REMOUNT 0x0008 /* This node needs to be remounted */ +#define AMF_SOFTLOOKUP 0x0010 /* This node returns EIO if server is down */ + +/* + * macros for struct mntfs (list of mounted filesystems) + */ +#define MFF_MOUNTED 0x0001 /* Node is mounted */ +#define MFF_MOUNTING 0x0002 /* Mount is in progress */ +#define MFF_UNMOUNTING 0x0004 /* Unmount is in progress */ +#define MFF_RESTART 0x0008 /* Restarted node */ +#define MFF_MKMNT 0x0010 /* Delete this node's am_mount */ +#define MFF_ERROR 0x0020 /* This node failed to mount */ +#define MFF_LOGDOWN 0x0040 /* Logged that this mount is down */ +#define MFF_RSTKEEP 0x0080 /* Don't timeout this filesystem - restarted */ +#define MFF_WANTTIMO 0x0100 /* Need a timeout call when not busy */ +#define MFF_NFSLINK 0x0200 /* nfsl type, and deemed a link */ +#define MFF_IS_AUTOFS 0x0400 /* this filesystem is of type autofs */ +#define MFF_NFS_SCALEDOWN 0x0800 /* the mount failed, retry with v2/UDP */ +#define MFF_ON_AUTOFS 0x1000 /* autofs has a lofs/link to this f/s */ +#define MFF_WEBNFS 0x2000 /* use public filehandle */ + +/* + * macros for struct fserver. + */ +#define FSF_VALID 0x0001 /* Valid information available */ +#define FSF_DOWN 0x0002 /* This fileserver is thought to be down */ +#define FSF_ERROR 0x0004 /* Permanent error has occurred */ +#define FSF_WANT 0x0008 /* Want a wakeup call */ +#define FSF_PINGING 0x0010 /* Already doing pings */ +#define FSF_WEBNFS 0x0020 /* Don't try to contact portmapper */ +#define FSF_PING_UNINIT 0x0040 /* ping values have not been initilized */ +#define FSF_FORCE_UNMOUNT 0x0080 /* force umount of this fserver */ +#define FSRV_ERROR(fs) ((fs) && (((fs)->fs_flags & FSF_ERROR) == FSF_ERROR)) +#define FSRV_ISDOWN(fs) ((fs) && (((fs)->fs_flags & (FSF_DOWN|FSF_VALID)) == (FSF_DOWN|FSF_VALID))) +#define FSRV_ISUP(fs) (!(fs) || (((fs)->fs_flags & (FSF_DOWN|FSF_VALID)) == (FSF_VALID))) /* some systems (SunOS 4.x) neglect to define the mount null message */ #ifndef MOUNTPROC_NULL # define MOUNTPROC_NULL ((u_long)(0)) #endif /* not MOUNTPROC_NULL */ +/* + * Error to return if remote host is not available. + * Try, in order, "host down", "host unreachable", "invalid argument". + */ +#ifdef EHOSTDOWN +# define AM_ERRNO_HOST_DOWN EHOSTDOWN +#else /* not EHOSTDOWN */ +# ifdef EHOSTUNREACH +# define AM_ERRNO_HOST_DOWN EHOSTUNREACH +# else /* not EHOSTUNREACH */ +# define AM_ERRNO_HOST_DOWN EINVAL +# endif /* not EHOSTUNREACH */ +#endif /* not EHOSTDOWN */ + /* Hash table size */ #define NKVHASH (1 << 2) /* Power of two */ +/* Max entries to return in one call */ +#define MAX_READDIR_ENTRIES 16 + +/* + * default amfs_auto retrans - 1/10th seconds + */ +#define AMFS_AUTO_RETRANS(x) ((ALLOWED_MOUNT_TIME*10+5*gopt.amfs_auto_timeo[(x)])/gopt.amfs_auto_timeo[(x)] * 2) + +/* + * The following values can be tuned... + */ +#define AM_TTL (300) /* Default cache period (5 min) */ +#define AM_TTL_W (120) /* Default unmount interval (2 min) */ +#define AM_PINGER 30 /* NFS ping interval for live systems */ +#define AMFS_AUTO_TIMEO 8 /* Default amfs_auto timeout - .8s */ +#define AMFS_EXEC_MAP_TIMEOUT 10 /* default 10sec exec map timeout */ + /* interval between forced retries of a mount */ #define RETRY_INTERVAL 2 -#define ereturn(x) { *error_return = x; return 0; } +#ifndef ROOT_MAP +# define ROOT_MAP "\"root\"" +#endif /* not ROOT_MAP */ +#define ereturn(x) do { *error_return = x; return 0; } while (0) + +#define NEVER (time_t) 0 + +#if defined(HAVE_TCPD_H) && defined(HAVE_LIBWRAP) +# define AMD_SERVICE_NAME "amd" /* for tcpwrappers */ +#endif /* defined(HAVE_TCPD_H) && defined(HAVE_LIBWRAP) */ /* * TYPEDEFS: @@ -84,6 +210,16 @@ typedef struct cf_map cf_map_t; typedef struct kv kv; +typedef struct am_node am_node; +typedef struct mntfs mntfs; +typedef struct am_opts am_opts; +typedef struct am_ops am_ops; +typedef struct am_stats am_stats; +typedef struct fserver fserver; + +typedef voidp wchan_t; +typedef voidp opaque_t; + /* * Cache map operations */ @@ -93,6 +229,28 @@ typedef int mtime_fn(mnt_map *, char *, time_t *); typedef int isup_fn(mnt_map *, char *); typedef int reload_fn(mnt_map *, char *, add_fn *); typedef int search_fn(mnt_map *, char *, char *, char **, time_t *); +typedef int task_fun(opaque_t); +typedef void cb_fun(int, int, opaque_t); +typedef void fwd_fun(voidp, int, struct sockaddr_in *, + struct sockaddr_in *, opaque_t, int); +typedef int key_fun(char *, opaque_t); +typedef void callout_fun(opaque_t); + +/* + * automounter vfs/vnode operations. + */ +typedef char *(*vfs_match) (am_opts *); +typedef int (*vfs_init) (mntfs *); +typedef int (*vmount_fs) (am_node *, mntfs *); +typedef int (*vumount_fs) (am_node *, mntfs *); +typedef am_node *(*vlookup_child) (am_node *, char *, int *, int); +typedef am_node *(*vmount_child) (am_node *, int *); +typedef int (*vreaddir) (am_node *, nfscookie, nfsdirlist *, nfsentry *, u_int); +typedef am_node *(*vreadlink) (am_node *, int *); +typedef void (*vmounted) (mntfs *); +typedef void (*vumounted) (mntfs *); +typedef fserver *(*vffserver) (mntfs *); +typedef wchan_t (*vget_wchan) (mntfs *); @@ -104,6 +262,7 @@ typedef int search_fn(mnt_map *, char *, char *, char **, time_t *); struct amu_global_options { char *arch; /* name of current architecture */ char *auto_dir; /* automounter temp dir */ + int auto_attrcache; /* attribute cache timeout for auto dirs */ char *cluster; /* cluster name */ char *karch; /* kernel architecture */ char *logfile; /* amd log file */ @@ -113,16 +272,33 @@ struct amu_global_options { char *op_sys_vendor; /* name of OS vendor ${vendor} */ char *pid_file; /* PID file */ char *sub_domain; /* local domain */ + char *localhost_address; /* localhost address (NULL means use 127.0.0.1) */ + char *map_defaults; /* global map /default options */ char *map_options; /* global map options */ + int map_reload_interval; /* map reload interval */ char *map_type; /* global map type */ char *search_path; /* search path for maps */ char *mount_type; /* mount type for map */ + char *debug_mtab_file; /* path for the mtab file during debug mode */ u_int flags; /* various CFM_* flags */ - int amfs_auto_retrans; /* NFS retransmit counter */ - int amfs_auto_timeo; /* NFS retry interval */ + +#define AMU_TYPE_NONE -1 /* for amfs_auto_{timeo,retrans,toplvl} */ +#define AMU_TYPE_UDP 0 /* for amfs_auto_{timeo,retrans,toplvl} */ +#define AMU_TYPE_TCP 1 /* for amfs_auto_{timeo,retrans,toplvl} */ + /* + * Note: toplvl is only UDP, but we want to separate it from regular + * NFS mounts which Amd makes, because the toplvl mount is a localhost + * mount for which different timeo/retrans parameters may be desired. + */ +#define AMU_TYPE_TOPLVL 2 /* for amfs_auto_{timeo,retrans,toplvl} */ +#define AMU_TYPE_MAX 3 /* for amfs_auto_{timeo,retrans,toplvl} */ + int amfs_auto_retrans[AMU_TYPE_MAX]; /* NFS retransmit counter */ + int amfs_auto_timeo[AMU_TYPE_MAX]; /* NFS retry interval */ + int am_timeo; /* cache duration */ int am_timeo_w; /* dismount interval */ - int portmap_program; /* amd RPC program number */ + u_long portmap_program; /* amd RPC program number */ + u_short preferred_amq_port; /* preferred amq service RPC port number (0 means "any") */ #ifdef HAVE_MAP_HESIOD char *hesiod_base; /* Hesiod rhs */ #endif /* HAVE_MAP_HESIOD */ @@ -131,12 +307,14 @@ struct amu_global_options { char *ldap_hostports; /* LDAP host ports */ long ldap_cache_seconds; /* LDAP internal cache - keep seconds */ long ldap_cache_maxmem; /* LDAP internal cache - max memory (bytes) */ + long ldap_proto_version; /* LDAP protocol version */ #endif /* HAVE_MAP_LDAP */ #ifdef HAVE_MAP_NIS char *nis_domain; /* YP domain name */ #endif /* HAVE_MAP_NIS */ char *nfs_proto; /* NFS protocol (NULL, udp, tcp) */ int nfs_vers; /* NFS version (0, 2, 3, 4) */ + u_int exec_map_timeout; /* timeout (seconds) for executable maps */ }; /* if you add anything here, update conf.c:reset_cf_map() */ @@ -144,10 +322,12 @@ struct cf_map { char *cfm_dir; /* /home, /u, /src */ char *cfm_name; /* amd.home, /etc/amd.home ... */ char *cfm_type; /* file, hesiod, ndbm, nis ... */ + char *cfm_defaults; /* map /defaults options in amd.conf */ char *cfm_opts; /* -cache:=all, etc. */ char *cfm_search_path; /* /etc/local:/etc/amdmaps:/misc/yp */ char *cfm_tag; /* optional map tag for amd -T */ u_int cfm_flags; /* browsable_dirs? mount_type? */ + struct cf_map *cfm_next; /* pointer to next in list (if any) */ }; /* @@ -176,52 +356,173 @@ struct mnt_map { 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 */ + cf_map_t *cfm; /* pointer to per-map amd.conf opts, if any */ 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 successful. 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 */ + * Options + */ +struct am_opts { + char *fs_glob; /* Smashed copy of global options */ + char *fs_local; /* Expanded copy of local options */ + char *fs_mtab; /* Mount table entry */ + /* Other options ... */ + char *opt_dev; + char *opt_delay; + char *opt_dir; + char *opt_fs; + char *opt_group; + char *opt_mount; + char *opt_opts; + char *opt_remopts; + char *opt_pref; + char *opt_cache; + char *opt_rfs; + char *opt_rhost; + char *opt_sublink; + char *opt_type; + char *opt_mount_type; /* "nfs" or "autofs" */ + char *opt_unmount; + char *opt_umount; /* an "alias" for opt_unmount (type:=program) */ + char *opt_user; + char *opt_maptype; /* map type: file, nis, hesiod, etc. */ + char *opt_cachedir; /* cache directory */ + char *opt_addopts; /* options to add to opt_opts */ }; +struct am_ops { + char *fs_type; /* type of filesystems e.g. "nfsx" */ + vfs_match fs_match; /* fxn: match */ + vfs_init fs_init; /* fxn: initialization */ + vmount_fs mount_fs; /* fxn: mount my own vnode */ + vumount_fs umount_fs; /* fxn: unmount my own vnode */ + vlookup_child lookup_child; /* fxn: lookup path-name */ + vmount_child mount_child; /* fxn: mount path-name */ + vreaddir readdir; /* fxn: read directory */ + vreadlink readlink; /* fxn: read link */ + vmounted mounted; /* fxn: after-mount extra actions */ + vumounted umounted; /* fxn: after-umount extra actions */ + vffserver ffserver; /* fxn: find a file server */ + vget_wchan get_wchan; /* fxn: get the waiting channel */ + int nfs_fs_flags; /* filesystem flags FS_* for nfs mounts */ +#ifdef HAVE_FS_AUTOFS + int autofs_fs_flags;/* filesystem flags FS_* for autofs mounts */ +#endif /* HAVE_FS_AUTOFS */ +}; + +/* + * List of mounted filesystems + */ +struct mntfs { + qelem mf_q; /* List of mounted filesystems */ + am_ops *mf_ops; /* Operations on this mountpoint */ + am_opts *mf_fo; /* File opts */ + char *mf_mount; /* "/a/kiska/home/kiska" */ + char *mf_info; /* Mount info */ + char *mf_auto; /* Automount opts */ + char *mf_mopts; /* FS mount opts */ + char *mf_remopts; /* Remote FS mount opts */ + char *mf_loopdev; /* loop device name for /dev/loop mounts */ + fserver *mf_server; /* File server */ + int mf_fsflags; /* Flags FS_* copied from mf_ops->*_fs_flags */ + int mf_flags; /* Flags MFF_* */ + int mf_error; /* Error code from background mount */ + int mf_refc; /* Number of references to this node */ + int mf_cid; /* Callout id */ + void (*mf_prfree) (opaque_t); /* Free private space */ + opaque_t mf_private; /* Private - per-fs data */ +}; + +/* + * List of fileservers + */ +struct fserver { + qelem fs_q; /* List of fileservers */ + int fs_refc; /* Number of references to this server */ + char *fs_host; /* Normalized hostname of server */ + struct sockaddr_in *fs_ip; /* Network address of server */ + int fs_cid; /* Callout id */ + int fs_pinger; /* Ping (keepalive) interval */ + int fs_flags; /* Flags */ + char *fs_type; /* File server type */ + u_long fs_version; /* NFS version of server (2, 3, etc.)*/ + char *fs_proto; /* NFS protocol of server (tcp, udp, etc.) */ + opaque_t fs_private; /* Private data */ + void (*fs_prfree) (opaque_t); /* Free private data */ +}; + +/* + * Per-mountpoint statistics + */ +struct am_stats { + time_t s_mtime; /* Mount time */ + u_short s_uid; /* Uid of mounter */ + int s_getattr; /* Count of getattrs */ + int s_lookup; /* Count of lookups */ + int s_readdir; /* Count of readdirs */ + int s_readlink; /* Count of readlinks */ + int s_statfs; /* Count of statfs */ +}; + +/* + * System statistics + */ +struct amd_stats { + int d_drops; /* Dropped requests */ + int d_stale; /* Stale NFS handles */ + int d_mok; /* Successful mounts */ + int d_merr; /* Failed mounts */ + int d_uerr; /* Failed unmounts */ +}; +extern struct amd_stats amd_stats; + +/* + * Map of auto-mount points. + */ +struct am_node { + int am_mapno; /* Map number */ + mntfs *am_mnt; /* Mounted filesystem */ + mntfs **am_mfarray; /* Filesystem sources to try to mount */ + char *am_name; /* "kiska": name of this node */ + char *am_path; /* "/home/kiska": path of this node's mount point */ + char *am_link; /* "/a/kiska/home/kiska/this/that": link to sub-dir */ + am_node *am_parent; /* Parent of this node */ + am_node *am_ysib; /* Younger sibling of this node */ + am_node *am_osib; /* Older sibling of this node */ + am_node *am_child; /* First child of this node */ + nfsattrstat am_attr; /* File attributes */ +#define am_fattr am_attr.ns_u.ns_attr_u + int am_flags; /* Boolean flags AMF_* */ + int am_error; /* Specific mount error */ + time_t am_ttl; /* Time to live */ + int am_timeo_w; /* Dismount wait interval */ + int am_timeo; /* Cache timeout interval */ + u_int am_gen; /* Generation number */ + char *am_pref; /* Mount info prefix */ + am_stats am_stats; /* Statistics gathering */ + SVCXPRT *am_transp; /* Info for quick reply */ + dev_t am_dev; /* Device number */ + dev_t am_rdev; /* Remote/real device number */ +#ifdef HAVE_FS_AUTOFS + autofs_fh_t *am_autofs_fh; + time_t am_autofs_ttl; /* Time to expire autofs nodes */ +#endif /* HAVE_FS_AUTOFS */ +}; /* * EXTERNALS: */ -/* Amq server global functions */ +/* + * Amq server global functions + */ extern amq_mount_info_list *amqproc_getmntfs_1_svc(voidp argp, struct svc_req *rqstp); extern amq_mount_stats *amqproc_stats_1_svc(voidp argp, struct svc_req *rqstp); extern amq_mount_tree_list *amqproc_export_1_svc(voidp argp, struct svc_req *rqstp); extern amq_mount_tree_p *amqproc_mnttree_1_svc(voidp argp, struct svc_req *rqstp); extern amq_string *amqproc_getvers_1_svc(voidp argp, struct svc_req *rqstp); +extern amq_string *amqproc_pawd_1_svc(voidp argp, struct svc_req *rqstp); extern int *amqproc_getpid_1_svc(voidp argp, struct svc_req *rqstp); extern int *amqproc_mount_1_svc(voidp argp, struct svc_req *rqstp); extern int *amqproc_setopt_1_svc(voidp argp, struct svc_req *rqstp); @@ -229,44 +530,134 @@ 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_nfs_fh *get_root_nfs_fh(char *dir); extern am_node *find_ap(char *); -extern am_node *find_ap2(char *, am_node *); +extern am_node *get_ap_child(am_node *, char *); extern bool_t xdr_amq_mount_info_qelem(XDR *xdrs, qelem *qhead); extern fserver *find_nfs_srvr(mntfs *mf); -extern int 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 mount_nfs_fh(am_nfs_handle_t *fhp, char *mntdir, char *fs_name, mntfs *mf); +extern int process_all_regular_maps(void); +extern cf_map_t *find_cf_map(const char *name); extern int set_conf_kv(const char *section, const char *k, const char *v); -extern int try_mount(voidp mvp); +extern int mount_node(opaque_t arg); +extern int unmount_mp(am_node *mp); 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 amfs_mkcacheref(mntfs *mf); +extern int amfs_mount(am_node *mp, mntfs *mf, char *opts); 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 am_node *next_nonerror_node(am_node *xp); +extern void flush_srvr_nfs_cache(fserver *fs); +extern void am_mounted(am_node *); +extern void mf_mounted(mntfs *mf, bool_t call_free_opts); +extern void am_unmounted(am_node *); +extern am_node *get_exported_ap(int index); +extern am_node *get_first_exported_ap(int *index); +extern am_node *get_next_exported_ap(int *index); +extern am_node *path_to_exported_ap(char *path); +extern am_node *exported_ap_alloc(void); +extern am_node *find_mf(mntfs *); +extern am_node *next_map(int *); +extern am_ops *ops_match(am_opts *, char *, char *, char *, char *, char *); +extern am_ops *ops_search(char *); +extern fserver *dup_srvr(fserver *); +extern void srvrlog(fserver *, char *); +extern int get_mountd_port(fserver *, u_short *, wchan_t); +extern void flush_nfs_fhandle_cache(fserver *); + +extern mntfs *dup_mntfs(mntfs *); +extern mntfs *find_mntfs(am_ops *, am_opts *, char *, char *, char *, char *, char *); +extern mntfs *locate_mntfs(am_ops *, am_opts *, char *, char *, char *, char *, char *); +extern mntfs *new_mntfs(void); +extern mntfs *realloc_mntfs(mntfs *, am_ops *, am_opts *, char *, char *, char *, char *, char *); +extern void flush_mntfs(void); +extern void free_mntfs(voidp); + + +extern void amq_program_1(struct svc_req *rqstp, SVCXPRT *transp); +extern int background(void); +extern void deslashify(char *); +extern void do_task_notify(void); +extern int eval_fs_opts(am_opts *, char *, char *, char *, char *, char *); +extern void forcibly_timeout_mp(am_node *); +extern void free_map(am_node *); +extern void free_opts(am_opts *); +extern void free_srvr(fserver *); +extern int fwd_init(void); +extern int fwd_packet(int, char *, int, struct sockaddr_in *, struct sockaddr_in *, opaque_t, fwd_fun *); +extern void fwd_reply(void); +extern void get_args(int argc, char *argv[]); +extern wchan_t get_mntfs_wchan(mntfs *mf); +extern void host_normalize(char **); +extern void init_map(am_node *, char *); +extern void ins_que(qelem *, qelem *); +extern void insert_am(am_node *, am_node *); +extern int make_nfs_auth(void); +extern void make_root_node(void); +extern void map_flush_srvr(fserver *); +extern void mapc_add_kv(mnt_map *, char *, char *); +extern mnt_map *mapc_find(char *, char *, const char *); +extern void mapc_free(opaque_t); +extern int mapc_keyiter(mnt_map *, key_fun, opaque_t); +extern void mapc_reload(void); +extern int mapc_search(mnt_map *, char *, char **); +extern void mapc_showtypes(char *buf, size_t l); +extern int mapc_type_exists(const char *type); +extern void mk_fattr(nfsfattr *, nfsftype); +extern int mount_auto_node(char *, opaque_t); +extern int mount_automounter(int); +extern int mount_exported(void); +extern void mp_to_fh(am_node *, am_nfs_fh *); +extern void new_ttl(am_node *); +extern void nfs_quick_reply(am_node *mp, int error); +extern void normalize_slash(char *); +extern void ops_showamfstypes(char *buf, size_t l); +extern void ops_showfstypes(char *outbuf, size_t l); +extern void rem_que(qelem *); +extern void reschedule_timeout_mp(void); +extern void restart(void); +extern void restart_automounter_nodes(void); +extern int root_keyiter(key_fun *, opaque_t); extern void root_newmap(const char *, const char *, const char *, const cf_map_t *); +extern void run_task(task_fun *, opaque_t, cb_fun *, opaque_t); +extern void sched_task(cb_fun *, opaque_t, wchan_t); +extern int softclock(void); +extern int timeout(u_int, void (*fn)(opaque_t), opaque_t); +extern void timeout_mp(opaque_t); +extern void untimeout(int); +extern void umount_exported(void); +extern int valid_key(char *); +extern void wakeup(wchan_t); +extern void wakeup_srvr(fserver *); +extern void wakeup_task(int, int, wchan_t); +#define SIZEOF_PID_FSNAME (16 + MAXHOSTNAMELEN) +extern char pid_fsname[SIZEOF_PID_FSNAME]; /* "kiska.southseas.nz:(pid%d)" */ +#define SIZEOF_HOSTD (2 * MAXHOSTNAMELEN + 1) +extern char hostd[SIZEOF_HOSTD]; /* Host+domain */ +#define SIZEOF_OPTS 256 /* used for char opts[] and preopts[] */ -/* amd global variables */ +/* + * Global variables. + */ extern FILE *yyin; -extern SVCXPRT *nfs_program_2_transp; /* For quick_reply() */ +extern SVCXPRT *current_transp; /* For nfs_quick_reply() */ extern char *conf_tag; -extern char *opt_gid; -extern char *opt_uid; -extern int NumChild; +#define SIZEOF_UID_STR 12 +#define SIZEOF_GID_STR 12 +extern char *opt_gid, gid_str[SIZEOF_GID_STR]; +extern char *opt_uid, uid_str[SIZEOF_UID_STR]; +extern int NumChildren; extern int fwd_sock; extern int select_intr_valid; +extern int immediate_abort; /* Should close-down unmounts be retried */ extern int usage; extern int use_conf_file; /* use amd configuration file */ +extern int task_notify_todo; /* Task notifier needs running */ extern jmp_buf select_intr; extern qelem mfhead; -extern struct am_opts fs_static; /* copy of the options to play with */ extern struct amu_global_options gopt; /* where global options are stored */ +extern time_t do_mapc_reload; /* Flush & reload mount map cache */ +extern time_t next_softclock; /* Time to call softclock() */ #ifdef HAVE_SIGACTION extern sigset_t masked_sigs; @@ -274,16 +665,88 @@ extern sigset_t masked_sigs; #if defined(HAVE_AMU_FS_LINK) || defined(HAVE_AMU_FS_LINKX) extern char *amfs_link_match(am_opts *fo); -extern int amfs_link_fumount(mntfs *mf); #endif /* defined(HAVE_AMU_FS_LINK) || defined(HAVE_AMU_FS_LINKX) */ -#ifdef HAVE_AMU_FS_NFSL -extern char *nfs_match(am_opts *fo); -#endif /* HAVE_AMU_FS_NFSL */ +#ifdef HAVE_FS_AUTOFS +extern int amd_use_autofs; + +extern int autofs_get_fh(am_node *mp); +extern void autofs_release_fh(am_node *mp); +extern void autofs_get_mp(am_node *mp); +extern void autofs_release_mp(am_node *mp); +extern void autofs_add_fdset(fd_set *readfds); +extern int autofs_handle_fdset(fd_set *readfds, int nsel); +extern void autofs_mounted(am_node *mp); +extern void autofs_mount_succeeded(am_node *mp); +extern void autofs_mount_failed(am_node *mp); +extern int autofs_umount_succeeded(am_node *mp); +extern int autofs_umount_failed(am_node *mp); +extern int autofs_mount_fs(am_node *mp, mntfs *mf); +extern int autofs_umount_fs(am_node *mp, mntfs *mf); +extern void autofs_get_opts(char *opts, size_t l, autofs_fh_t *fh); +extern int autofs_compute_mount_flags(mntent_t *); +extern void autofs_timeout_mp(am_node *); +extern int create_autofs_service(void); +extern int destroy_autofs_service(void); +#endif /* HAVE_FS_AUTOFS */ + +/**************************************************************************/ +/*** Generic file-system types, implemented as part of the native O/S. ***/ +/**************************************************************************/ + +/* + * Loopback File System + * Many systems can't support this, and in any case most of the + * functionality is available with Symlink FS. + */ +#ifdef HAVE_FS_LOFS +extern am_ops lofs_ops; +extern int mount_lofs(char *mntdir, char *fs_name, char *opts, int on_autofs); +#endif /* HAVE_FS_LOFS */ -#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) */ +/* + * CD-ROM File System (CD-ROM) + * (HSFS: High Sierra F/S on some machines) + * Many systems can't support this, and in any case most of the + * functionality is available with program FS. + */ +#ifdef HAVE_FS_CDFS +extern am_ops cdfs_ops; +#endif /* HAVE_FS_CDFS */ + +/* + * PC File System (MS-DOS) + * Many systems can't support this, and in any case most of the + * functionality is available with program FS. + */ +#ifdef HAVE_FS_PCFS +extern am_ops pcfs_ops; +#endif /* HAVE_FS_PCFS */ + +/* + * Caching File System (Solaris) + */ +#ifdef HAVE_FS_CACHEFS +extern am_ops cachefs_ops; +#endif /* HAVE_FS_CACHEFS */ + +/* + * Network File System + * Good, slow, NFS V.2. + */ +#ifdef HAVE_FS_NFS +extern am_ops nfs_ops; /* NFS */ +extern fserver *find_nfs_srvr (mntfs *); +extern qelem nfs_srvr_list; +#endif /* HAVE_FS_NFS */ + +/* + * Un*x File System + * Normal local disk file system. + */ +#ifdef HAVE_FS_UFS +extern am_ops ufs_ops; /* Un*x file system */ +#endif /* HAVE_FS_UFS */ /* Unix file system (irix) */ #ifdef HAVE_FS_XFS @@ -295,4 +758,110 @@ extern am_ops xfs_ops; /* Un*x file system */ extern am_ops efs_ops; /* Un*x file system */ #endif /* HAVE_FS_EFS */ +/**************************************************************************/ +/*** Automounter file-system types, implemented by amd. ***/ +/**************************************************************************/ + +/* + * Root AMD File System + */ +extern am_ops amfs_root_ops; /* Root file system */ + +/* + * Generic amfs helper methods + */ +extern am_node *amfs_generic_lookup_child(am_node *mp, char *fname, int *error_return, int op); +extern am_node *amfs_generic_mount_child(am_node *ap, int *error_return); +extern int amfs_generic_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, u_int count); +extern int amfs_generic_umount(am_node *mp, mntfs *mf); +extern void amfs_generic_mounted(mntfs *mf); +extern char *amfs_generic_match(am_opts *fo); +extern fserver *amfs_generic_find_srvr(mntfs *); + +/* + * Automount File System + */ +#ifdef HAVE_AMU_FS_AUTO +extern am_ops amfs_auto_ops; /* Automount file system (this!) */ +#endif /* HAVE_AMU_FS_AUTO */ + +/* + * Toplvl Automount File System + */ +#ifdef HAVE_AMU_FS_TOPLVL +extern am_ops amfs_toplvl_ops; /* Toplvl Automount file system */ +extern int amfs_toplvl_mount(am_node *mp, mntfs *mf); +extern int amfs_toplvl_umount(am_node *mp, mntfs *mf); +#endif /* HAVE_AMU_FS_TOPLVL */ + +/* + * Direct Automount File System + */ +#ifdef HAVE_AMU_FS_DIRECT +extern am_ops amfs_direct_ops; /* Direct Automount file system (this too) */ +#endif /* HAVE_AMU_FS_DIRECT */ + +/* + * Error File System + */ +#ifdef HAVE_AMU_FS_ERROR +extern am_ops amfs_error_ops; /* Error file system */ +extern am_node *amfs_error_lookup_child(am_node *mp, char *fname, int *error_return, int op); +extern am_node *amfs_error_mount_child(am_node *ap, int *error_return); +extern int amfs_error_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, u_int count); +#endif /* HAVE_AMU_FS_ERROR */ + +/* + * NFS mounts with local existence check. + */ +#ifdef HAVE_AMU_FS_NFSL +extern am_ops amfs_nfsl_ops; /* NFSL */ +#endif /* HAVE_AMU_FS_NFSL */ + +/* + * Multi-nfs mounts. + */ +#ifdef HAVE_AMU_FS_NFSX +extern am_ops amfs_nfsx_ops; /* NFSX */ +#endif /* HAVE_AMU_FS_NFSX */ + +/* + * NFS host - a whole tree. + */ +#ifdef HAVE_AMU_FS_HOST +extern am_ops amfs_host_ops; /* NFS host */ +#endif /* HAVE_AMU_FS_HOST */ + +/* + * Program File System + * This is useful for things like RVD. + */ +#ifdef HAVE_AMU_FS_PROGRAM +extern am_ops amfs_program_ops; /* Program File System */ +#endif /* HAVE_AMU_FS_PROGRAM */ + +/* + * Symbolic-link file system. + * A "filesystem" which is just a symbol link. + */ +#ifdef HAVE_AMU_FS_LINK +extern am_ops amfs_link_ops; /* Symlink FS */ +#endif /* HAVE_AMU_FS_LINK */ + +/* + * Symbolic-link file system, which also checks that the target of + * the symlink exists. + * A "filesystem" which is just a symbol link. + */ +#ifdef HAVE_AMU_FS_LINKX +extern am_ops amfs_linkx_ops; /* Symlink FS with existence check */ +#endif /* HAVE_AMU_FS_LINKX */ + +/* + * Union file system + */ +#ifdef HAVE_AMU_FS_UNION +extern am_ops amfs_union_ops; /* Union FS */ +#endif /* HAVE_AMU_FS_UNION */ + #endif /* not _AMD_H */ diff --git a/contrib/amd/amd/amfs_auto.c b/contrib/amd/amd/amfs_auto.c index ad05393..75a451f 100644 --- a/contrib/amd/amd/amfs_auto.c +++ b/contrib/amd/amd/amfs_auto.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * 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.9.2.12 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/amfs_auto.c * */ @@ -55,9 +54,7 @@ /**************************************************************************** *** MACROS *** ****************************************************************************/ -#define IN_PROGRESS(cp) ((cp)->mp->am_mnt->mf_flags & MFF_MOUNTING) -#define DOT_DOT_COOKIE (u_int) 1 /**************************************************************************** *** STRUCTURES *** @@ -67,10 +64,7 @@ /**************************************************************************** *** 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); +static int amfs_auto_mount(am_node *mp, mntfs *mf); /**************************************************************************** @@ -79,19 +73,22 @@ static void amfs_auto_umounted(am_node *mp); am_ops amfs_auto_ops = { "auto", - amfs_auto_match, + amfs_generic_match, 0, /* amfs_auto_init */ amfs_auto_mount, - 0, - amfs_auto_umount, - 0, - amfs_auto_lookuppn, - amfs_auto_readdir, + amfs_generic_umount, + amfs_generic_lookup_child, + amfs_generic_mount_child, + amfs_generic_readdir, 0, /* amfs_auto_readlink */ - 0, /* amfs_auto_mounted */ - amfs_auto_umounted, - find_amfs_auto_srvr, - FS_AMQINFO | FS_DIRECTORY + amfs_generic_mounted, + 0, /* amfs_auto_umounted */ + amfs_generic_find_srvr, + 0, /* amfs_auto_get_wchan */ + FS_AMQINFO | FS_DIRECTORY, +#ifdef HAVE_FS_AUTOFS + AUTOFS_AUTO_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ }; @@ -99,65 +96,11 @@ am_ops amfs_auto_ops = *** 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) +amfs_auto_mount(am_node *mp, mntfs *mf) { - mntfs *mf = mp->am_mnt; - /* * Pseudo-directories are used to provide some structure * to the automounted directories instead @@ -207,1432 +150,27 @@ amfs_auto_mount(am_node *mp) 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); -} +#ifdef HAVE_FS_AUTOFS + if (mf->mf_flags & MFF_IS_AUTOFS) { + char opts[SIZEOF_OPTS]; + int error; + autofs_get_opts(opts, sizeof(opts), mp->am_autofs_fh); -/* - * 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); + /* now do the mount */ + error = amfs_mount(mp, mf, opts); + if (error) { + errno = error; + plog(XLOG_FATAL, "amfs_auto_mount: amfs_mount failed: %m"); + return error; } - - /* - * 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(%s) failed: %m", mp->am_path); - } -#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 = initialize mount point - fi - if no error on this mount and mount is delayed then - this_error = -1 - fi - if this_error < 0 then - retry = true - fi - if no error on this mount then - make mount point if required - fi - if no error on this mount then - if mount in background then - run mount in background - return -1 - else - this_error = mount in foreground - fi - fi - if an error occurred on this mount then - update stats - save error in mount point - fi -endfor - */ -static int -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 did not match", *cp->ivec, mp->am_path); - if (this_error <= 0) - this_error = ENOENT; - continue; - } else { - if (cp->fs_opts.fs_mtab) { - plog(XLOG_MAP, "Trying mount of %s on %s fstype %s", - cp->fs_opts.fs_mtab, mp->am_path, p->fs_type); - } - cp->tried = TRUE; - } - - this_error = 0; - dont_retry = FALSE; - - if (mp->am_link) { - XFREE(mp->am_link); - mp->am_link = 0; - } - link_dir = mf->mf_fo->opt_sublink; - - if (link_dir && *link_dir) { - if (*link_dir == '/') { - mp->am_link = strdup(link_dir); - } else { - /* - * Try getting fs option from continuation, not mountpoint! - * Don't try logging the string from mf, since it may be bad! - */ - if (cp->fs_opts.opt_fs != mf->mf_fo->opt_fs) - plog(XLOG_ERROR, "use %s instead of 0x%lx", - cp->fs_opts.opt_fs, (unsigned long) mf->mf_fo->opt_fs); - - mp->am_link = str3cat((char *) 0, - cp->fs_opts.opt_fs, "/", link_dir); - - normalize_slash(mp->am_link); - } - } - - if (mf->mf_error > 0) { - this_error = mf->mf_error; - } else if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) { - /* - * Still mounting - retry later - */ -#ifdef DEBUG - dlog("Duplicate pending mount fstype %s", p->fs_type); -#endif /* DEBUG */ - this_error = -1; - } else if (FSRV_ISDOWN(mf->mf_server)) { - /* - * Would just mount from the same place - * as a hung mount - so give up - */ -#ifdef DEBUG - dlog("%s is already hung - giving up", mf->mf_mount); -#endif /* DEBUG */ - mp_error = EWOULDBLOCK; - dont_retry = TRUE; - this_error = -1; - } else if (mf->mf_flags & MFF_MOUNTED) { -#ifdef DEBUG - dlog("duplicate mount of \"%s\" ...", mf->mf_info); -#endif /* DEBUG */ - - /* - * Just call mounted() - */ - am_mounted(mp); - - this_error = 0; - break; - } - - /* - * Will usually need to play around with the mount nodes - * file attribute structure. This must be done here. - * Try and get things initialized, even if the fileserver - * is not known to be up. In the common case this will - * progress things faster. - */ - if (!this_error) { - /* - * Fill in attribute fields. - */ - if (mf->mf_ops->fs_flags & FS_DIRECTORY) - mk_fattr(mp, NFDIR); - else - mk_fattr(mp, NFLNK); - - if (p->fs_init) - this_error = (*p->fs_init) (mf); - } - - /* - * Make sure the fileserver is UP before doing any more work - */ - if (!FSRV_ISUP(mf->mf_server)) { -#ifdef DEBUG - dlog("waiting for server %s to become available", mf->mf_server->fs_host); -#endif /* DEBUG */ - this_error = -1; - } - - if (!this_error && mf->mf_fo->opt_delay) { - /* - * If there is a delay timer on the mount - * then don't try to mount if the timer - * has not expired. - */ - int i = atoi(mf->mf_fo->opt_delay); - if (i > 0 && clocktime() < (cp->start + i)) { -#ifdef DEBUG - dlog("Mount of %s delayed by %lds", mf->mf_mount, (long) (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; -#ifdef DEBUG - dlog("will retry ...\n"); -#endif /* DEBUG */ - break; - } - - 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; - } - - /* actually run the task, backgrounding as necessary */ - 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. - */ -#ifdef DEBUG - dlog("(skipping rewind)\n"); -#endif /* DEBUG */ - /* - * 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); } +#endif /* HAVE_FS_AUTOFS */ /* - * 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 occurred then return it. - */ - if (error) { -#ifdef DEBUG - errno = error; /* XXX */ - dlog("Returning error: %m"); -#endif /* DEBUG */ - XFREE(fname); - ereturn(error); - } - - /* - * If doing a delete then don't create again! - */ - switch (op) { - case VLOOK_DELETE: - ereturn(ENOENT); - - case VLOOK_CREATE: - break; - - default: - plog(XLOG_FATAL, "Unknown op to 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_SELECTORS_IN_DEFAULTS) { - /* - * Pick whichever first entry matched the list of selectors. - * Strip the selectors from the string, and assign to dfl the - * rest of the string. - */ - if (rvec) { - am_opts ap; - am_ops *pt; - char **sp = rvec; - while (*sp) { /* loop until you find something, if any */ - memset((char *) &ap, 0, sizeof(am_opts)); - /* - * This next routine cause many spurious "expansion of ... is" - * messages, which are ignored, b/c all we need out of this - * routine is to match selectors. These spurious messages may - * be wrong, esp. if they try to expand ${key} b/c it will - * get expanded to "/defaults" - */ - pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults", - mp->am_parent->am_mnt->mf_info); - free_opts(&ap); /* don't leak */ - if (pt == &amfs_error_ops) { - plog(XLOG_MAP, "did not match defaults for \"%s\"", *sp); - } else { - dfl = strip_selectors(*sp, "/defaults"); - plog(XLOG_MAP, "matched default selectors \"%s\"", dfl); - break; - } - ++sp; - } - } - } else { /* not 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_SELECTORS_IN_DEFAULTS) && rvec[1]) { -# ifdef DEBUG - dlog("/defaults chopped into %s", dfl); -# endif /* DEBUG */ - plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info); - } - - /* - * Prepend to existing defaults if they exist, - * otherwise just use these defaults. - */ - if (*auto_opts && *dfl) { - char *nopts = (char *) xmalloc(strlen(auto_opts) + strlen(dfl) + 2); - sprintf(nopts, "%s;%s", dfl, auto_opts); - XFREE(auto_opts); - auto_opts = nopts; - } else if (*dfl) { - auto_opts = strealloc(auto_opts, dfl); - } - } - XFREE(dflts); - /* - * Don't need info vector any more - */ - XFREE(rvec); - } - - /* - * Fill it in - */ - init_map(new_mp, fname); - - /* - * 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; -#ifdef DEBUG - nfsentry *ne; - static int j; -#endif /* DEBUG */ - - 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); - } - - /* when gen is 0, we start reading from the beginning of the directory */ - if (gen == 0) { - /* - * In the default instance (which is used to start a search) we return - * "." and "..". - * - * This assumes that the count is big enough to allow both "." and ".." - * to be returned in a single packet. If it isn't (which would be - * fairly unbelievable) then tough. - */ -#ifdef DEBUG - dlog("amfs_auto_readdir: 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 : DOT_DOT_COOKIE); - - if (!xp) - dp->dl_eof = TRUE; /* by default assume readdir done */ - -#ifdef DEBUG - amuDebug(D_READDIR) - for (j=0,ne=ep; ne; ne=ne->ne_nextentry) - plog(XLOG_DEBUG, "gen1 key %4d \"%s\" fi=%d ck=%d", - j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie); -#endif /* DEBUG */ - return 0; - } -#ifdef DEBUG - dlog("amfs_auto_readdir: real child"); -#endif /* DEBUG */ - - if (gen == DOT_DOT_COOKIE) { -#ifdef DEBUG - dlog("amfs_auto_readdir: End of readdir in %s", mp->am_path); -#endif /* DEBUG */ - dp->dl_eof = TRUE; - dp->dl_entries = 0; -#ifdef DEBUG - amuDebug(D_READDIR) - plog(XLOG_DEBUG, "end of readdir eof=TRUE, dl_entries=0\n"); -#endif /* DEBUG */ - return 0; - } - - /* non-browsable directories code */ - xp = mp->am_child; - while (xp && xp->am_gen != gen) - xp = xp->am_osib; - - if (xp) { - int nbytes = count / 2; /* conservative */ - int todo = MAX_READDIR_ENTRIES; - - dp->dl_entries = ep; - do { - am_node *xp_next = next_nonerror_node(xp->am_osib); - - if (xp_next) { - *(u_int *) ep->ne_cookie = xp_next->am_gen; - } else { - *(u_int *) ep->ne_cookie = DOT_DOT_COOKIE; - dp->dl_eof = TRUE; - } - - ep->ne_fileid = xp->am_gen; - ep->ne_name = xp->am_name; - nbytes -= sizeof(*ep) + 1; - if (xp->am_name) - nbytes -= strlen(xp->am_name); - - xp = xp_next; - - if (nbytes > 0 && !dp->dl_eof && todo > 1) { - ep->ne_nextentry = ep + 1; - ep++; - --todo; - } else { - todo = 0; - } - } while (todo > 0); - - ep->ne_nextentry = 0; - -#ifdef DEBUG - amuDebug(D_READDIR) - for (j=0,ne=ep; ne; ne=ne->ne_nextentry) - plog(XLOG_DEBUG, "gen2 key %4d \"%s\" fi=%d ck=%d", - j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie); -#endif /* DEBUG */ - 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 - nfsentry *ne; - static int j; -#endif /* DEBUG */ - - dp->dl_eof = FALSE; /* assume readdir not done */ - -#ifdef DEBUG - amuDebug(D_READDIR) - plog(XLOG_DEBUG, "amfs_auto_readdir_browsable gen=%u, count=%d", - gen, count); -#endif /* DEBUG */ - - 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("amfs_auto_readdir_browsable: 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 = DOT_DOT_COOKIE; - - /* - * If map is browsable, call a function make_entry_chain() to construct - * a linked list of unmounted keys, and return it. Then link the chain - * to the regular list. Get the chain only once, but return - * chunks of it each time. - */ - te = make_entry_chain(mp, dp->dl_entries, fully_browsable); - if (!te) - return 0; -#ifdef DEBUG - amuDebug(D_READDIR) - for (j=0,ne=te; ne; ne=ne->ne_nextentry) - plog(XLOG_DEBUG, "gen1 key %4d \"%s\"", j++, ne->ne_name); -#endif /* DEBUG */ - - /* return only "chain_length" entries */ - te_next = te; - for (i=1; i<chain_length; ++i) { - te_next = te_next->ne_nextentry; - if (!te_next) - break; - } - if (te_next) { - nfsentry *te_saved = te_next->ne_nextentry; - te_next->ne_nextentry = NULL; /* terminate "te" chain */ - te_next = te_saved; /* save rest of "te" for next iteration */ - dp->dl_eof = FALSE; /* tell readdir there's more */ - } else { - dp->dl_eof = TRUE; /* tell readdir that's it */ - } - ep[1].ne_nextentry = te; /* append this chunk of "te" chain */ -#ifdef DEBUG - amuDebug(D_READDIR) { - for (j=0,ne=te; ne; ne=ne->ne_nextentry) - plog(XLOG_DEBUG, "gen2 key %4d \"%s\"", j++, ne->ne_name); - for (j=0,ne=ep; ne; ne=ne->ne_nextentry) - plog(XLOG_DEBUG, "gen2+ key %4d \"%s\" fi=%d ck=%d", - j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie); - plog(XLOG_DEBUG, "EOF is %d", dp->dl_eof); - } -#endif /* DEBUG_READDIR */ - return 0; - } /* end of "if (gen == 0)" statement */ - -#ifdef DEBUG - dlog("amfs_auto_readdir_browsable: real child"); -#endif /* DEBUG */ - - if (gen == DOT_DOT_COOKIE) { -#ifdef DEBUG - dlog("amfs_auto_readdir_browsable: 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. + * Attach a map cache */ - chain_length = count / 128; + amfs_mkcacheref(mf); - /* return only "chain_length" entries */ - for (i=1; i<chain_length; ++i) { - te_next = te_next->ne_nextentry; - if (!te_next) - break; - } - if (te_next) { - nfsentry *te_saved = te_next->ne_nextentry; - te_next->ne_nextentry = NULL; /* terminate "te" chain */ - te_next = te_saved; /* save rest of "te" for next iteration */ - dp->dl_eof = FALSE; /* tell readdir there's more */ - } - ep = te; /* send next chunk of "te" chain */ - dp->dl_entries = ep; -#ifdef DEBUG - amuDebug(D_READDIR) { - plog(XLOG_DEBUG, "dl_entries=0x%lx, te_next=0x%lx, dl_eof=%d", - (long) dp->dl_entries, (long) te_next, dp->dl_eof); - for (ne=te; ne; ne=ne->ne_nextentry) - plog(XLOG_DEBUG, "gen3 key %4d \"%s\"", j++, ne->ne_name); - } -#endif /* DEBUG */ 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 index 2ea0ae6..dbef743 100644 --- a/contrib/amd/amd/amfs_direct.c +++ b/contrib/amd/amd/amfs_direct.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * 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.3.2.4 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/amfs_direct.c * */ @@ -63,19 +62,22 @@ static am_node *amfs_direct_readlink(am_node *mp, int *error_return); am_ops amfs_direct_ops = { "direct", - amfs_auto_match, + amfs_generic_match, 0, /* amfs_direct_init */ amfs_toplvl_mount, - 0, amfs_toplvl_umount, - 0, - amfs_error_lookuppn, + amfs_generic_lookup_child, + amfs_generic_mount_child, 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 + amfs_generic_mounted, + 0, /* amfs_direct_umounted */ + amfs_generic_find_srvr, + 0, /* amfs_direct_get_wchan */ + FS_DIRECT | FS_MKMNT | FS_NOTIMEOUT | FS_BACKGROUND | FS_AMQINFO, +#ifdef HAVE_FS_AUTOFS + AUTOFS_DIRECT_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ }; @@ -92,8 +94,10 @@ amfs_direct_readlink(am_node *mp, int *error_return) 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); + amfs_mkcacheref(mp->am_mnt); /* XXX */ + xp = amfs_generic_lookup_child(mp, mp->am_path + 1, &rc, VLOOK_CREATE); + if (xp && rc < 0) + xp = amfs_generic_mount_child(xp, &rc); } if (xp) { new_ttl(xp); /* (7/12/89) from Rein Tollevik */ diff --git a/contrib/amd/amd/amfs_error.c b/contrib/amd/amd/amfs_error.c index ca62033..51bdaa6 100644 --- a/contrib/amd/amd/amfs_error.c +++ b/contrib/amd/amd/amfs_error.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1989 Jan-Simon Pendry * Copyright (c) 1989 Imperial College of Science, Technology & Medicine * Copyright (c) 1989 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: amfs_error.c,v 1.3.2.5 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/amfs_error.c * */ @@ -57,9 +56,8 @@ #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); +static int amfs_error_mount(am_node *am, mntfs *mf); +static int amfs_error_umount(am_node *am, mntfs *mf); /* @@ -70,17 +68,20 @@ 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_mount, + amfs_error_umount, + amfs_error_lookup_child, + amfs_error_mount_child, amfs_error_readdir, 0, /* amfs_error_readlink */ 0, /* amfs_error_mounted */ - amfs_error_umounted, - find_amfs_auto_srvr, - FS_DISCARD + 0, /* amfs_error_umounted */ + amfs_generic_find_srvr, + 0, /* amfs_error_get_wchan */ + FS_DISCARD, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_ERROR_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ }; @@ -96,14 +97,14 @@ amfs_error_match(am_opts *fo) static int -amfs_error_fmount(mntfs *mf) +amfs_error_mount(am_node *am, mntfs *mf) { return ENOENT; } static int -amfs_error_fumount(mntfs *mf) +amfs_error_umount(am_node *am, mntfs *mf) { /* * Always succeed @@ -118,7 +119,7 @@ amfs_error_fumount(mntfs *mf) * If we do then just give an error. */ am_node * -amfs_error_lookuppn(am_node *mp, char *fname, int *error_return, int op) +amfs_error_lookup_child(am_node *mp, char *fname, int *error_return, int op) { *error_return = ESTALE; return 0; @@ -126,25 +127,25 @@ amfs_error_lookuppn(am_node *mp, char *fname, int *error_return, int op) /* - * EFS interface to RPC readdir() routine. + * EFS interface to RPC lookup() 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) +am_node * +amfs_error_mount_child(am_node *ap, int *error_return) { - return ESTALE; + *error_return = ESTALE; + return 0; } /* - * umounted() callback for EFS. - * - * This prevents core-dumps on callbacks to error file-systems from - * nfsx_fumount. + * EFS interface to RPC readdir() routine. + * Should never get here in the automounter. + * If we do then just give an error. */ -static void -amfs_error_umounted(am_node *mp) +int +amfs_error_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, u_int count) { - /* nothing to do */ + return ESTALE; } diff --git a/contrib/amd/amd/amfs_generic.c b/contrib/amd/amd/amfs_generic.c new file mode 100644 index 0000000..3e7c365 --- /dev/null +++ b/contrib/amd/amd/amfs_generic.c @@ -0,0 +1,1262 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/amfs_generic.c + * + */ + +/* + * generic functions used by amfs filesystems, ripped out of amfs_auto.c. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + + +/**************************************************************************** + *** MACROS *** + ****************************************************************************/ +#define IN_PROGRESS(cp) ((cp)->mp->am_mnt->mf_flags & MFF_MOUNTING) + + +/**************************************************************************** + *** STRUCTURES *** + ****************************************************************************/ +/* + * Mounting a file system may take a significant period of time. The + * problem is that if this is done in the main process thread then the + * entire automounter could be blocked, possibly hanging lots of processes + * on the system. Instead we use a continuation scheme to allow mounts to + * be attempted in a sub-process. When the sub-process exits we pick up the + * exit status (by convention a UN*X error number) and continue in a + * notifier. The notifier gets handed a data structure and can then + * determine whether the mount was successful or not. If not, it updates + * the data structure and tries again until there are no more ways to try + * the mount, or some other permanent error occurs. In the mean time no RPC + * reply is sent, even after the mount is successful. We rely on the RPC + * retry mechanism to resend the lookup request which can then be handled. + */ +struct continuation { + am_node *mp; /* Node we are trying to mount */ + int retry; /* Try again? */ + time_t start; /* Time we started this mount */ + int callout; /* Callout identifier */ + mntfs **mf; /* Current mntfs */ +}; + + +/**************************************************************************** + *** FORWARD DEFINITIONS *** + ****************************************************************************/ +static am_node *amfs_lookup_node(am_node *mp, char *fname, int *error_return); +static mntfs *amfs_lookup_one_mntfs(am_node *new_mp, mntfs *mf, char *ivec, + char *def_opts, char *pfname); +static mntfs **amfs_lookup_mntfs(am_node *new_mp, int *error_return); +static void amfs_cont(int rc, int term, opaque_t arg); +static void amfs_retry(int rc, int term, opaque_t arg); +static void free_continuation(struct continuation *cp); +static int amfs_bgmount(struct continuation *cp); +static char *amfs_parse_defaults(am_node *mp, mntfs *mf, char *def_opts); + + +/**************************************************************************** + *** FUNCTIONS *** + ****************************************************************************/ +static am_node * +amfs_lookup_node(am_node *mp, char *fname, int *error_return) +{ + am_node *new_mp; + int error = 0; /* Error so far */ + int in_progress = 0; /* # of (un)mount in progress */ + mntfs *mf; + char *expanded_fname = 0; + + dlog("in amfs_lookup_node"); + + /* + * If the server is shutting down + * then don't return information + * about the mount point. + */ + if (amd_state == Finishing) { + if (mp->am_mnt == 0 || mp->am_mnt->mf_fsflags & FS_DIRECT) { + dlog("%s mount ignored - going down", fname); + } else { + dlog("%s/%s mount ignored - going down", mp->am_path, fname); + } + ereturn(ENOENT); + } + + /* + * Handle special case of "." and ".." + */ + if (fname[0] == '.') { + if (fname[1] == '\0') + return mp; /* "." is the current node */ + if (fname[1] == '.' && fname[2] == '\0') { + if (mp->am_parent) { + dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path); + return mp->am_parent; /* ".." is the parent node */ + } + ereturn(ESTALE); + } + } + + /* + * Check for valid key name. + * If it is invalid then pretend it doesn't exist. + */ + if (!valid_key(fname)) { + plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname); + ereturn(ENOENT); + } + + /* + * Expand key name. + * expanded_fname is now a private copy. + */ + expanded_fname = expand_selectors(fname); + + /* + * Search children of this node + */ + for (new_mp = mp->am_child; new_mp; new_mp = new_mp->am_osib) { + if (FSTREQ(new_mp->am_name, expanded_fname)) { + if (new_mp->am_error) { + error = new_mp->am_error; + continue; + } + + /* + * If the error code is undefined then it must be + * in progress. + */ + mf = new_mp->am_mnt; + if (mf->mf_error < 0) + goto in_progrss; + + /* + * If there was a previous error with this node + * then return that error code. + */ + if (mf->mf_flags & MFF_ERROR) { + error = mf->mf_error; + continue; + } + if (!(mf->mf_flags & MFF_MOUNTED) || (mf->mf_flags & MFF_UNMOUNTING)) { + in_progrss: + /* + * If the fs is not mounted or it is unmounting then there + * is a background (un)mount in progress. In this case + * we just drop the RPC request (return nil) and + * wait for a retry, by which time the (un)mount may + * have completed. + */ + dlog("ignoring mount of %s in %s -- %smounting in progress, flags %x", + expanded_fname, mf->mf_mount, + (mf->mf_flags & MFF_UNMOUNTING) ? "un" : "", mf->mf_flags); + in_progress++; + if (mf->mf_flags & MFF_UNMOUNTING) { + dlog("will remount later"); + new_mp->am_flags |= AMF_REMOUNT; + } + continue; + } + + /* + * Otherwise we have a hit: return the current mount point. + */ + dlog("matched %s in %s", expanded_fname, new_mp->am_path); + XFREE(expanded_fname); + return new_mp; + } + } + + if (in_progress) { + dlog("Waiting while %d mount(s) in progress", in_progress); + XFREE(expanded_fname); + ereturn(-1); + } + + /* + * If an error occurred then return it. + */ + if (error) { + dlog("Returning error: %s", strerror(error)); + XFREE(expanded_fname); + ereturn(error); + } + + /* + * If the server is going down then just return, + * don't try to mount any more file systems + */ + if ((int) amd_state >= (int) Finishing) { + dlog("not found - server going down anyway"); + ereturn(ENOENT); + } + + /* + * Allocate a new map + */ + new_mp = get_ap_child(mp, expanded_fname); + XFREE(expanded_fname); + if (new_mp == 0) + ereturn(ENOSPC); + + *error_return = -1; + return new_mp; +} + + + +static mntfs * +amfs_lookup_one_mntfs(am_node *new_mp, mntfs *mf, char *ivec, + char *def_opts, char *pfname) +{ + am_ops *p; + am_opts *fs_opts; + mntfs *new_mf; + char *mp_dir = 0; +#ifdef HAVE_FS_AUTOFS + int on_autofs = 1; +#endif /* HAVE_FS_AUTOFS */ + + /* match the operators */ + fs_opts = CALLOC(am_opts); + p = ops_match(fs_opts, ivec, def_opts, new_mp->am_path, + pfname, mf->mf_info); +#ifdef HAVE_FS_AUTOFS + /* XXX: this should be factored out into an autofs-specific function */ + if (new_mp->am_flags & AMF_AUTOFS) { + /* ignore user-provided fs if we're using autofs */ + if (fs_opts->opt_sublink) { + /* + * For sublinks we need to use a hack with autofs: + * mount the filesystem on the original opt_fs (which is NOT an + * autofs mountpoint) and symlink (or lofs-mount) to it from + * the autofs mountpoint. + */ + on_autofs = 0; + mp_dir = fs_opts->opt_fs; + } else { + if (p->autofs_fs_flags & FS_ON_AUTOFS) { + mp_dir = new_mp->am_path; + } else { + mp_dir = fs_opts->opt_fs; + on_autofs = 0; + } + } + } else +#endif /* HAVE_FS_AUTOFS */ + mp_dir = fs_opts->opt_fs; + + /* + * Find or allocate a filesystem for this node. + */ + new_mf = find_mntfs(p, fs_opts, + mp_dir, + fs_opts->fs_mtab, + def_opts, + fs_opts->opt_opts, + fs_opts->opt_remopts); + + /* + * See whether this is a real filesystem + */ + p = new_mf->mf_ops; + if (p == &amfs_error_ops) { + plog(XLOG_MAP, "Map entry %s for %s did not match", ivec, new_mp->am_path); + free_mntfs(new_mf); + return NULL; + } + + dlog("Got a hit with %s", p->fs_type); + +#ifdef HAVE_FS_AUTOFS + if (new_mp->am_flags & AMF_AUTOFS && on_autofs) { + new_mf->mf_flags |= MFF_ON_AUTOFS; + new_mf->mf_fsflags = new_mf->mf_ops->autofs_fs_flags; + } + /* + * A new filesystem is an autofs filesystems if: + * 1. it claims it can be one (has the FS_AUTOFS flag) + * 2. autofs is enabled system-wide + * 3. either has an autofs parent, + * or it is explicitly requested to be autofs. + */ + if (new_mf->mf_ops->autofs_fs_flags & FS_AUTOFS && + amd_use_autofs && + ((mf->mf_flags & MFF_IS_AUTOFS) || + (new_mf->mf_fo && new_mf->mf_fo->opt_mount_type && + STREQ(new_mf->mf_fo->opt_mount_type, "autofs")))) + new_mf->mf_flags |= MFF_IS_AUTOFS; +#endif /* HAVE_FS_AUTOFS */ + + return new_mf; +} + + +static mntfs ** +amfs_lookup_mntfs(am_node *new_mp, int *error_return) +{ + am_node *mp; + char *info; /* Mount info - where to get the file system */ + char **ivecs, **cur_ivec; /* Split version of info */ + int num_ivecs; + char *orig_def_opts; /* Original Automount options */ + char *def_opts; /* Automount options */ + int error = 0; /* Error so far */ + char path_name[MAXPATHLEN]; /* General path name buffer */ + char *pfname; /* Path for database lookup */ + mntfs *mf, **mf_array; + int count; + + dlog("in amfs_lookup_mntfs"); + + mp = new_mp->am_parent; + + /* + * If we get here then this is a reference to an, + * as yet, unknown name so we need to search the mount + * map for it. + */ + if (mp->am_pref) { + if (strlen(mp->am_pref) + strlen(new_mp->am_name) >= sizeof(path_name)) + ereturn(ENAMETOOLONG); + xsnprintf(path_name, sizeof(path_name), "%s%s", mp->am_pref, new_mp->am_name); + pfname = path_name; + } else { + pfname = new_mp->am_name; + } + + mf = mp->am_mnt; + + dlog("will search map info in %s to find %s", mf->mf_info, pfname); + /* + * Consult the oracle for some mount information. + * info is malloc'ed and belongs to this routine. + * It ends up being free'd in free_continuation(). + * + * Note that this may return -1 indicating that information + * is not yet available. + */ + error = mapc_search((mnt_map *) mf->mf_private, pfname, &info); + if (error) { + if (error > 0) + plog(XLOG_MAP, "No map entry for %s", pfname); + else + plog(XLOG_MAP, "Waiting on map entry for %s", pfname); + ereturn(error); + } + dlog("mount info is %s", info); + + /* + * Split info into an argument vector. + * The vector is malloc'ed and belongs to + * this routine. It is free'd further down. + * + * Note: the vector pointers point into info, so don't free it! + */ + ivecs = strsplit(info, ' ', '\"'); + + if (mf->mf_auto) + def_opts = mf->mf_auto; + else + def_opts = ""; + + orig_def_opts = amfs_parse_defaults(mp, mf, strdup(def_opts)); + def_opts = strdup(orig_def_opts); + + /* first build our defaults */ + num_ivecs = 0; + for (cur_ivec = ivecs; *cur_ivec; cur_ivec++) { + if (**cur_ivec == '-') { + /* + * Pick up new defaults + */ + char *new_def_opts = str3cat(NULL, def_opts, ";", *cur_ivec + 1); + XFREE(def_opts); + def_opts = new_def_opts; + dlog("Setting def_opts to \"%s\"", def_opts); + continue; + } else + num_ivecs++; + } + + mf_array = calloc(num_ivecs + 1, sizeof(mntfs *)); + + /* construct the array of struct mntfs for this mount point */ + for (count = 0, cur_ivec = ivecs; *cur_ivec; cur_ivec++) { + mntfs *new_mf; + + if (**cur_ivec == '-') { + XFREE(def_opts); + if ((*cur_ivec)[1] == '\0') { + /* + * If we have a single dash '-' than we need to reset the + * default options. + */ + def_opts = strdup(orig_def_opts); + dlog("Resetting the default options, a single dash '-' was found."); + } else { + /* append options to /default options */ + def_opts = str3cat((char *) 0, orig_def_opts, ";", *cur_ivec + 1); + dlog("Resetting def_opts to \"%s\"", def_opts); + } + continue; + } + + /* + * If a mntfs has already been found, and we find + * a cut then don't try any more locations. + * + * XXX: we do not know when the "/" was added as an equivalent for "||". + * It's undocumented, it might go away at any time. Caveat emptor. + */ + if (STREQ(*cur_ivec, "/") || STREQ(*cur_ivec, "||")) { + if (count > 0) { + dlog("Cut: not trying any more locations for %s", mp->am_path); + break; + } + continue; + } + + new_mf = amfs_lookup_one_mntfs(new_mp, mf, *cur_ivec, def_opts, pfname); + if (new_mf == NULL) + continue; + mf_array[count++] = new_mf; + } + + /* We're done with ivecs */ + XFREE(ivecs); + XFREE(info); + XFREE(orig_def_opts); + XFREE(def_opts); + if (count == 0) { /* no match */ + XFREE(mf_array); + ereturn(ENOENT); + } + + return mf_array; +} + + +/* + * The continuation function. This is called by + * the task notifier when a background mount attempt + * completes. + */ +static void +amfs_cont(int rc, int term, opaque_t arg) +{ + struct continuation *cp = (struct continuation *) arg; + am_node *mp = cp->mp; + mntfs *mf = mp->am_mnt; + + dlog("amfs_cont: '%s'", mp->am_path); + + /* + * Definitely not trying to mount at the moment + */ + mf->mf_flags &= ~MFF_MOUNTING; + + /* + * While we are mounting - try to avoid race conditions + */ + new_ttl(mp); + + /* + * Wakeup anything waiting for this mount + */ + wakeup(get_mntfs_wchan(mf)); + + /* + * Check for termination signal or exit status... + */ + if (rc || term) { +#ifdef HAVE_FS_AUTOFS + if (mf->mf_flags & MFF_IS_AUTOFS && + !(mf->mf_flags & MFF_MOUNTED)) + autofs_release_fh(mp); +#endif /* HAVE_FS_AUTOFS */ + + if (term) { + /* + * Not sure what to do for an error code. + */ + mf->mf_error = EIO; /* XXX ? */ + mf->mf_flags |= MFF_ERROR; + plog(XLOG_ERROR, "mount for %s got signal %d", mp->am_path, term); + } else { + /* + * Check for exit status... + */ +#ifdef __linux__ + /* + * HACK ALERT! + * + * On Linux (and maybe not only) it's possible to run + * an amd which "knows" how to mount certain combinations + * of nfs_proto/nfs_version which the kernel doesn't grok. + * So if we got an EINVAL and we have a server that's not + * using NFSv2/UDP, try again with NFSv2/UDP. + * + * Too bad that there is no way to dynamically determine + * what combinations the _client_ supports, as opposed to + * what the _server_ supports... + */ + if (rc == EINVAL && + mf->mf_server && + (mf->mf_server->fs_version != 2 || + !STREQ(mf->mf_server->fs_proto, "udp"))) + mf->mf_flags |= MFF_NFS_SCALEDOWN; + else +#endif /* __linux__ */ + { + mf->mf_error = rc; + mf->mf_flags |= MFF_ERROR; + errno = rc; /* XXX */ + if (!STREQ(mp->am_mnt->mf_ops->fs_type, "linkx")) + plog(XLOG_ERROR, "%s: mount (amfs_cont): %m", mp->am_path); + } + } + + if (!(mf->mf_flags & MFF_NFS_SCALEDOWN)) { + /* + * If we get here then that attempt didn't work, so + * move the info vector pointer along by one and + * call the background mount routine again + */ + amd_stats.d_merr++; + cp->mf++; + } + amfs_bgmount(cp); + if (mp->am_error > 0) + assign_error_mntfs(mp); + } else { + /* + * The mount worked. + */ + dlog("Mounting %s returned success", cp->mp->am_path); + am_mounted(cp->mp); + free_continuation(cp); + } + + reschedule_timeout_mp(); +} + + +/* + * Retry a mount + */ +static void +amfs_retry(int rc, int term, opaque_t arg) +{ + struct continuation *cp = (struct continuation *) arg; + am_node *mp = cp->mp; + int error = 0; + + dlog("Commencing retry for mount of %s", mp->am_path); + + new_ttl(mp); + + if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime(NULL)) { + /* + * The entire mount has timed out. Set the error code and skip past all + * the mntfs's so that amfs_bgmount will not have any more + * ways to try the mount, thus causing an error. + */ + plog(XLOG_INFO, "mount of \"%s\" has timed out", mp->am_path); + error = ETIMEDOUT; + while (*cp->mf) + cp->mf++; + /* explicitly forbid further retries after timeout */ + cp->retry = FALSE; + } + if (error || !IN_PROGRESS(cp)) + error = amfs_bgmount(cp); + + reschedule_timeout_mp(); +} + + +/* + * Discard an old continuation + */ +static void +free_continuation(struct continuation *cp) +{ + mntfs **mfp; + + dlog("free_continuation"); + if (cp->callout) + untimeout(cp->callout); + /* + * we must free the mntfs's in the list. + * so free all of them if there was an error, + * or free all but the used one, if the mount succeeded. + */ + for (mfp = cp->mp->am_mfarray; *mfp; mfp++) { + free_mntfs(*mfp); + } + XFREE(cp->mp->am_mfarray); + cp->mp->am_mfarray = 0; + XFREE(cp); +} + + +/* + * Pick a file system to try mounting and + * do that in the background if necessary + * +For each location: + discard previous mount location if required + fetch next mount location + if the filesystem failed to be mounted then + this_error = error from filesystem + goto failed + if the filesystem is mounting or unmounting then + goto retry; + if the fileserver is down then + this_error = EIO + continue; + if the filesystem is already mounted + break + fi + + this_error = initialize mount point + + if no error on this mount and mount is delayed then + this_error = -1 + fi + if this_error < 0 then + retry = true + fi + if no error on this mount then + if mount in background then + run mount in background + return -1 + else + this_error = mount in foreground + fi + fi + if an error occurred on this mount then + update stats + save error in mount point + fi +endfor + */ +static int +amfs_bgmount(struct continuation *cp) +{ + am_node *mp = cp->mp; + mntfs *mf; /* Current mntfs */ + int this_error = -1; /* Per-mount error */ + int hard_error = -1; /* Cumulative per-node error */ + + if (mp->am_mnt) + free_mntfs(mp->am_mnt); + + /* + * Try to mount each location. + * At the end: + * hard_error == 0 indicates something was mounted. + * hard_error > 0 indicates everything failed with a hard error + * hard_error < 0 indicates nothing could be mounted now + */ + for (mp->am_mnt = *cp->mf; *cp->mf; cp->mf++, mp->am_mnt = *cp->mf) { + am_ops *p; + + mf = dup_mntfs(mp->am_mnt); + p = mf->mf_ops; + + if (hard_error < 0) + hard_error = this_error; + this_error = 0; + + if (mf->mf_error > 0) { + this_error = mf->mf_error; + goto failed; + } + + if (mf->mf_flags & (MFF_MOUNTING | MFF_UNMOUNTING)) { + /* + * Still mounting - retry later + */ + dlog("mount of \"%s\" already pending", mf->mf_info); + goto retry; + } + + if (FSRV_ISDOWN(mf->mf_server)) { + /* + * Would just mount from the same place + * as a hung mount - so give up + */ + dlog("%s is already hung - giving up", mf->mf_server->fs_host); + this_error = EIO; + goto failed; + } + + if (mp->am_link) { + XFREE(mp->am_link); + mp->am_link = NULL; + } + if (mf->mf_fo && mf->mf_fo->opt_sublink) + mp->am_link = strdup(mf->mf_fo->opt_sublink); + + /* + * Will usually need to play around with the mount nodes + * file attribute structure. This must be done here. + * Try and get things initialized, even if the fileserver + * is not known to be up. In the common case this will + * progress things faster. + */ + + /* + * Fill in attribute fields. + */ + if (mf->mf_fsflags & FS_DIRECTORY) + mk_fattr(&mp->am_fattr, NFDIR); + else + mk_fattr(&mp->am_fattr, NFLNK); + + if (mf->mf_flags & MFF_MOUNTED) { + dlog("duplicate mount of \"%s\" ...", mf->mf_info); + /* + * Skip initial processing of the mountpoint if already mounted. + * This could happen if we have multiple sublinks into the same f/s, + * or if we are restarting an already-mounted filesystem. + */ + goto already_mounted; + } + + if (mf->mf_fo && mf->mf_fo->fs_mtab) { + plog(XLOG_MAP, "Trying mount of %s on %s fstype %s mount_type %s", + mf->mf_fo->fs_mtab, mf->mf_mount, p->fs_type, + mp->am_flags & AMF_AUTOFS ? "autofs" : "non-autofs"); + } + + if (p->fs_init && !(mf->mf_flags & MFF_RESTART)) + this_error = p->fs_init(mf); + + if (this_error > 0) + goto failed; + if (this_error < 0) + goto retry; + + if (mf->mf_fo && mf->mf_fo->opt_delay) { + /* + * If there is a delay timer on the mount + * then don't try to mount if the timer + * has not expired. + */ + int i = atoi(mf->mf_fo->opt_delay); + time_t now = clocktime(NULL); + if (i > 0 && now < (cp->start + i)) { + dlog("Mount of %s delayed by %lds", mf->mf_mount, (long) (i - now + cp->start)); + goto retry; + } + } + + /* + * If the directory is not yet made and it needs to be made, then make it! + */ + if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_fsflags & FS_MKMNT) { + plog(XLOG_INFO, "creating mountpoint directory '%s'", mf->mf_mount); + this_error = mkdirs(mf->mf_mount, 0555); + if (this_error) { + plog(XLOG_ERROR, "mkdirs failed: %s", strerror(this_error)); + goto failed; + } + mf->mf_flags |= MFF_MKMNT; + } + +#ifdef HAVE_FS_AUTOFS + if (mf->mf_flags & MFF_IS_AUTOFS) + if ((this_error = autofs_get_fh(mp))) + goto failed; +#endif /* HAVE_FS_AUTOFS */ + + already_mounted: + mf->mf_flags |= MFF_MOUNTING; + if (mf->mf_fsflags & FS_MBACKGROUND) { + dlog("backgrounding mount of \"%s\"", mf->mf_mount); + if (cp->callout) { + untimeout(cp->callout); + cp->callout = 0; + } + + /* actually run the task, backgrounding as necessary */ + run_task(mount_node, (opaque_t) mp, amfs_cont, (opaque_t) cp); + return -1; + } else { + dlog("foreground mount of \"%s\" ...", mf->mf_mount); + this_error = mount_node((opaque_t) mp); + } + + mf->mf_flags &= ~MFF_MOUNTING; + if (this_error > 0) + goto failed; + if (this_error == 0) { + am_mounted(mp); + break; /* Success */ + } + + retry: + if (!cp->retry) + continue; + dlog("will retry ...\n"); + + /* + * Arrange that amfs_bgmount is called + * after anything else happens. + */ + dlog("Arranging to retry mount of %s", mp->am_path); + sched_task(amfs_retry, (opaque_t) cp, get_mntfs_wchan(mf)); + if (cp->callout) + untimeout(cp->callout); + cp->callout = timeout(RETRY_INTERVAL, wakeup, + (opaque_t) get_mntfs_wchan(mf)); + + mp->am_ttl = clocktime(NULL) + RETRY_INTERVAL; + + /* + * Not done yet - so don't return anything + */ + return -1; + + failed: + amd_stats.d_merr++; + mf->mf_error = this_error; + mf->mf_flags |= MFF_ERROR; +#ifdef HAVE_FS_AUTOFS + if (mp->am_autofs_fh) + autofs_release_fh(mp); +#endif /* HAVE_FS_AUTOFS */ + if (mf->mf_flags & MFF_MKMNT) { + rmdirs(mf->mf_mount); + mf->mf_flags &= ~MFF_MKMNT; + } + /* + * Wakeup anything waiting for this mount + */ + wakeup(get_mntfs_wchan(mf)); + free_mntfs(mf); + /* continue */ + } + + /* + * If we get here, then either the mount succeeded or + * there is no more mount information available. + */ + if (this_error) { + mp->am_mnt = mf = new_mntfs(); + +#ifdef HAVE_FS_AUTOFS + if (mp->am_flags & AMF_AUTOFS) + autofs_mount_failed(mp); + else +#endif /* HAVE_FS_AUTOFS */ + nfs_quick_reply(mp, this_error); + + if (hard_error <= 0) + hard_error = this_error; + if (hard_error < 0) + hard_error = ETIMEDOUT; + + /* + * Set a small(ish) timeout on an error node if + * the error was not a time out. + */ + switch (hard_error) { + case ETIMEDOUT: + case EWOULDBLOCK: + case EIO: + mp->am_timeo = 17; + break; + default: + mp->am_timeo = 5; + break; + } + new_ttl(mp); + } else { + mf = mp->am_mnt; + /* + * Wakeup anything waiting for this mount + */ + wakeup(get_mntfs_wchan(mf)); + hard_error = 0; + } + + /* + * Make sure that the error value in the mntfs has a + * reasonable value. + */ + if (mf->mf_error < 0) { + mf->mf_error = hard_error; + if (hard_error) + mf->mf_flags |= MFF_ERROR; + } + + /* + * In any case we don't need the continuation any more + */ + free_continuation(cp); + + return hard_error; +} + + +static char * +amfs_parse_defaults(am_node *mp, mntfs *mf, char *def_opts) +{ + char *dflts; + char *dfl; + char **rvec = NULL; + struct mnt_map *mm = (mnt_map *) mf->mf_private; + + dlog("determining /defaults entry value"); + + /* + * Find out if amd.conf overrode any map-specific /defaults. + * + * HACK ALERT: there's no easy way to find out what the map mount point is + * at this point, so I am forced to initialize the mnt_map->cfm field here + * for the first time, upon the very first search for a /defaults entry in + * this map. This initialization is much better done in mapc_create(), + * but it's impossible to do that there with the current code structure. + */ + if (mm->cfm == NULL) { /* then initialize it for first time */ + mm->cfm = find_cf_map(mf->mf_mount); + } + if (mm->cfm && mm->cfm->cfm_defaults) { + dlog("map %s map_defaults override: %s", mf->mf_mount, mm->cfm->cfm_defaults); + dflts = strdup(mm->cfm->cfm_defaults); + } else if (mapc_search(mm, "/defaults", &dflts) == 0) { + dlog("/defaults gave %s", dflts); + } else { + return def_opts; /* if nothing found */ + } + + /* trim leading '-' in case thee's one */ + if (*dflts == '-') + dfl = dflts + 1; + else + dfl = dflts; + + /* + * Chop the defaults up + */ + rvec = strsplit(dfl, ' ', '\"'); + + if (gopt.flags & CFM_SELECTORS_IN_DEFAULTS) { + /* + * Pick whichever first entry matched the list of selectors. + * Strip the selectors from the string, and assign to dfl the + * rest of the string. + */ + if (rvec) { + am_opts ap; + am_ops *pt; + char **sp = rvec; + while (*sp) { /* loop until you find something, if any */ + memset((char *) &ap, 0, sizeof(am_opts)); + /* + * This next routine cause many spurious "expansion of ... is" + * messages, which are ignored, b/c all we need out of this + * routine is to match selectors. These spurious messages may + * be wrong, esp. if they try to expand ${key} b/c it will + * get expanded to "/defaults" + */ + pt = ops_match(&ap, *sp, "", mp->am_path, "/defaults", + mp->am_parent->am_mnt->mf_info); + free_opts(&ap); /* don't leak */ + if (pt == &amfs_error_ops) { + plog(XLOG_MAP, "did not match defaults for \"%s\"", *sp); + } else { + dfl = strip_selectors(*sp, "/defaults"); + plog(XLOG_MAP, "matched default selectors \"%s\"", dfl); + break; + } + ++sp; + } + } + } else { /* not selectors_in_defaults */ + /* + * Extract first value + */ + dfl = rvec[0]; + } + + /* + * If there were any values at all... + */ + if (dfl) { + /* + * Log error if there were other values + */ + if (!(gopt.flags & CFM_SELECTORS_IN_DEFAULTS) && rvec[1]) { + dlog("/defaults chopped into %s", dfl); + plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info); + } + + /* + * Prepend to existing defaults if they exist, + * otherwise just use these defaults. + */ + if (*def_opts && *dfl) { + size_t l = strlen(def_opts) + strlen(dfl) + 2; + char *nopts = (char *) xmalloc(l); + xsnprintf(nopts, l, "%s;%s", dfl, def_opts); + XFREE(def_opts); + def_opts = nopts; + } else if (*dfl) { + def_opts = strealloc(def_opts, dfl); + } + } + + XFREE(dflts); + + /* don't need info vector any more */ + if (rvec) + XFREE(rvec); + + return def_opts; +} + + +am_node * +amfs_generic_mount_child(am_node *new_mp, int *error_return) +{ + int error; + struct continuation *cp; /* Continuation structure if need to mount */ + + dlog("in amfs_generic_mount_child"); + + *error_return = error = 0; /* Error so far */ + + /* we have an errorfs attached to the am_node, free it */ + free_mntfs(new_mp->am_mnt); + new_mp->am_mnt = 0; + + /* + * Construct a continuation + */ + cp = ALLOC(struct continuation); + cp->callout = 0; + cp->mp = new_mp; + cp->retry = TRUE; + cp->start = clocktime(NULL); + cp->mf = new_mp->am_mfarray; + + /* + * Try and mount the file system. If this succeeds immediately (possible + * for a ufs file system) then return the attributes, otherwise just + * return an error. + */ + error = amfs_bgmount(cp); + reschedule_timeout_mp(); + if (!error) + return new_mp; + + /* + * Code for quick reply. If current_transp is set, then it's the + * transp that's been passed down from nfs_program_2() or from + * autofs_program_[123](). + * If new_mp->am_transp is not already set, set it by copying in + * current_transp. Once am_transp is set, nfs_quick_reply() and + * autofs_mount_succeeded() can use it to send a reply to the + * client that requested this mount. + */ + if (current_transp && !new_mp->am_transp) { + dlog("Saving RPC transport for %s", new_mp->am_path); + new_mp->am_transp = (SVCXPRT *) xmalloc(sizeof(SVCXPRT)); + *(new_mp->am_transp) = *current_transp; + } + if (error && (new_mp->am_mnt->mf_ops == &amfs_error_ops)) + new_mp->am_error = error; + + if (new_mp->am_error > 0) + assign_error_mntfs(new_mp); + + ereturn(error); +} + + +/* + * Automount interface to RPC lookup routine + * Find the corresponding entry and return + * the file handle for it. + */ +am_node * +amfs_generic_lookup_child(am_node *mp, char *fname, int *error_return, int op) +{ + am_node *new_mp; + mntfs **mf_array; + int mp_error; + + dlog("in amfs_generic_lookup_child"); + + *error_return = 0; + new_mp = amfs_lookup_node(mp, fname, error_return); + + /* return if we got an error */ + if (!new_mp || *error_return > 0) + return new_mp; + + /* also return if it's already mounted and known to be up */ + if (*error_return == 0 && FSRV_ISUP(new_mp->am_mnt->mf_server)) + return new_mp; + + switch (op) { + case VLOOK_DELETE: + /* + * If doing a delete then don't create again! + */ + ereturn(ENOENT); + case VLOOK_LOOKUP: + return new_mp; + } + + /* save error_return */ + mp_error = *error_return; + + mf_array = amfs_lookup_mntfs(new_mp, error_return); + if (!mf_array) { + new_mp->am_error = new_mp->am_mnt->mf_error = *error_return; + free_map(new_mp); + return NULL; + } + + /* + * Already mounted but known to be down: + * check if we have any alternatives to mount + */ + if (mp_error == 0) { + mntfs **mfp; + for (mfp = mf_array; *mfp; mfp++) + if (*mfp != new_mp->am_mnt) + break; + if (*mfp != NULL) { + /* + * we found an alternative, so try mounting again. + */ + *error_return = -1; + } else { + for (mfp = mf_array; *mfp; mfp++) + free_mntfs(*mfp); + XFREE(mf_array); + if (new_mp->am_flags & AMF_SOFTLOOKUP) { + ereturn(EIO); + } else { + *error_return = 0; + return new_mp; + } + } + } + + /* store the array inside the am_node */ + new_mp->am_mfarray = mf_array; + + /* + * Note: while it might seem like a good idea to prioritize + * the list of mntfs's we got here, it probably isn't. + * It would ignore the ordering of entries specified by the user, + * which is counterintuitive and confusing. + */ + return new_mp; +} + + +void +amfs_generic_mounted(mntfs *mf) +{ + amfs_mkcacheref(mf); +} + + +/* + * Unmount an automount sub-node + */ +int +amfs_generic_umount(am_node *mp, mntfs *mf) +{ + int error = 0; + +#ifdef HAVE_FS_AUTOFS + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + if (mf->mf_flags & MFF_IS_AUTOFS) + error = UMOUNT_FS(mp->am_path, mnttab_file_name, unmount_flags); +#endif /* HAVE_FS_AUTOFS */ + + return error; +} + + +char * +amfs_generic_match(am_opts *fo) +{ + char *p; + + if (!fo->opt_rfs) { + plog(XLOG_USER, "amfs_generic_match: no mount point named (rfs:=)"); + return 0; + } + if (!fo->opt_fs) { + plog(XLOG_USER, "amfs_generic_match: no map named (fs:=)"); + return 0; + } + + /* + * Swap round fs:= and rfs:= options + * ... historical (jsp) + */ + p = fo->opt_rfs; + fo->opt_rfs = fo->opt_fs; + fo->opt_fs = p; + + /* + * mtab entry turns out to be the name of the mount map + */ + return strdup(fo->opt_rfs ? fo->opt_rfs : "."); +} diff --git a/contrib/amd/amd/amfs_host.c b/contrib/amd/amd/amfs_host.c index ad0887c..55423c4 100644 --- a/contrib/amd/amd/amfs_host.c +++ b/contrib/amd/amd/amfs_host.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: amfs_host.c,v 1.4.2.7 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/amfs_host.c * */ @@ -57,10 +56,10 @@ #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); +static int amfs_host_mount(am_node *am, mntfs *mf); +static int amfs_host_umount(am_node *am, mntfs *mf); +static void amfs_host_umounted(mntfs *mf); /* * Ops structure @@ -70,17 +69,20 @@ 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_host_mount, + amfs_host_umount, + amfs_error_lookup_child, + amfs_error_mount_child, amfs_error_readdir, 0, /* amfs_host_readlink */ 0, /* amfs_host_mounted */ amfs_host_umounted, find_nfs_srvr, - FS_MKMNT | FS_BACKGROUND | FS_AMQINFO + 0, /* amfs_host_get_wchan */ + FS_MKMNT | FS_BACKGROUND | FS_AMQINFO, +#ifdef HAVE_FS_AUTOFS + AUTOFS_HOST_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ }; @@ -95,21 +97,21 @@ am_ops amfs_host_ops = * allows the entire PC disk to be mounted. */ static void -make_mntpt(char *mntpt, const exports ex, const mntfs *mf) +make_mntpt(char *mntpt, size_t l, const exports ex, const char *mf_mount) { if (ex->ex_dir[0] == '/') { if (ex->ex_dir[1] == 0) - strcpy(mntpt, (mf)->mf_mount); + xstrlcpy(mntpt, mf_mount, l); else - sprintf(mntpt, "%s%s", mf->mf_mount, ex->ex_dir); + xsnprintf(mntpt, l, "%s%s", mf_mount, ex->ex_dir); } else if (ex->ex_dir[0] >= 'a' && ex->ex_dir[0] <= 'z' && ex->ex_dir[1] == ':' && ex->ex_dir[2] == '/' && ex->ex_dir[3] == 0) - sprintf(mntpt, "%s/%c%%", mf->mf_mount, ex->ex_dir[0]); + xsnprintf(mntpt, l, "%s/%c%%", mf_mount, ex->ex_dir[0]); else - sprintf(mntpt, "%s/%s", mf->mf_mount, ex->ex_dir); + xsnprintf(mntpt, l, "%s/%s", mf_mount, ex->ex_dir); } @@ -134,8 +136,7 @@ amfs_host_match(am_opts *fo) static int amfs_host_init(mntfs *mf) { - fserver *fs; - u_short port; + u_short mountd_port; if (strchr(mf->mf_info, ':') == 0) return ENOENT; @@ -152,41 +153,39 @@ amfs_host_init(mntfs *mf) */ /* * 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 + * get_mountd_port with our mntfs passed as the wait channel. + * get_mountd_port will check some things and then schedule * it so that when the fileserver is ready, a wakeup is done - * on this mntfs. amfs_auto_cont() is already sleeping on this mntfs - * so as soon as that wakeup happens amfs_auto_cont() is called and + * on this mntfs. amfs_cont() is already sleeping on this mntfs + * so as soon as that wakeup happens amfs_cont() is called and * this mount is retried. */ - if ((fs = mf->mf_server)) + if (mf->mf_server) /* * We don't really care if there's an error returned. * Since this is just to help speed things along, the * error will get handled properly elsewhere. */ - (void) nfs_srvr_port(fs, &port, (voidp) mf); + get_mountd_port(mf->mf_server, &mountd_port, get_mntfs_wchan(mf)); return 0; } static int -do_mount(am_nfs_handle_t *fhp, char *dir, char *fs_name, char *opts, mntfs *mf) +do_mount(am_nfs_handle_t *fhp, char *mntdir, char *fs_name, mntfs *mf) { struct stat stb; -#ifdef DEBUG - dlog("amfs_host: mounting fs %s on %s\n", fs_name, dir); -#endif /* DEBUG */ + dlog("amfs_host: mounting fs %s on %s\n", fs_name, mntdir); - (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); + (void) mkdirs(mntdir, 0555); + if (stat(mntdir, &stb) < 0 || (stb.st_mode & S_IFMT) != S_IFDIR) { + plog(XLOG_ERROR, "No mount point for %s - skipping", mntdir); return ENOENT; } - return mount_nfs_fh(fhp, dir, fs_name, opts, mf); + return mount_nfs_fh(fhp, mntdir, fs_name, mf); } @@ -208,6 +207,10 @@ fetch_fhandle(CLIENT *client, char *dir, am_nfs_handle_t *fhp, u_long nfs_versio { struct timeval tv; enum clnt_stat clnt_stat; + struct fhstatus res; +#ifdef HAVE_FS_NFS3 + struct am_mountres3 res3; +#endif /* HAVE_FS_NFS3 */ /* * Pick a number, any number... @@ -215,9 +218,7 @@ fetch_fhandle(CLIENT *client, char *dir, am_nfs_handle_t *fhp, u_long nfs_versio 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 @@ -227,25 +228,28 @@ fetch_fhandle(CLIENT *client, char *dir, am_nfs_handle_t *fhp, u_long nfs_versio plog(XLOG_INFO, "fetch_fhandle: NFS version %d", (int) nfs_version); #ifdef HAVE_FS_NFS3 if (nfs_version == NFS_VERSION3) { - memset((char *) &fhp->v3, 0, sizeof(fhp->v3)); + memset((char *) &res3, 0, sizeof(res3)); clnt_stat = clnt_call(client, MOUNTPROC_MNT, (XDRPROC_T_TYPE) xdr_dirpath, (SVC_IN_ARG_TYPE) &dir, - (XDRPROC_T_TYPE) xdr_mountres3, - (SVC_IN_ARG_TYPE) &fhp->v3, + (XDRPROC_T_TYPE) xdr_am_mountres3, + (SVC_IN_ARG_TYPE) &res3, tv); if (clnt_stat != RPC_SUCCESS) { plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat)); return EIO; } /* Check the status of the filehandle */ - if ((errno = fhp->v3.fhs_status)) { -#ifdef DEBUG + if ((errno = res3.fhs_status)) { dlog("fhandle fetch for mount version 3 failed: %m"); -#endif /* DEBUG */ return errno; } + memset((voidp) &fhp->v3, 0, sizeof(am_nfs_fh3)); + fhp->v3.am_fh3_length = res3.mountres3_u.mountinfo.fhandle.fhandle3_len; + memmove(fhp->v3.am_fh3_data, + res3.mountres3_u.mountinfo.fhandle.fhandle3_val, + fhp->v3.am_fh3_length); } else { /* not NFS_VERSION3 mount */ #endif /* HAVE_FS_NFS3 */ clnt_stat = clnt_call(client, @@ -253,20 +257,19 @@ fetch_fhandle(CLIENT *client, char *dir, am_nfs_handle_t *fhp, u_long nfs_versio (XDRPROC_T_TYPE) xdr_dirpath, (SVC_IN_ARG_TYPE) &dir, (XDRPROC_T_TYPE) xdr_fhstatus, - (SVC_IN_ARG_TYPE) &fhp->v2, + (SVC_IN_ARG_TYPE) &res, tv); if (clnt_stat != RPC_SUCCESS) { plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat)); return EIO; } /* Check status of filehandle */ - if (fhp->v2.fhs_status) { - errno = fhp->v2.fhs_status; -#ifdef DEBUG + if (res.fhs_status) { + errno = res.fhs_status; dlog("fhandle fetch for mount version 1 failed: %m"); -#endif /* DEBUG */ return errno; } + memmove(&fhp->v2, &res.fhs_fh, NFS_FHSIZE); #ifdef HAVE_FS_NFS3 } /* end of "if (nfs_version == NFS_VERSION3)" statement */ #endif /* HAVE_FS_NFS3 */ @@ -291,11 +294,8 @@ already_mounted(mntlist *mlist, char *dir) } -/* - * Mount the export tree from a host - */ static int -amfs_host_fmount(mntfs *mf) +amfs_host_mount(am_node *am, mntfs *mf) { struct timeval tv2; CLIENT *client; @@ -317,6 +317,14 @@ amfs_host_fmount(mntfs *mf) u_long mnt_version; /* + * WebNFS servers don't necessarily run mountd. + */ + if (mf->mf_flags & MFF_WEBNFS) { + plog(XLOG_ERROR, "amfs_host_mount: cannot support WebNFS"); + return EIO; + } + + /* * Read the mount list */ mlist = read_mtab(mf->mf_mount, mnttab_file_name); @@ -334,10 +342,10 @@ amfs_host_fmount(mntfs *mf) */ host = mf->mf_server->fs_host; sin = *mf->mf_server->fs_ip; - plog(XLOG_INFO, "amfs_host_fmount: NFS version %d", (int) mf->mf_server->fs_version); + plog(XLOG_INFO, "amfs_host_mount: NFS version %d", (int) mf->mf_server->fs_version); #ifdef HAVE_FS_NFS3 if (mf->mf_server->fs_version == NFS_VERSION3) - mnt_version = MOUNTVERS3; + mnt_version = AM_MOUNTVERS3; else #endif /* HAVE_FS_NFS3 */ mnt_version = MOUNTVERS; @@ -374,9 +382,7 @@ amfs_host_fmount(mntfs *mf) } client->cl_auth = nfs_auth; -#ifdef DEBUG dlog("Fetching export list from %s", host); -#endif /* DEBUG */ /* * Fetch the export list @@ -392,7 +398,7 @@ amfs_host_fmount(mntfs *mf) tv2); if (clnt_stat != RPC_SUCCESS) { const char *msg = clnt_sperrno(clnt_stat); - plog(XLOG_ERROR, "host_fmount rpc failed: %s", msg); + plog(XLOG_ERROR, "host_mount rpc failed: %s", msg); /* clnt_perror(client, "rpc"); */ error = EIO; goto out; @@ -412,7 +418,7 @@ amfs_host_fmount(mntfs *mf) */ ep = (exports *) xmalloc(n_export * sizeof(exports)); for (j = 0, ex = exlist; ex; ex = ex->ex_next) { - make_mntpt(mntpt, ex, mf); + make_mntpt(mntpt, sizeof(mntpt), ex, mf->mf_mount); if (already_mounted(mlist, mntpt)) /* we have at least one mounted f/s, so don't fail the mount */ ok = TRUE; @@ -442,9 +448,7 @@ amfs_host_fmount(mntfs *mf) 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; @@ -461,9 +465,9 @@ amfs_host_fmount(mntfs *mf) * 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)); + xstrlcpy(fs_name, mf->mf_info, MAXPATHLEN); if ((rfs_dir = strchr(fs_name, ':')) == (char *) 0) { - plog(XLOG_FATAL, "amfs_host_fmount: mf_info has no colon"); + plog(XLOG_FATAL, "amfs_host_mount: mf_info has no colon"); error = EINVAL; goto out; } @@ -471,9 +475,15 @@ amfs_host_fmount(mntfs *mf) 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) + /* + * Note: the sizeof space left in rfs_dir is what's left in fs_name + * after strchr() above returned a pointer _inside_ fs_name. The + * calculation below also takes into account that rfs_dir was + * incremented by the ++ above. + */ + xstrlcpy(rfs_dir, ex->ex_dir, sizeof(fs_name) - (rfs_dir - fs_name)); + make_mntpt(mntpt, sizeof(mntpt), ex, mf->mf_mount); + if (do_mount(&fp[j], mntpt, fs_name, mf) == 0) ok = TRUE; } } @@ -522,9 +532,10 @@ directory_prefix(char *pref, char *dir) * Unmount a mount tree */ static int -amfs_host_fumount(mntfs *mf) +amfs_host_umount(am_node *am, mntfs *mf) { mntlist *ml, *mprev; + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; int xerror = 0; /* @@ -559,18 +570,24 @@ amfs_host_fumount(mntfs *mf) 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); + error = UMOUNT_FS(dir, mnttab_file_name, unmount_flags); /* * Keep track of errors */ if (error) { - if (!xerror) + /* + * If we have not already set xerror and error is not ENOENT, + * then set xerror equal to error and log it. + * 'xerror' is the return value for this function. + * + * We do not want to pass ENOENT as an error because if the + * directory does not exists our work is done anyway. + */ + if (!xerror && error != ENOENT) xerror = error; if (error != EBUSY) { errno = error; @@ -591,7 +608,7 @@ amfs_host_fumount(mntfs *mf) * Try to remount, except when we are shutting down. */ if (xerror && amd_state != Finishing) { - xerror = amfs_host_fmount(mf); + xerror = amfs_host_mount(am, mf); if (!xerror) { /* * Don't log this - it's usually too verbose @@ -611,9 +628,8 @@ amfs_host_fumount(mntfs *mf) * mountd protocol is badly broken anyway. */ static void -amfs_host_umounted(am_node *mp) +amfs_host_umounted(mntfs *mf) { - mntfs *mf = mp->am_mnt; char *host; CLIENT *client; enum clnt_stat clnt_stat; @@ -626,6 +642,14 @@ amfs_host_umounted(am_node *mp) return; /* + * WebNFS servers shouldn't ever get here. + */ + if (mf->mf_flags & MFF_WEBNFS) { + plog(XLOG_ERROR, "amfs_host_umounted: cannot support WebNFS"); + return; + } + + /* * Take a copy of the server hostname, address, and NFS version * to mount version conversion. */ @@ -634,7 +658,7 @@ amfs_host_umounted(am_node *mp) plog(XLOG_INFO, "amfs_host_umounted: NFS version %d", (int) mf->mf_server->fs_version); #ifdef HAVE_FS_NFS3 if (mf->mf_server->fs_version == NFS_VERSION3) - mnt_version = MOUNTVERS3; + mnt_version = AM_MOUNTVERS3; else #endif /* HAVE_FS_NFS3 */ mnt_version = MOUNTVERS; @@ -661,9 +685,7 @@ amfs_host_umounted(am_node *mp) } client->cl_auth = nfs_auth; -#ifdef DEBUG dlog("Unmounting all from %s", host); -#endif /* DEBUG */ clnt_stat = clnt_call(client, MOUNTPROC_UMNTALL, diff --git a/contrib/amd/amd/amfs_link.c b/contrib/amd/amd/amfs_link.c index 55552ae..e75ab86 100644 --- a/contrib/amd/amd/amfs_link.c +++ b/contrib/amd/amd/amfs_link.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: amfs_link.c,v 1.3.2.4 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/amfs_link.c * */ @@ -52,6 +51,9 @@ #include <am_defs.h> #include <amd.h> +/* forward declarations */ +static int amfs_link_mount(am_node *mp, mntfs *mf); +static int amfs_link_umount(am_node *mp, mntfs *mf); /* * Ops structures @@ -61,17 +63,20 @@ 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_link_mount, + amfs_link_umount, + amfs_error_lookup_child, + amfs_error_mount_child, amfs_error_readdir, 0, /* amfs_link_readlink */ 0, /* amfs_link_mounted */ 0, /* amfs_link_umounted */ - find_amfs_auto_srvr, - 0 + amfs_generic_find_srvr, + 0, /* nfs_fs_flags */ + 0, /* amfs_link_get_wchan */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_LINK_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ }; @@ -88,54 +93,41 @@ amfs_link_match(am_opts *fo) } /* - * 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 the link target points to another mount point, then we could + * end up with an unpleasant situation, where the link f/s simply + * "assumes" the mntfs of that mount point. * - * 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. + * For example, if the link points to /usr, and /usr is a real ufs + * filesystem, then the link f/s will use the inherited ufs mntfs, + * and the end result will be that it will become unmountable. * - * If sublink is nil then set sublink to fs - * else set sublink to fs / sublink + * To prevent this, we use a hack: we prepend a dot ('.') to opt_fs if + * its original value was an absolute path, so that it will never match + * any other mntfs. * - * Finally set fs to ".". + * XXX: a less hacky solution should be used... */ - 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, ""); + if (fo->opt_fs[0] == '/') { + char *link_hack = str3cat(NULL, ".", fo->opt_fs, ""); + if (!fo->opt_sublink) + fo->opt_sublink = strdup(fo->opt_fs); + XFREE(fo->opt_fs); + fo->opt_fs = link_hack; } return strdup(fo->opt_fs); } -int -amfs_link_fmount(mntfs *mf) +static int +amfs_link_mount(am_node *mp, mntfs *mf) { - /* - * Wow - this is hard to implement! :-) - */ return 0; } -int -amfs_link_fumount(mntfs *mf) +static int +amfs_link_umount(am_node *mp, mntfs *mf) { return 0; } diff --git a/contrib/amd/amd/amfs_linkx.c b/contrib/amd/amd/amfs_linkx.c index c7c9b79..e306eda 100644 --- a/contrib/amd/amd/amfs_linkx.c +++ b/contrib/amd/amd/amfs_linkx.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: amfs_linkx.c,v 1.3.2.4 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/amfs_linkx.c * */ @@ -53,7 +52,8 @@ #include <amd.h> /* forward declarations */ -static int amfs_linkx_mount(am_node *mp); +static int amfs_linkx_mount(am_node *mp, mntfs *mf); +static int amfs_linkx_umount(am_node *mp, mntfs *mf); /* * linkx operations @@ -64,21 +64,24 @@ struct am_ops amfs_linkx_ops = amfs_link_match, 0, /* amfs_linkx_init */ amfs_linkx_mount, - 0, - amfs_auto_fumount, - amfs_link_fumount, - amfs_error_lookuppn, + amfs_linkx_umount, + amfs_error_lookup_child, + amfs_error_mount_child, amfs_error_readdir, 0, /* amfs_linkx_readlink */ 0, /* amfs_linkx_mounted */ 0, /* amfs_linkx_umounted */ - find_amfs_auto_srvr, - FS_MBACKGROUND + amfs_generic_find_srvr, + 0, /* amfs_linkx_get_wchan */ + FS_MBACKGROUND, +#ifdef HAVE_FS_AUTOFS + AUTOFS_LINKX_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ }; static int -amfs_linkx_mount(am_node *mp) +amfs_linkx_mount(am_node *mp, mntfs *mf) { /* * Check for existence of target. @@ -89,7 +92,7 @@ amfs_linkx_mount(am_node *mp) if (mp->am_link) ln = mp->am_link; else /* should never occur */ - ln = mp->am_mnt->mf_mount; + ln = mf->mf_mount; /* * Use lstat, not stat, since we don't @@ -101,3 +104,10 @@ amfs_linkx_mount(am_node *mp) return 0; } + + +static int +amfs_linkx_umount(am_node *mp, mntfs *mf) +{ + return 0; +} diff --git a/contrib/amd/amd/amfs_nfsl.c b/contrib/amd/amd/amfs_nfsl.c index bed6f4d..bb48f0e 100644 --- a/contrib/amd/amd/amfs_nfsl.c +++ b/contrib/amd/amd/amfs_nfsl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: amfs_nfsl.c,v 1.4.2.4 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/amfs_nfsl.c * */ @@ -60,9 +59,9 @@ /* 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 int amfs_nfsl_mount(am_node *mp, mntfs *mf); +static int amfs_nfsl_umount(am_node *mp, mntfs *mf); +static void amfs_nfsl_umounted(mntfs *mf); static fserver *amfs_nfsl_ffserver(mntfs *mf); /* @@ -70,20 +69,23 @@ static fserver *amfs_nfsl_ffserver(mntfs *mf); */ 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 */ + "nfsl", + amfs_nfsl_match, + amfs_nfsl_init, + amfs_nfsl_mount, + amfs_nfsl_umount, + amfs_error_lookup_child, + amfs_error_mount_child, + amfs_error_readdir, + 0, /* amfs_nfsl_readlink */ + 0, /* amfs_nfsl_mounted */ + amfs_nfsl_umounted, + amfs_nfsl_ffserver, + 0, /* amfs_nfsl_get_wchan */ + FS_MKMNT | FS_BACKGROUND | FS_AMQINFO, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_NFSL_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ }; @@ -94,10 +96,16 @@ am_ops amfs_nfsl_ops = static char * amfs_nfsl_match(am_opts *fo) { - char *cp = fo->opt_fs; + char *cp; char *ho = fo->opt_rhost; + char *retval; struct stat stb; + if (fo->opt_sublink) + cp = fo->opt_sublink; + else + cp = fo->opt_fs; + if (!cp || !ho) { plog(XLOG_USER, "amfs_nfsl: host $fs and $rhost must be specified"); return NULL; @@ -105,20 +113,20 @@ amfs_nfsl_match(am_opts *fo) /* * 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. + * call nfs_ops.fs_match(). + * If link value exists (or same host), call amfs_link_ops.fs_match(). */ if (!STRCEQ(ho, am_get_hostname())) { plog(XLOG_INFO, "amfs_nfsl: \"%s\" is not local host, using type:=nfs", ho); - return nfs_match(fo); + retval = nfs_ops.fs_match(fo); } else if (lstat(cp, &stb) < 0) { plog(XLOG_INFO, "amfs_nfsl: \"%s\" does not exist, using type:=nfs", cp); - return nfs_match(fo); + retval = nfs_ops.fs_match(fo); } else { plog(XLOG_INFO, "amfs_nfsl: \"%s\" exists, using type:=link", cp); - return amfs_link_match(fo); + retval = amfs_link_ops.fs_match(fo); } + return retval; } @@ -129,15 +137,15 @@ amfs_nfsl_match(am_opts *fo) 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). - */ + int ret = 0; if (mf->mf_flags & MFF_NFSLINK) { - return 0; + if (amfs_link_ops.fs_init) + ret = amfs_link_ops.fs_init(mf); } else { - return nfs_init(mf); + if (nfs_ops.fs_init) + ret = nfs_ops.fs_init(mf); } + return ret; } @@ -146,17 +154,17 @@ amfs_nfsl_init(mntfs *mf) * Returns: 0 if OK, non-zero (errno) if failed. */ static int -amfs_nfsl_fmount(mntfs *mf) +amfs_nfsl_mount(am_node *mp, mntfs *mf) { - /* - * If a link, do run amfs_link_fmount() (same as type:=link) - * If non-link, do nfs_fmount (same as type:=nfs). - */ + int ret = 0; if (mf->mf_flags & MFF_NFSLINK) { - return amfs_link_fmount(mf); + if (amfs_link_ops.mount_fs) + ret = amfs_link_ops.mount_fs(mp, mf); } else { - return nfs_fmount(mf); + if (nfs_ops.mount_fs) + ret = nfs_ops.mount_fs(mp, mf); } + return ret; } @@ -165,17 +173,17 @@ amfs_nfsl_fmount(mntfs *mf) * Returns: 0 if OK, non-zero (errno) if failed. */ static int -amfs_nfsl_fumount(mntfs *mf) +amfs_nfsl_umount(am_node *mp, mntfs *mf) { - /* - * If a link, do run amfs_link_fumount() (same as type:=link) - * If non-link, do nfs_fumount (same as type:=nfs). - */ + int ret = 0; if (mf->mf_flags & MFF_NFSLINK) { - return amfs_link_fumount(mf); + if (amfs_link_ops.umount_fs) + ret = amfs_link_ops.umount_fs(mp, mf); } else { - return nfs_fumount(mf); + if (nfs_ops.umount_fs) + ret = nfs_ops.umount_fs(mp, mf); } + return ret; } @@ -186,27 +194,14 @@ amfs_nfsl_fumount(mntfs *mf) * See amfs_auto_umounted(), host_umounted(), nfs_umounted(). */ static void -amfs_nfsl_umounted(am_node *mp) +amfs_nfsl_umounted(mntfs *mf) { - 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; + if (amfs_link_ops.umounted) + amfs_link_ops.umounted(mf); } 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; + if (nfs_ops.umounted) + nfs_ops.umounted(mf); } } @@ -218,20 +213,26 @@ amfs_nfsl_umounted(am_node *mp) static fserver * amfs_nfsl_ffserver(mntfs *mf) { - char *cp = mf->mf_fo->opt_fs; + char *cp; char *ho = mf->mf_fo->opt_rhost; struct stat stb; + if (mf->mf_fo->opt_sublink) + cp = mf->mf_fo->opt_sublink; + else + cp = mf->mf_fo->opt_fs; + /* * If this host is not the same as $rhost, or if link does not exist, - * 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. + * call amfs_link_ops.ffserver(). + * If link value exists (or same host), then call ops_nfs.ffserver(). */ if (!STRCEQ(ho, am_get_hostname()) || lstat(cp, &stb) < 0) { - return find_nfs_srvr(mf); + return nfs_ops.ffserver(mf); } else { mf->mf_flags |= MFF_NFSLINK; - return find_amfs_auto_srvr(mf); + /* remove the FS_MKMNT flag, we don't want amd touching the mountpoint */ + mf->mf_fsflags &= ~FS_MKMNT; + return amfs_link_ops.ffserver(mf); } } diff --git a/contrib/amd/amd/amfs_nfsx.c b/contrib/amd/amd/amfs_nfsx.c index 7d8949b..91be8af 100644 --- a/contrib/amd/amd/amfs_nfsx.c +++ b/contrib/amd/amd/amfs_nfsx.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: amfs_nfsx.c,v 1.3.2.5 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/amfs_nfsx.c * */ @@ -67,13 +66,13 @@ struct amfs_nfsx { int nx_c; /* Number of elements in nx_v */ amfs_nfsx_mnt *nx_v; /* Underlying mounts */ amfs_nfsx_mnt *nx_try; + am_node *nx_mp; }; /* forward definitions */ static char *amfs_nfsx_match(am_opts *fo); -static int amfs_nfsx_fmount (mntfs *); -static int amfs_nfsx_fmount(mntfs *mf); -static int amfs_nfsx_fumount(mntfs *mf); +static int amfs_nfsx_mount(am_node *am, mntfs *mf); +static int amfs_nfsx_umount(am_node *am, mntfs *mf); static int amfs_nfsx_init(mntfs *mf); /* @@ -84,17 +83,20 @@ 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_nfsx_mount, + amfs_nfsx_umount, + amfs_error_lookup_child, + amfs_error_mount_child, amfs_error_readdir, 0, /* amfs_nfsx_readlink */ 0, /* amfs_nfsx_mounted */ 0, /* amfs_nfsx_umounted */ find_nfs_srvr, /* XXX */ - /* FS_UBACKGROUND| */ FS_AMQINFO + 0, /* amfs_nfsx_get_wchan */ + /* FS_UBACKGROUND| */ FS_AMQINFO, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_NFSX_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ }; @@ -118,7 +120,7 @@ amfs_nfsx_match(am_opts *fo) /* set default sublink */ if (fo->opt_sublink == 0) { ptr = strchr(fo->opt_rfs, ','); - if (ptr && ptr != (fo->opt_rfs + 1)) + if (ptr && ptr > (fo->opt_rfs + 1)) fo->opt_sublink = strnsave(fo->opt_rfs + 1, ptr - fo->opt_rfs - 1); } @@ -148,17 +150,15 @@ amfs_nfsx_match(am_opts *fo) * Determine magic cookie to put in mtab */ xmtab = str3cat((char *) 0, fo->opt_rhost, ":", fo->opt_rfs); -#ifdef DEBUG dlog("NFSX: 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) +amfs_nfsx_prfree(opaque_t vp) { struct amfs_nfsx *nx = (struct amfs_nfsx *) vp; int i; @@ -201,7 +201,7 @@ amfs_nfsx_init(mntfs *mf) error = EINVAL; goto errexit; } - pref = host +1; + pref = host + 1; host = info; /* @@ -212,14 +212,16 @@ amfs_nfsx_init(mntfs *mf) /* * Count array size */ - for (i = 0; ivec[i]; i++) ; + for (i = 0; ivec[i]; i++) + /* nothing */; nx = ALLOC(struct amfs_nfsx); - mf->mf_private = (voidp) nx; + mf->mf_private = (opaque_t) nx; mf->mf_prfree = amfs_nfsx_prfree; nx->nx_c = i - 1; /* i-1 because we don't want the prefix */ nx->nx_v = (amfs_nfsx_mnt *) xmalloc(nx->nx_c * sizeof(amfs_nfsx_mnt)); + nx->nx_mp = 0; { char *mp = 0; char *xinfo = 0; @@ -243,11 +245,11 @@ amfs_nfsx_init(mntfs *mf) 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); + /* propagate the on_autofs flag */ + nx->nx_v[i].n_mnt->mf_flags |= mf->mf_flags & MFF_ON_AUTOFS; } if (rfs) XFREE(rfs); @@ -274,7 +276,9 @@ amfs_nfsx_init(mntfs *mf) 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); + int error = 0; + if (m->mf_ops->fs_init && !(mf->mf_flags & MFF_RESTART)) + error = m->mf_ops->fs_init(m); /* * if you just "return error" here, you will have made a failure * in any submounts to fail the whole group. There was old unused code @@ -287,7 +291,7 @@ amfs_nfsx_init(mntfs *mf) glob_error = -1; if (!asked_for_wakeup) { asked_for_wakeup = 1; - sched_task(wakeup_task, (voidp) mf, (voidp) m); + sched_task(wakeup_task, (opaque_t) mf, get_mntfs_wchan(m)); } } } @@ -297,10 +301,11 @@ amfs_nfsx_init(mntfs *mf) static void -amfs_nfsx_cont(int rc, int term, voidp closure) +amfs_nfsx_cont(int rc, int term, opaque_t arg) { - mntfs *mf = (mntfs *) closure; + mntfs *mf = (mntfs *) arg; struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private; + am_node *mp = nx->nx_mp; amfs_nfsx_mnt *n = nx->nx_try; n->n_mnt->mf_flags &= ~(MFF_ERROR | MFF_MOUNTING); @@ -309,7 +314,7 @@ amfs_nfsx_cont(int rc, int term, voidp closure) /* * Wakeup anything waiting for this mount */ - wakeup((voidp) n->n_mnt); + wakeup(get_mntfs_wchan(n->n_mnt)); if (rc || term) { if (term) { @@ -334,52 +339,41 @@ amfs_nfsx_cont(int rc, int term, voidp closure) /* * The mount worked. */ - mf_mounted(n->n_mnt); + mf_mounted(n->n_mnt, FALSE); /* FALSE => don't free the n_mnt->am_opts */ n->n_error = 0; } /* * Do the remaining bits */ - if (amfs_nfsx_fmount(mf) >= 0) { - wakeup((voidp) mf); - mf->mf_flags &= ~MFF_MOUNTING; - mf_mounted(mf); - } + if (amfs_nfsx_mount(mp, mf) >= 0) + wakeup(get_mntfs_wchan(mf)); } static int -try_amfs_nfsx_mount(voidp mv) +try_amfs_nfsx_mount(opaque_t mv) { mntfs *mf = (mntfs *) mv; + struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private; + am_node *mp = nx->nx_mp; int error; - mf->mf_flags |= MFF_MOUNTING; - error = (*mf->mf_ops->fmount_fs) (mf); - mf->mf_flags &= ~MFF_MOUNTING; + error = mf->mf_ops->mount_fs(mp, mf); return error; } static int -amfs_nfsx_remount(mntfs *mf, int fg) +amfs_nfsx_remount(am_node *am, mntfs *mf, int fg) { struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private; amfs_nfsx_mnt *n; int glob_error = -1; - 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; - } - } - } + /* Save the am_node pointer for later use */ + nx->nx_mp = am; /* * Iterate through the mntfs's and mount each filesystem @@ -387,40 +381,38 @@ amfs_nfsx_remount(mntfs *mf, int fg) */ for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) { mntfs *m = n->n_mnt; + + if (m->mf_flags & MFF_MOUNTING) + break; + + if (m->mf_flags & MFF_MOUNTED) { + mf_mounted(m, FALSE); /* FALSE => don't free the m->am_opts */ + n->n_error = glob_error = 0; + continue; + } + if (n->n_error < 0) { - /* - * 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); - } + /* Create the mountpoint, if and as required */ + if (!(m->mf_flags & MFF_MKMNT) && m->mf_fsflags & FS_MKMNT) { + if (!mkdirs(m->mf_mount, 0555)) + m->mf_flags |= MFF_MKMNT; } -#ifdef DEBUG - if (n->n_error > 0) { - errno = n->n_error; /* XXX */ - dlog("underlying fmount of %s failed: %m", m->mf_mount); + dlog("calling underlying mount on %s", m->mf_mount); + if (!fg && foreground && (m->mf_fsflags & FS_MBACKGROUND)) { + m->mf_flags |= MFF_MOUNTING; + dlog("backgrounding mount of \"%s\"", m->mf_info); + nx->nx_try = n; + run_task(try_amfs_nfsx_mount, (opaque_t) m, amfs_nfsx_cont, (opaque_t) mf); + n->n_error = -1; + return -1; + } else { + dlog("foreground mount of \"%s\" ...", mf->mf_info); + n->n_error = m->mf_ops->mount_fs(am, m); } -#endif /* DEBUG */ + + if (n->n_error > 0) + dlog("underlying fmount of %s failed: %s", m->mf_mount, strerror(n->n_error)); if (n->n_error == 0) { glob_error = 0; @@ -435,9 +427,9 @@ amfs_nfsx_remount(mntfs *mf, int fg) static int -amfs_nfsx_fmount(mntfs *mf) +amfs_nfsx_mount(am_node *am, mntfs *mf) { - return amfs_nfsx_remount(mf, FALSE); + return amfs_nfsx_remount(am, mf, FALSE); } @@ -447,7 +439,7 @@ amfs_nfsx_fmount(mntfs *mf) * and so may hang under extremely rare conditions. */ static int -amfs_nfsx_fumount(mntfs *mf) +amfs_nfsx_umount(am_node *am, mntfs *mf) { struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private; amfs_nfsx_mnt *n; @@ -469,10 +461,8 @@ amfs_nfsx_fumount(mntfs *mf) * 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); + n->n_error = m->mf_ops->umount_fs(am, m); if (n->n_error) { glob_error = n->n_error; n->n_error = 0; @@ -490,7 +480,7 @@ amfs_nfsx_fumount(mntfs *mf) * whole lot... */ if (glob_error) { - glob_error = amfs_nfsx_remount(mf, TRUE); + glob_error = amfs_nfsx_remount(am, mf, TRUE); if (glob_error) { errno = glob_error; /* XXX */ plog(XLOG_USER, "amfs_nfsx: remount of %s failed: %m", mf->mf_mount); @@ -502,22 +492,12 @@ amfs_nfsx_fumount(mntfs *mf) */ 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 (m->mf_ops->umounted) + m->mf_ops->umounted(m); if (n->n_error < 0) { - if (m->mf_ops->fs_flags & FS_MKMNT) { + if (m->mf_fsflags & FS_MKMNT) { (void) rmdirs(m->mf_mount); m->mf_flags &= ~MFF_MKMNT; } diff --git a/contrib/amd/amd/amfs_program.c b/contrib/amd/amd/amfs_program.c index 795c596..843e805 100644 --- a/contrib/amd/amd/amfs_program.c +++ b/contrib/amd/amd/amfs_program.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1989 Jan-Simon Pendry * Copyright (c) 1989 Imperial College of Science, Technology & Medicine * Copyright (c) 1989 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: amfs_program.c,v 1.6.2.4 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/amfs_program.c * */ @@ -54,8 +53,8 @@ /* 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_mount(am_node *am, mntfs *mf); +static int amfs_program_umount(am_node *am, mntfs *mf); static int amfs_program_init(mntfs *mf); /* @@ -66,17 +65,20 @@ 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_program_mount, + amfs_program_umount, + amfs_error_lookup_child, + amfs_error_mount_child, amfs_error_readdir, 0, /* amfs_program_readlink */ 0, /* amfs_program_mounted */ 0, /* amfs_program_umounted */ - find_amfs_auto_srvr, - FS_BACKGROUND | FS_AMQINFO + amfs_generic_find_srvr, + 0, /* amfs_program_get_wchan */ + FS_MKMNT | FS_BACKGROUND | FS_AMQINFO, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_PROGRAM_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ }; @@ -88,10 +90,19 @@ amfs_program_match(am_opts *fo) { char *prog; - if (!fo->opt_mount || !fo->opt_unmount) { - plog(XLOG_ERROR, "program: both mount and unmount must be specified"); + if (fo->opt_unmount && fo->opt_umount) { + plog(XLOG_ERROR, "program: cannot specify both unmount and umount options"); return 0; } + if (!fo->opt_mount) { + plog(XLOG_ERROR, "program: must specify mount command"); + return 0; + } + if (!fo->opt_unmount && !fo->opt_umount) { + fo->opt_unmount = str3cat(NULL, UNMOUNT_PROGRAM, " umount ", fo->opt_fs); + plog(XLOG_INFO, "program: un/umount not specified; using default \"%s\"", + fo->opt_unmount); + } prog = strchr(fo->opt_mount, ' '); return strdup(prog ? prog + 1 : fo->opt_mount); @@ -101,13 +112,16 @@ amfs_program_match(am_opts *fo) 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; - } + /* check if already saved value */ + if (mf->mf_private != NULL) + return 0; + + /* save unmount (or umount) command */ + if (mf->mf_fo->opt_unmount != NULL) + mf->mf_private = (opaque_t) strdup(mf->mf_fo->opt_unmount); + else + mf->mf_private = (opaque_t) strdup(mf->mf_fo->opt_umount); + mf->mf_prfree = (void (*)(opaque_t)) free; return 0; } @@ -142,8 +156,7 @@ amfs_program_exec(char *info) /* * Try the exec */ -#ifdef DEBUG - amuDebug(D_FULL) { + if (amuDebug(D_FULL)) { char **cp = xivec; plog(XLOG_DEBUG, "executing (un)mount command..."); while (*cp) { @@ -151,7 +164,6 @@ amfs_program_exec(char *info) cp++; } } -#endif /* DEBUG */ if (xivec[0] == 0 || xivec[1] == 0) { errno = EINVAL; @@ -180,14 +192,14 @@ amfs_program_exec(char *info) static int -amfs_program_fmount(mntfs *mf) +amfs_program_mount(am_node *am, mntfs *mf) { return amfs_program_exec(mf->mf_fo->opt_mount); } static int -amfs_program_fumount(mntfs *mf) +amfs_program_umount(am_node *am, 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 index 9d121bb..243267e 100644 --- a/contrib/amd/amd/amfs_root.c +++ b/contrib/amd/amd/amfs_root.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * 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.3.2.4 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/amfs_root.c * */ @@ -55,7 +54,7 @@ /**************************************************************************** *** FORWARD DEFINITIONS *** ****************************************************************************/ -static int amfs_root_mount(am_node *mp); +static int amfs_root_mount(am_node *mp, mntfs *mf); /**************************************************************************** *** OPS STRUCTURES *** @@ -66,16 +65,19 @@ am_ops amfs_root_ops = 0, /* amfs_root_match */ 0, /* amfs_root_init */ amfs_root_mount, - 0, - amfs_auto_umount, - 0, - amfs_auto_lookuppn, - amfs_auto_readdir, + amfs_generic_umount, + amfs_generic_lookup_child, + amfs_generic_mount_child, + amfs_generic_readdir, 0, /* amfs_root_readlink */ 0, /* amfs_root_mounted */ 0, /* amfs_root_umounted */ - find_amfs_auto_srvr, - FS_NOTIMEOUT | FS_AMQINFO | FS_DIRECTORY + amfs_generic_find_srvr, + 0, /* amfs_root_get_wchan */ + FS_NOTIMEOUT | FS_AMQINFO | FS_DIRECTORY, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_ROOT_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ }; @@ -87,12 +89,10 @@ am_ops amfs_root_ops = * Mount the root... */ static int -amfs_root_mount(am_node *mp) +amfs_root_mount(am_node *mp, mntfs *mf) { - 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_private = (opaque_t) 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 index c0e7604..1b968f1 100644 --- a/contrib/amd/amd/amfs_toplvl.c +++ b/contrib/amd/amd/amfs_toplvl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * 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.7.2.5 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/amfs_toplvl.c * */ @@ -55,7 +54,7 @@ /**************************************************************************** *** FORWARD DEFINITIONS *** ****************************************************************************/ - +static int amfs_toplvl_init(mntfs *mf); /**************************************************************************** *** OPS STRUCTURES *** @@ -63,19 +62,23 @@ am_ops amfs_toplvl_ops = { "toplvl", - amfs_auto_match, - 0, /* amfs_auto_init */ + amfs_generic_match, + amfs_toplvl_init, /* amfs_toplvl_init */ amfs_toplvl_mount, - 0, amfs_toplvl_umount, - 0, - amfs_auto_lookuppn, - amfs_auto_readdir, /* browsable version of readdir() */ + amfs_generic_lookup_child, + amfs_generic_mount_child, + amfs_generic_readdir, 0, /* amfs_toplvl_readlink */ - amfs_toplvl_mounted, + amfs_generic_mounted, 0, /* amfs_toplvl_umounted */ - find_amfs_auto_srvr, - FS_MKMNT | FS_NOTIMEOUT | FS_BACKGROUND | FS_AMQINFO | FS_DIRECTORY + amfs_generic_find_srvr, + 0, /* amfs_toplvl_get_wchan */ + FS_MKMNT | FS_NOTIMEOUT | FS_BACKGROUND | + FS_AMQINFO | FS_DIRECTORY, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_TOPLVL_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ }; @@ -83,166 +86,79 @@ am_ops amfs_toplvl_ops = *** 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) +static void +set_auto_attrcache_timeout(char *preopts, char *opts, size_t l) { - 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 +#ifdef MNTTAB_OPT_NOAC /* - * 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. + * Don't cache attributes - they are changing under the kernel's feet. + * For example, IRIX5.2 will dispense with nfs lookup calls and hand stale + * filehandles to getattr unless we disable attribute caching on the + * automount points. */ - 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; + if (gopt.auto_attrcache == 0) { + xsnprintf(preopts, l, ",%s", MNTTAB_OPT_NOAC); + xstrlcat(opts, preopts, l); } -#endif /* not HAVE_TRANSPORT_TYPE_TLI */ +#endif /* MNTTAB_OPT_NOAC */ /* - * Make a ``hostname'' string for the kernel - */ - sprintf(fs_hostname, "pid%ld@%s:%s", - (long) (foreground ? am_mypid : getppid()), - am_get_hostname(), - dir); - /* - * Most kernels have a name length restriction (64 bytes)... + * XXX: note that setting these to 0 in the past resulted in an error on + * some systems, which is why it's better to use "noac" if possible. For + * now, we're setting everything possible, but if this will cause trouble, + * then we'll have to condition the remainder of this on OPT_NOAC. */ - 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 */ +#ifdef MNTTAB_OPT_ACTIMEO + xsnprintf(preopts, l, ",%s=%d", MNTTAB_OPT_ACTIMEO, gopt.auto_attrcache); + xstrlcat(opts, preopts, l); +#else /* MNTTAB_OPT_ACTIMEO */ +# ifdef MNTTAB_OPT_ACDIRMIN + xsnprintf(preopts, l, ",%s=%d", MNTTAB_OPT_ACTDIRMIN, gopt.auto_attrcache); + xstrlcat(opts, preopts, l); +# endif /* MNTTAB_OPT_ACDIRMIN */ +# ifdef MNTTAB_OPT_ACDIRMAX + xsnprintf(preopts, l, ",%s=%d", MNTTAB_OPT_ACTDIRMAX, gopt.auto_attrcache); + xstrlcat(opts, preopts, l); +# endif /* MNTTAB_OPT_ACDIRMAX */ +# ifdef MNTTAB_OPT_ACREGMIN + xsnprintf(preopts, l, ",%s=%d", MNTTAB_OPT_ACTREGMIN, gopt.auto_attrcache); + xstrlcat(opts, preopts, l); +# endif /* MNTTAB_OPT_ACREGMIN */ +# ifdef MNTTAB_OPT_ACREGMAX + xsnprintf(preopts, l, ",%s=%d", MNTTAB_OPT_ACTREGMAX, gopt.auto_attrcache); + xstrlcat(opts, preopts, l); +# endif /* MNTTAB_OPT_ACREGMAX */ +#endif /* MNTTAB_OPT_ACTIMEO */ +} - /* - * Finally we can compute the mount genflags set above, - * and add any automounter specific flags. - */ - genflags = compute_mount_flags(&mnt); - genflags |= compute_automounter_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); +/* + * Initialize a top-level mount. In our case, if the user asked for + * forced_unmounts, and the OS supports it, then we try forced/lazy unmounts + * on any previous toplvl mounts. This is useful if a previous Amd died and + * left behind toplvl mount points (this Amd will clean them up). + * + * WARNING: Don't use forced/lazy unmounts if you have another valid Amd + * running, because this code WILL force those valid toplvl mount points to + * be detached as well! + */ +static int +amfs_toplvl_init(mntfs *mf) +{ + int error = 0; - /* This is it! Here we try to mount amd on its mount points */ -#ifdef DEBUG - amuDebug(D_TRACE) { - print_nfs_args(&nfs_args, 0); - plog(XLOG_DEBUG, "Generic mount flags 0x%x", genflags); +#if defined(MNT2_GEN_OPT_FORCE) || defined(MNT2_GEN_OPT_DETACH) + if (gopt.flags & CFM_FORCED_UNMOUNTS) { + plog(XLOG_INFO, "amfs_toplvl_init: trying forced/lazy unmount of %s", + mf->mf_mount); + error = umount2_fs(mf->mf_mount, AMU_UMOUNT_FORCE | AMU_UMOUNT_DETACH); + if (error) + plog(XLOG_INFO, "amfs_toplvl_init: forced/lazy unmount failed: %m"); + else + dlog("amfs_toplvl_init: forced/lazy unmount succeeded"); } -#endif /* 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 */ - +#endif /* MNT2_GEN_OPT_FORCE || MNT2_GEN_OPT_DETACH */ return error; } @@ -251,13 +167,11 @@ mount_amfs_toplvl(char *dir, char *opts) * Mount the top-level */ int -amfs_toplvl_mount(am_node *mp) +amfs_toplvl_mount(am_node *mp, mntfs *mf) { - mntfs *mf = mp->am_mnt; struct stat stb; - char opts[256], preopts[256]; + char opts[SIZEOF_OPTS], preopts[SIZEOF_OPTS], toplvl_opts[40]; int error; - char *mnttype; /* * Mounting the automounter. @@ -271,16 +185,6 @@ amfs_toplvl_mount(am_node *mp) 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_AMU_FS_UNION - else if (mf->mf_ops == &amfs_union_ops) - mnttype = "union"; -#endif /* HAVE_AMU_FS_UNION */ - else - mnttype = "auto"; /* * Construct some mount options: @@ -288,49 +192,71 @@ amfs_toplvl_mount(am_node *mp) * Tack on magic map=<mapname> option in mtab to emulate * SunOS automounter behavior. */ - preopts[0] = '\0'; + +#ifdef HAVE_FS_AUTOFS + if (mf->mf_flags & MFF_IS_AUTOFS) { + autofs_get_opts(opts, sizeof(opts), mp->am_autofs_fh); + } else +#endif /* HAVE_FS_AUTOFS */ + { + preopts[0] = '\0'; #ifdef MNTTAB_OPT_INTR - strcat(preopts, MNTTAB_OPT_INTR); - strcat(preopts, ","); + xstrlcat(preopts, MNTTAB_OPT_INTR, sizeof(preopts)); + xstrlcat(preopts, ",", sizeof(preopts)); #endif /* MNTTAB_OPT_INTR */ #ifdef MNTTAB_OPT_IGNORE - strcat(preopts, MNTTAB_OPT_IGNORE); - strcat(preopts, ","); + xstrlcat(preopts, MNTTAB_OPT_IGNORE, sizeof(preopts)); + xstrlcat(preopts, ",", sizeof(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); + /* write most of the initial options + preopts */ + xsnprintf(opts, sizeof(opts), "%s%s,%s=%d,%s,map=%s", + preopts, + MNTTAB_OPT_RW, + MNTTAB_OPT_PORT, nfs_port, + mf->mf_ops->fs_type, mf->mf_info); + + /* process toplvl timeo/retrans options, if any */ + if (gopt.amfs_auto_timeo[AMU_TYPE_TOPLVL] > 0) { + xsnprintf(toplvl_opts, sizeof(toplvl_opts), ",%s=%d", + MNTTAB_OPT_TIMEO, gopt.amfs_auto_timeo[AMU_TYPE_TOPLVL]); + xstrlcat(opts, toplvl_opts, sizeof(opts)); + } + if (gopt.amfs_auto_retrans[AMU_TYPE_TOPLVL] > 0) { + xsnprintf(toplvl_opts, sizeof(toplvl_opts), ",%s=%d", + MNTTAB_OPT_RETRANS, gopt.amfs_auto_retrans[AMU_TYPE_TOPLVL]); + xstrlcat(opts, toplvl_opts, sizeof(opts)); + } + +#ifdef MNTTAB_OPT_NOAC + if (gopt.auto_attrcache == 0) { + xstrlcat(opts, ",", sizeof(opts)); + xstrlcat(opts, MNTTAB_OPT_NOAC, sizeof(opts)); + } else +#endif /* MNTTAB_OPT_NOAC */ + set_auto_attrcache_timeout(preopts, opts, sizeof(preopts)); + } /* now do the mount */ - error = mount_amfs_toplvl(mf->mf_mount, opts); + error = amfs_mount(mp, mf, opts); if (error) { errno = error; - plog(XLOG_FATAL, "amfs_toplvl_mount: mount_amfs_toplvl failed: %m"); + plog(XLOG_FATAL, "amfs_toplvl_mount: amfs_mount failed: %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) +amfs_toplvl_umount(am_node *mp, mntfs *mf) { - int error; struct stat stb; + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + int error; + int count = 0; /* how many times did we try to unmount? */ again: /* @@ -345,15 +271,48 @@ again: * actually fixed the problem - so simulate an ls -ld here. */ if (lstat(mp->am_path, &stb) < 0) { -#ifdef DEBUG + error = errno; dlog("lstat(%s): %m", mp->am_path); -#endif /* DEBUG */ + goto out; } - error = UMOUNT_FS(mp->am_path, mnttab_file_name); + if ((stb.st_mode & S_IFMT) != S_IFDIR) { + plog(XLOG_ERROR, "amfs_toplvl_umount: %s is not a directory, aborting.", mp->am_path); + error = ENOTDIR; + goto out; + } + + error = UMOUNT_FS(mp->am_path, mnttab_file_name, unmount_flags); if (error == EBUSY) { +#ifdef HAVE_FS_AUTOFS + /* + * autofs mounts are "in place", so it is possible + * that we can't just unmount our mount points and go away. + * If that's the case, just give up. + */ + if (mf->mf_flags & MFF_IS_AUTOFS) + return error; +#endif /* HAVE_FS_AUTOFS */ plog(XLOG_WARNING, "amfs_toplvl_unmount retrying %s in 1s", mp->am_path); - sleep(1); /* XXX */ + count++; + sleep(1); + /* + * If user wants forced/lazy unmount semantics, then set those flags, + * but only after we've tried normal lstat/umount a few times -- + * otherwise forced unmounts may hang this very same Amd (by preventing + * it from achieving a clean unmount). + */ + if (gopt.flags & CFM_FORCED_UNMOUNTS) { + if (count == 5) { /* after 5 seconds, try MNT_FORCE */ + dlog("enabling forced unmounts for toplvl node %s", mp->am_path); + unmount_flags |= AMU_UMOUNT_FORCE; + } + if (count == 10) { /* after 10 seconds, try MNT_DETACH */ + dlog("enabling detached unmounts for toplvl node %s", mp->am_path); + unmount_flags |= AMU_UMOUNT_DETACH; + } + } goto again; } +out: return error; } diff --git a/contrib/amd/amd/amfs_union.c b/contrib/amd/amd/amfs_union.c index 2a74d8c..6adb5b0 100644 --- a/contrib/amd/amd/amfs_union.c +++ b/contrib/amd/amd/amfs_union.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * 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.3.2.5 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/amfs_union.c * */ @@ -55,6 +54,7 @@ /**************************************************************************** *** FORWARD DEFINITIONS *** ****************************************************************************/ +static int create_amfs_union_node(char *dir, opaque_t arg); static void amfs_union_mounted(mntfs *mf); @@ -64,19 +64,22 @@ static void amfs_union_mounted(mntfs *mf); am_ops amfs_union_ops = { "union", - amfs_auto_match, - 0, /* amfs_auto_init */ + amfs_generic_match, + 0, /* amfs_union_init */ amfs_toplvl_mount, - 0, amfs_toplvl_umount, - 0, - amfs_auto_lookuppn, - amfs_auto_readdir, - 0, /* amfs_toplvl_readlink */ + amfs_generic_lookup_child, + amfs_generic_mount_child, + amfs_generic_readdir, + 0, /* amfs_union_readlink */ amfs_union_mounted, - 0, /* amfs_toplvl_umounted */ - find_amfs_auto_srvr, - FS_MKMNT | FS_NOTIMEOUT | FS_BACKGROUND | FS_AMQINFO | FS_DIRECTORY + 0, /* amfs_union_umounted */ + amfs_generic_find_srvr, + 0, /* amfs_union_get_wchan */ + FS_MKMNT | FS_NOTIMEOUT | FS_BACKGROUND | FS_AMQINFO | FS_DIRECTORY, +#ifdef HAVE_FS_AUTOFS + AUTOFS_UNION_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ }; @@ -85,11 +88,14 @@ am_ops amfs_union_ops = * XXX: this function may not be used anywhere... */ static int -create_amfs_union_node(char *dir, voidp arg) +create_amfs_union_node(char *dir, opaque_t arg) { if (!STREQ(dir, "/defaults")) { int error = 0; - (void) amfs_toplvl_ops.lookuppn(arg, dir, &error, VLOOK_CREATE); + am_node *am; + am = amfs_generic_lookup_child(arg, dir, &error, VLOOK_CREATE); + if (am && error < 0) + am = amfs_generic_mount_child(am, &error); if (error > 0) { errno = error; /* XXX */ plog(XLOG_ERROR, "unionfs: could not mount %s: %m", dir); @@ -103,20 +109,22 @@ create_amfs_union_node(char *dir, voidp arg) static void amfs_union_mounted(mntfs *mf) { - int i; + int index; + am_node *mp; - amfs_auto_mkcacheref(mf); + amfs_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) { + for (mp = get_first_exported_ap(&index); + mp; + mp = get_next_exported_ap(&index)) { + if (mp->am_mnt == mf) { /* return value from create_amfs_union_node is ignored by mapc_keyiter */ (void) mapc_keyiter((mnt_map *) mp->am_mnt->mf_private, - (void (*)(char *, voidp)) create_amfs_union_node, + create_amfs_union_node, mp); break; } diff --git a/contrib/amd/amd/amq_subr.c b/contrib/amd/amd/amq_subr.c index d0e49ba..79ecafc 100644 --- a/contrib/amd/amd/amq_subr.c +++ b/contrib/amd/amd/amq_subr.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: amq_subr.c,v 1.6.2.6 2004/01/19 00:25:55 ezk Exp $ + * File: am-utils/amd/amq_subr.c * */ /* @@ -111,8 +110,10 @@ amq_mount_tree_list * amqproc_export_1_svc(voidp argp, struct svc_req *rqstp) { static amq_mount_tree_list aml; + static am_node *mp; - aml.amq_mount_tree_list_val = (amq_mount_tree_p *) &exported_ap[0]; + mp = get_exported_ap(0); + aml.amq_mount_tree_list_val = (amq_mount_tree_p *) ((void *) &mp); aml.amq_mount_tree_list_len = 1; /* XXX */ return &aml; @@ -130,16 +131,14 @@ amqproc_setopt_1_svc(voidp argp, struct svc_req *rqstp) 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, orig_umask)) + if (switch_to_logfile(opt->as_str, orig_umask, 0)) rc = EINVAL; } else { rc = EACCES; @@ -155,8 +154,8 @@ amqproc_setopt_1_svc(voidp argp, struct svc_req *rqstp) 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(); + flush_nfs_fhandle_cache((fserver *) NULL); + flush_srvr_nfs_cache((fserver *) NULL); } break; } @@ -168,7 +167,7 @@ amqproc_setopt_1_svc(voidp argp, struct svc_req *rqstp) amq_mount_info_list * amqproc_getmntfs_1_svc(voidp argp, struct svc_req *rqstp) { - return (amq_mount_info_list *) ((void *)&mfhead); /* XXX */ + return (amq_mount_info_list *) ((void *)&mfhead); /* XXX */ } @@ -194,6 +193,64 @@ amqproc_getpid_1_svc(voidp argp, struct svc_req *rqstp) /* + * Process PAWD string of remote pawd tool. + * + * We repeat the resolution of the string until the resolved string resolves + * to itself. This ensures that we follow path resolutions through all + * possible Amd mount points until we reach some sort of convergence. To + * prevent possible infinite loops, we break out of this loop if the strings + * do not converge after MAX_PAWD_TRIES times. + */ +amq_string * +amqproc_pawd_1_svc(voidp argp, struct svc_req *rqstp) +{ + static amq_string res; +#define MAX_PAWD_TRIES 10 + int index, len, maxagain = MAX_PAWD_TRIES; + am_node *mp; + char *mountpoint; + char *dir = *(char **) argp; + static char tmp_buf[MAXPATHLEN]; + char prev_buf[MAXPATHLEN]; + + tmp_buf[0] = prev_buf[0] = '\0'; /* default is empty string: no match */ + do { + for (mp = get_first_exported_ap(&index); + mp; + mp = get_next_exported_ap(&index)) { + if (STREQ(mp->am_mnt->mf_ops->fs_type, "toplvl")) + continue; + if (STREQ(mp->am_mnt->mf_ops->fs_type, "auto")) + continue; + mountpoint = (mp->am_link ? mp->am_link : mp->am_mnt->mf_mount); + len = strlen(mountpoint); + if (len == 0) + continue; + if (!NSTREQ(mountpoint, dir, len)) + continue; + if (dir[len] != '\0' && dir[len] != '/') + continue; + xstrlcpy(tmp_buf, mp->am_path, sizeof(tmp_buf)); + xstrlcat(tmp_buf, &dir[len], sizeof(tmp_buf)); + break; + } /* end of "for" loop */ + /* once tmp_buf and prev_buf are equal, break out of "do" loop */ + if (STREQ(tmp_buf, prev_buf)) + break; + else + xstrlcpy(prev_buf, tmp_buf, sizeof(prev_buf)); + } while (--maxagain); + /* check if we couldn't resolve the string after MAX_PAWD_TRIES times */ + if (maxagain <= 0) + plog(XLOG_WARNING, "path \"%s\" did not resolve after %d tries", + tmp_buf, MAX_PAWD_TRIES); + + res = tmp_buf; + return &res; +} + + +/* * XDR routines. */ @@ -201,7 +258,7 @@ amqproc_getpid_1_svc(voidp argp, struct svc_req *rqstp) bool_t xdr_amq_setopt(XDR *xdrs, amq_setopt *objp) { - if (!xdr_enum(xdrs, (enum_t *) & objp->as_opt)) { + if (!xdr_enum(xdrs, (enum_t *) ((voidp) &objp->as_opt))) { return (FALSE); } if (!xdr_string(xdrs, &objp->as_str, AMQ_STRLEN)) { @@ -266,10 +323,16 @@ xdr_amq_mount_subtree(XDR *xdrs, amq_mount_tree *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)) { + if (!xdr_pointer(xdrs, + (char **) ((voidp) &mp->am_osib), + sizeof(amq_mount_tree), + (XDRPROC_T_TYPE) xdr_amq_mount_subtree)) { return (FALSE); } - if (!xdr_pointer(xdrs, (char **) &mp->am_child, sizeof(amq_mount_tree), (XDRPROC_T_TYPE) xdr_amq_mount_subtree)) { + if (!xdr_pointer(xdrs, + (char **) ((voidp) &mp->am_child), + sizeof(amq_mount_tree), + (XDRPROC_T_TYPE) xdr_amq_mount_subtree)) { return (FALSE); } return (TRUE); @@ -280,15 +343,21 @@ bool_t xdr_amq_mount_tree(XDR *xdrs, amq_mount_tree *objp) { am_node *mp = (am_node *) objp; - am_node *mnil = 0; + am_node *mnil = NULL; if (!xdr_amq_mount_tree_node(xdrs, objp)) { return (FALSE); } - if (!xdr_pointer(xdrs, (char **) ((void *)&mnil), sizeof(amq_mount_tree), (XDRPROC_T_TYPE) xdr_amq_mount_subtree)) { + if (!xdr_pointer(xdrs, + (char **) ((voidp) &mnil), + sizeof(amq_mount_tree), + (XDRPROC_T_TYPE) xdr_amq_mount_subtree)) { return (FALSE); } - if (!xdr_pointer(xdrs, (char **) &mp->am_child, sizeof(amq_mount_tree), (XDRPROC_T_TYPE) xdr_amq_mount_subtree)) { + if (!xdr_pointer(xdrs, + (char **) ((voidp) &mp->am_child), + sizeof(amq_mount_tree), + (XDRPROC_T_TYPE) xdr_amq_mount_subtree)) { return (FALSE); } return (TRUE); @@ -332,7 +401,7 @@ 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, + (char **) ((voidp) &objp->amq_mount_tree_list_val), (u_int *) &objp->amq_mount_tree_list_len, ~0, sizeof(amq_mount_tree_p), @@ -354,7 +423,7 @@ xdr_amq_mount_info_qelem(XDR *xdrs, qelem *qhead) 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)) + if (!(mf->mf_fsflags & FS_AMQINFO)) continue; len++; } @@ -365,7 +434,7 @@ xdr_amq_mount_info_qelem(XDR *xdrs, qelem *qhead) */ for (mf = AM_LAST(mntfs, qhead); mf != HEAD(mntfs, qhead); mf = PREV(mntfs, mf)) { int up; - if (!(mf->mf_ops->fs_flags & FS_AMQINFO)) + if (!(mf->mf_fsflags & FS_AMQINFO)) continue; if (!xdr_amq_string(xdrs, &mf->mf_ops->fs_type)) { @@ -386,20 +455,12 @@ xdr_amq_mount_info_qelem(XDR *xdrs, qelem *qhead) if (!xdr_int(xdrs, &mf->mf_refc)) { return (FALSE); } - if (mf->mf_server->fs_flags & FSF_ERROR) + if (FSRV_ERROR(mf->mf_server) || FSRV_ISDOWN(mf->mf_server)) up = 0; + else if (FSRV_ISUP(mf->mf_server)) + up = 1; 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; - } + up = -1; if (!xdr_int(xdrs, &up)) { return (FALSE); } diff --git a/contrib/amd/amd/amq_svc.c b/contrib/amd/amd/amq_svc.c index 8468567..6fadb16 100644 --- a/contrib/amd/amd/amq_svc.c +++ b/contrib/amd/amd/amq_svc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: amq_svc.c,v 1.4.2.5 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/amq_svc.c * */ @@ -51,6 +50,65 @@ /* typedefs */ typedef char *(*amqsvcproc_t)(voidp, struct svc_req *); +#if defined(HAVE_TCPD_H) && defined(HAVE_LIBWRAP) +# ifdef NEED_LIBWRAP_SEVERITY_VARIABLES +/* + * Some systems that define libwrap already define these two variables + * in libwrap, while others don't: so I need to know precisely iff + * to define these two severity variables. + */ +int allow_severity=0, deny_severity=0; +# endif /* NEED_LIBWRAP_SEVERITY_VARIABLES */ + +/* + * check if remote amq is authorized to access this amd. + * Returns: 1=allowed, 0=denied. + */ +static int +amqsvc_is_client_allowed(const struct sockaddr_in *addr, char *remote) +{ + struct hostent *h; + char *name = NULL, **ad; + int ret = 0; /* default is 0==denied */ + + /* Check IP address */ + if (hosts_ctl(AMD_SERVICE_NAME, "", remote, "")) { + ret = 1; + goto out; + } + /* Get address */ + if (!(h = gethostbyaddr((const char *)&(addr->sin_addr), + sizeof(addr->sin_addr), + AF_INET))) + goto out; + if (!(name = strdup(h->h_name))) + goto out; + /* Paranoia check */ + if (!(h = gethostbyname(name))) + goto out; + for (ad = h->h_addr_list; *ad; ad++) + if (!memcmp(*ad, &(addr->sin_addr), h->h_length)) + break; + if (!*ad) + goto out; + if (hosts_ctl(AMD_SERVICE_NAME, "", h->h_name, "")) { + return 1; + goto out; + } + /* Check aliases */ + for (ad = h->h_aliases; *ad; ad++) + if (hosts_ctl(AMD_SERVICE_NAME, "", *ad, "")) { + return 1; + goto out; + } + + out: + if (name) + XFREE(name); + return ret; +} +#endif /* defined(HAVE_TCPD_H) && defined(HAVE_LIBWRAP) */ + void amq_program_1(struct svc_req *rqstp, SVCXPRT *transp) @@ -64,6 +122,21 @@ amq_program_1(struct svc_req *rqstp, SVCXPRT *transp) xdrproc_t xdr_argument, xdr_result; amqsvcproc_t local; +#if defined(HAVE_TCPD_H) && defined(HAVE_LIBWRAP) + if (gopt.flags & CFM_USE_TCPWRAPPERS) { + struct sockaddr_in *remote_addr = svc_getcaller(rqstp->rq_xprt); + char *remote_hostname = inet_ntoa(remote_addr->sin_addr); + + if (!amqsvc_is_client_allowed(remote_addr, remote_hostname)) { + plog(XLOG_WARNING, "Amd denied remote amq service to %s", remote_hostname); + svcerr_auth(transp, AUTH_FAILED); + return; + } else { + dlog("Amd allowed remote amq service to %s", remote_hostname); + } + } +#endif /* defined(HAVE_TCPD_H) && defined(HAVE_LIBWRAP) */ + switch (rqstp->rq_proc) { case AMQPROC_NULL: @@ -120,6 +193,12 @@ amq_program_1(struct svc_req *rqstp, SVCXPRT *transp) local = (amqsvcproc_t) amqproc_getpid_1_svc; break; + case AMQPROC_PAWD: + xdr_argument = (xdrproc_t) xdr_amq_string; + xdr_result = (xdrproc_t) xdr_amq_string; + local = (amqsvcproc_t) amqproc_pawd_1_svc; + break; + default: svcerr_noproc(transp); return; diff --git a/contrib/amd/amd/autil.c b/contrib/amd/amd/autil.c index bb43918..a5ecd6d 100644 --- a/contrib/amd/amd/autil.c +++ b/contrib/amd/amd/autil.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: autil.c,v 1.4.2.6 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/autil.c * */ @@ -52,100 +51,49 @@ #include <am_defs.h> #include <amd.h> -int NumChild = 0; /* number of children of primary amd */ +int NumChildren = 0; /* number of children of primary amd */ static char invalid_keys[] = "\"'!;@ \t\n"; +/**************************************************************************** + *** MACROS *** + ****************************************************************************/ + #ifdef HAVE_TRANSPORT_TYPE_TLI # define PARENT_USLEEP_TIME 100000 /* 0.1 seconds */ #endif /* HAVE_TRANSPORT_TYPE_TLI */ +/**************************************************************************** + *** FORWARD DEFINITIONS *** + ****************************************************************************/ +static void domain_strip(char *otherdom, char *localdom); +static int dofork(void); + + +/**************************************************************************** + *** FUNCTIONS *** + ****************************************************************************/ + +/* + * Copy s into p, reallocating p if necessary + */ char * strealloc(char *p, char *s) { - int len = strlen(s) + 1; + size_t len = strlen(s) + 1; p = (char *) xrealloc((voidp) p, len); - strcpy(p, s); + xstrlcpy(p, s, len); #ifdef DEBUG_MEM +# if defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) malloc_verify(); +# endif /* not defined(HAVE_MALLINFO) && defined(HAVE_MALLOC_VERIFY) */ #endif /* DEBUG_MEM */ return p; } -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 @@ -170,7 +118,8 @@ domain_strip(char *otherdom, char *localdom) /* - * Normalize a host name + * Normalize a host name: replace cnames with real names, and decide if to + * strip domain name or not. */ void host_normalize(char **chp) @@ -182,20 +131,18 @@ host_normalize(char **chp) */ 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); + if (gopt.flags & CFM_DOMAIN_STRIP) { + domain_strip(*chp, hostd); + } } - /* * Keys are not allowed to contain " ' ! or ; to avoid * problems with macro expansions. @@ -226,14 +173,20 @@ forcibly_timeout_mp(am_node *mp) } else { plog(XLOG_INFO, "\"%s\" forcibly timed out", mp->am_path); mp->am_flags &= ~AMF_NOTIMEOUT; - mp->am_ttl = clocktime(); + mp->am_ttl = clocktime(NULL); + /* + * Force mtime update of parent dir, to prevent DNLC/dcache from caching + * the old entry, which could result in ESTALE errors, bad symlinks, and + * more. + */ + clocktime(&mp->am_parent->am_fattr.na_mtime); reschedule_timeout_mp(); } } void -mf_mounted(mntfs *mf) +mf_mounted(mntfs *mf, bool_t call_free_opts) { int quoted; int wasmounted = mf->mf_flags & MFF_MOUNTED; @@ -250,10 +203,33 @@ mf_mounted(mntfs *mf) /* * Do mounted callback */ - if (mf->mf_ops->mounted) { - (*mf->mf_ops->mounted) (mf); + if (mf->mf_ops->mounted) + mf->mf_ops->mounted(mf); + + /* + * Be careful when calling free_ops and XFREE here. Some pseudo file + * systems like nfsx call this function (mf_mounted), even though it + * would be called by the lower-level amd file system functions. nfsx + * needs to call this function because of the other actions it takes. + * So we pass a boolean from the caller (yes, not so clean workaround) + * to determine if we should free or not. If we're not freeing (often + * because we're called from a callback function), then just to be sure, + * we'll zero out the am_opts structure and set the pointer to NULL. + * The parent mntfs node owns this memory and is going to free it with a + * call to mf_mounted(mntfs,TRUE) (see comment in the am_mounted code). + */ + if (call_free_opts) { + free_opts(mf->mf_fo); /* this free is needed to prevent leaks */ + XFREE(mf->mf_fo); /* (also this one) */ + } else { + memset(mf->mf_fo, 0, sizeof(am_opts)); + mf->mf_fo = NULL; } - mf->mf_fo = 0; + } + + if (mf->mf_flags & MFF_RESTART) { + mf->mf_flags &= ~MFF_RESTART; + dlog("Restarted filesystem %s, flags 0x%x", mf->mf_mount, mf->mf_flags); } /* @@ -272,40 +248,61 @@ mf_mounted(mntfs *mf) void am_mounted(am_node *mp) { + int notimeout = 0; /* assume normal timeouts initially */ mntfs *mf = mp->am_mnt; - mf_mounted(mf); + /* + * This is the parent mntfs which does the mf->mf_fo (am_opts type), and + * we're passing TRUE here to tell mf_mounted to actually free the + * am_opts. See a related comment in mf_mounted(). + */ + mf_mounted(mf, TRUE); + +#ifdef HAVE_FS_AUTOFS + if (mf->mf_flags & MFF_IS_AUTOFS) + autofs_mounted(mp); +#endif /* HAVE_FS_AUTOFS */ /* * Patch up path for direct mounts */ - if (mp->am_parent && mp->am_parent->am_mnt->mf_ops == &amfs_direct_ops) + if (mp->am_parent && mp->am_parent->am_mnt->mf_fsflags & FS_DIRECT) mp->am_path = str3cat(mp->am_path, mp->am_parent->am_path, "/", "."); /* - * Check whether this mount should be cached permanently + * Check whether this mount should be cached permanently or not, + * and handle user-requested timeouts. */ - if (mf->mf_ops->fs_flags & FS_NOTIMEOUT) { + /* first check if file system was set to never timeout */ + if (mf->mf_fsflags & FS_NOTIMEOUT) + notimeout = 1; + /* next, alter that decision by map flags */ + if (mf->mf_mopts) { mntent_t mnt; mnt.mnt_opts = mf->mf_mopts; - if (mf->mf_mopts && hasmntopt(&mnt, "unmount")) - mp->am_flags &= ~AMF_NOTIMEOUT; + /* umount option: user wants to unmount this entry */ + if (amu_hasmntopt(&mnt, "unmount") || amu_hasmntopt(&mnt, "umount")) + notimeout = 0; + /* noumount option: user does NOT want to unmount this entry */ + if (amu_hasmntopt(&mnt, "nounmount") || amu_hasmntopt(&mnt, "noumount")) + notimeout = 1; + /* utimeout=N option: user wants to unmount this option AND set timeout */ + if ((mp->am_timeo = hasmntval(&mnt, "utimeout")) == 0) + mp->am_timeo = gopt.am_timeo; /* otherwise use default timeout */ else - mp->am_flags |= AMF_NOTIMEOUT; - } else if (mf->mf_mount[1] == '\0' && mf->mf_mount[0] == '/') { + notimeout = 0; + /* special case: don't try to unmount "/" (it can never succeed) */ + if (mf->mf_mount[0] == '/' && mf->mf_mount[1] == '\0') + notimeout = 1; + } + /* finally set actual flags */ + if (notimeout) { mp->am_flags |= AMF_NOTIMEOUT; + plog(XLOG_INFO, "%s set to never timeout", mp->am_path); } else { - mntent_t mnt; - if (mf->mf_mopts) { - mnt.mnt_opts = mf->mf_mopts; - if (hasmntopt(&mnt, "nounmount")) - mp->am_flags |= AMF_NOTIMEOUT; - if (hasmntopt(&mnt, "unmount")) - mp->am_flags &= ~AMF_NOTIMEOUT; - if ((mp->am_timeo = hasmntval(&mnt, "utimeout")) == 0) - mp->am_timeo = gopt.am_timeo; - } + mp->am_flags &= ~AMF_NOTIMEOUT; + plog(XLOG_INFO, "%s set to timeout in %d seconds", mp->am_path, mp->am_timeo); } /* @@ -313,25 +310,38 @@ am_mounted(am_node *mp) * 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); + mp->am_fattr.na_size = strlen(mp->am_link ? mp->am_link : mf->mf_mount); /* - * Record mount time + * Record mount time, and update am_stats at the same time. */ - mp->am_fattr.na_mtime.nt_seconds = mp->am_stats.s_mtime = clocktime(); + mp->am_stats.s_mtime = clocktime(&mp->am_fattr.na_mtime); new_ttl(mp); /* - * Update mtime of parent node + * Update mtime of parent node (copying "struct nfstime" in '=' below) */ if (mp->am_parent && mp->am_parent->am_mnt) - mp->am_parent->am_fattr.na_mtime.nt_seconds = mp->am_stats.s_mtime; + mp->am_parent->am_fattr.na_mtime = mp->am_fattr.na_mtime; /* - * Now, if we can, do a reply to our NFS client here + * This is ugly, but essentially unavoidable + * Sublinks must be treated separately as type==link + * when the base type is different. + */ + if (mp->am_link && mf->mf_ops != &amfs_link_ops) + amfs_link_ops.mount_fs(mp, mf); + + /* + * Now, if we can, do a reply to our client here * to speed things up. */ - quick_reply(mp, 0); +#ifdef HAVE_FS_AUTOFS + if (mp->am_flags & AMF_AUTOFS) + autofs_mount_succeeded(mp); + else +#endif /* HAVE_FS_AUTOFS */ + nfs_quick_reply(mp, 0); /* * Update stats @@ -340,21 +350,303 @@ am_mounted(am_node *mp) } +/* + * Replace mount point with a reference to an error filesystem. + * The mount point (struct mntfs) is NOT discarded, + * the caller must do it if it wants to _before_ calling this function. + */ +void +assign_error_mntfs(am_node *mp) +{ + int error; + dlog("assign_error_mntfs"); + /* + * Save the old error code + */ + error = mp->am_error; + if (error <= 0) + error = mp->am_mnt->mf_error; + /* + * Allocate a new error reference + */ + mp->am_mnt = new_mntfs(); + /* + * Put back the error code + */ + mp->am_mnt->mf_error = error; + mp->am_mnt->mf_flags |= MFF_ERROR; + /* + * Zero the error in the mount point + */ + mp->am_error = 0; +} + + +/* + * Build a new map cache for this node, or re-use + * an existing cache for the same map. + */ +void +amfs_mkcacheref(mntfs *mf) +{ + char *cache; + + if (mf->mf_fo && mf->mf_fo->opt_cache) + cache = mf->mf_fo->opt_cache; + else + cache = "none"; + mf->mf_private = (opaque_t) mapc_find(mf->mf_info, + cache, + (mf->mf_fo ? mf->mf_fo->opt_maptype : NULL)); + mf->mf_prfree = mapc_free; +} + + +/* + * Locate next node in sibling list which is mounted + * and is not an error node. + */ +am_node * +next_nonerror_node(am_node *xp) +{ + mntfs *mf; + + /* + * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no> + * Fixes a race condition when mounting direct automounts. + * Also fixes a problem when doing a readdir on a directory + * containing hung automounts. + */ + while (xp && + (!(mf = xp->am_mnt) || /* No mounted filesystem */ + mf->mf_error != 0 || /* There was a mntfs error */ + xp->am_error != 0 || /* There was a mount error */ + !(mf->mf_flags & MFF_MOUNTED) || /* The fs is not mounted */ + (mf->mf_server->fs_flags & FSF_DOWN)) /* The fs may be down */ + ) + xp = xp->am_osib; + + return xp; +} + + +/* + * Mount an automounter directory. + * The automounter is connected into the system + * as a user-level NFS server. amfs_mount constructs + * the necessary NFS parameters to be given to the + * kernel so that it will talk back to us. + * + * NOTE: automounter mounts in themselves are using NFS Version 2 (UDP). + * + * NEW: on certain systems, mounting can be done using the + * kernel-level automount (autofs) support. In that case, + * we don't need NFS at all here. + */ int -mount_node(am_node *mp) +amfs_mount(am_node *mp, mntfs *mf, char *opts) { - mntfs *mf = mp->am_mnt; - int error = 0; + char fs_hostname[MAXHOSTNAMELEN + MAXPATHLEN + 1]; + int retry, error = 0, genflags; + int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; + char *dir = mf->mf_mount; + mntent_t mnt; + MTYPE_TYPE type; + int forced_unmount = 0; /* are we using forced unmounts? */ + + memset((voidp) &mnt, 0, sizeof(mnt)); + mnt.mnt_dir = dir; + mnt.mnt_fsname = pid_fsname; + mnt.mnt_opts = opts; + +#ifdef HAVE_FS_AUTOFS + if (mf->mf_flags & MFF_IS_AUTOFS) { + type = MOUNT_TYPE_AUTOFS; + /* + * Make sure that amd's top-level autofs mounts are hidden by default + * from df. + * XXX: It works ok on Linux, might not work on other systems. + */ + mnt.mnt_type = "autofs"; + } else +#endif /* HAVE_FS_AUTOFS */ + { + type = MOUNT_TYPE_NFS; + /* + * Make sure that amd's top-level NFS mounts are hidden by default + * from df. + * If they don't appear to support the either the "ignore" mnttab + * option entry, or the "auto" one, set the mount type to "nfs". + */ + mnt.mnt_type = HIDE_MOUNT_TYPE; + } - mf->mf_flags |= MFF_MOUNTING; - error = (*mf->mf_ops->mount_fs) (mp); + retry = hasmntval(&mnt, MNTTAB_OPT_RETRY); + if (retry <= 0) + retry = 2; /* XXX: default to 2 retries */ - 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); + /* + * SET MOUNT ARGS + */ + + /* + * Make a ``hostname'' string for the kernel + */ + xsnprintf(fs_hostname, sizeof(fs_hostname), "pid%ld@%s:%s", + get_server_pid(), am_get_hostname(), dir); + /* + * Most kernels have a name length restriction (64 bytes)... + */ + if (strlen(fs_hostname) >= MAXHOSTNAMELEN) + xstrlcpy(fs_hostname + MAXHOSTNAMELEN - 3, "..", + sizeof(fs_hostname) - MAXHOSTNAMELEN + 3); +#ifdef HOSTNAMESZ + /* + * ... and some of these restrictions are 32 bytes (HOSTNAMESZ) + * If you need to get the definition for HOSTNAMESZ found, you may + * add the proper header file to the conf/nfs_prot/nfs_prot_*.h file. + */ + if (strlen(fs_hostname) >= HOSTNAMESZ) + xstrlcpy(fs_hostname + HOSTNAMESZ - 3, "..", + sizeof(fs_hostname) - HOSTNAMESZ + 3); +#endif /* HOSTNAMESZ */ + + /* + * Finally we can compute the mount genflags set above, + * and add any automounter specific flags. + */ + genflags = compute_mount_flags(&mnt); +#ifdef HAVE_FS_AUTOFS + if (on_autofs) + genflags |= autofs_compute_mount_flags(&mnt); +#endif /* HAVE_FS_AUTOFS */ + genflags |= compute_automounter_mount_flags(&mnt); + +again: + if (!(mf->mf_flags & MFF_IS_AUTOFS)) { + nfs_args_t nfs_args; + am_nfs_fh *fhp; + am_nfs_handle_t anh; +#ifndef HAVE_TRANSPORT_TYPE_TLI + u_short port; + struct sockaddr_in sin; +#endif /* not HAVE_TRANSPORT_TYPE_TLI */ + + /* + * get fhandle of remote path for automount point + */ + fhp = get_root_nfs_fh(dir); + if (!fhp) { + plog(XLOG_FATAL, "Can't find root file handle for %s", dir); + return EINVAL; + } + +#ifndef HAVE_TRANSPORT_TYPE_TLI + /* + * Create sockaddr to point to the local machine. + */ + memset((voidp) &sin, 0, sizeof(sin)); + /* as per POSIX, sin_len need not be set (used internally by kernel) */ + sin.sin_family = AF_INET; + sin.sin_addr = myipaddr; + port = hasmntval(&mnt, MNTTAB_OPT_PORT); + if (port) { + sin.sin_port = htons(port); + } else { + plog(XLOG_ERROR, "no port number specified for %s", dir); + return EINVAL; + } +#endif /* not HAVE_TRANSPORT_TYPE_TLI */ + + /* setup the many fields and flags within nfs_args */ + memmove(&anh.v2, fhp, sizeof(*fhp)); +#ifdef HAVE_TRANSPORT_TYPE_TLI + compute_nfs_args(&nfs_args, + &mnt, + genflags, + nfsncp, + NULL, /* remote host IP addr is set below */ + NFS_VERSION, /* version 2 */ + "udp", + &anh, + fs_hostname, + pid_fsname); + /* + * IMPORTANT: set the correct IP address AFTERWARDS. It cannot + * be done using the normal mechanism of compute_nfs_args(), because + * that one will allocate a new address and use NFS_SA_DREF() to copy + * parts to it, while assuming that the ip_addr passed is always + * a "struct sockaddr_in". That assumption is incorrect on TLI systems, + * because they define a special macro HOST_SELF which is DIFFERENT + * than localhost (127.0.0.1)! + */ + nfs_args.addr = &nfsxprt->xp_ltaddr; +#else /* not HAVE_TRANSPORT_TYPE_TLI */ + compute_nfs_args(&nfs_args, + &mnt, + genflags, + NULL, + &sin, + NFS_VERSION, /* version 2 */ + "udp", + &anh, + fs_hostname, + pid_fsname); +#endif /* not HAVE_TRANSPORT_TYPE_TLI */ + + /************************************************************************* + * NOTE: while compute_nfs_args() works ok for regular NFS mounts * + * the toplvl one is not quite regular, and so some options must be * + * corrected by hand more carefully, *after* compute_nfs_args() runs. * + *************************************************************************/ + compute_automounter_nfs_args(&nfs_args, &mnt); + + if (amuDebug(D_TRACE)) { + print_nfs_args(&nfs_args, 0); + plog(XLOG_DEBUG, "Generic mount flags 0x%x", genflags); + } + + /* This is it! Here we try to mount amd on its mount points */ + error = mount_fs(&mnt, genflags, (caddr_t) &nfs_args, + retry, type, 0, NULL, mnttab_file_name, on_autofs); + +#ifdef HAVE_TRANSPORT_TYPE_TLI + free_knetconfig(nfs_args.knconf); + /* + * local automounter mounts do not allocate a special address, so + * no need to XFREE(nfs_args.addr) under TLI. + */ +#endif /* HAVE_TRANSPORT_TYPE_TLI */ + +#ifdef HAVE_FS_AUTOFS + } else { + /* This is it! Here we try to mount amd on its mount points */ + error = mount_fs(&mnt, genflags, (caddr_t) mp->am_autofs_fh, + retry, type, 0, NULL, mnttab_file_name, on_autofs); +#endif /* HAVE_FS_AUTOFS */ + } + if (error == 0 || forced_unmount) + return error; + + /* + * If user wants forced/lazy unmount semantics, then try it iff the + * current mount failed with EIO or ESTALE. + */ + if (gopt.flags & CFM_FORCED_UNMOUNTS) { + switch (errno) { + case ESTALE: + case EIO: + forced_unmount = errno; + plog(XLOG_WARNING, "Mount %s failed (%m); force unmount.", mp->am_path); + if ((error = UMOUNT_FS(mp->am_path, mnttab_file_name, + AMU_UMOUNT_FORCE | AMU_UMOUNT_DETACH)) < 0) { + plog(XLOG_WARNING, "Forced umount %s failed: %m.", mp->am_path); + errno = forced_unmount; + } else + goto again; + default: + break; + } } return error; @@ -373,15 +665,79 @@ am_unmounted(am_node *mp) * Do unmounted callback */ if (mf->mf_ops->umounted) - (*mf->mf_ops->umounted) (mp); + mf->mf_ops->umounted(mf); + + /* + * This is ugly, but essentially unavoidable. + * Sublinks must be treated separately as type==link + * when the base type is different. + */ + if (mp->am_link && mf->mf_ops != &amfs_link_ops) + amfs_link_ops.umount_fs(mp, mf); + +#ifdef HAVE_FS_AUTOFS + if (mf->mf_flags & MFF_IS_AUTOFS) + autofs_release_fh(mp); + if (mp->am_flags & AMF_AUTOFS) + autofs_umount_succeeded(mp); +#endif /* HAVE_FS_AUTOFS */ + + /* + * Clean up any directories that were made + * + * If we remove the mount point of a pending mount, any queued access + * to it will fail. So don't do it in that case. + * Also don't do it if the refcount is > 1. + */ + if (mf->mf_flags & MFF_MKMNT && + mf->mf_refc == 1 && + !(mp->am_flags & AMF_REMOUNT)) { + plog(XLOG_INFO, "removing mountpoint directory '%s'", mf->mf_mount); + rmdirs(mf->mf_mount); + mf->mf_flags &= ~MFF_MKMNT; + } + + /* + * If this is a pseudo-directory then adjust the link count + * in the parent + */ + if (mp->am_parent && mp->am_fattr.na_type == NFDIR) + --mp->am_parent->am_fattr.na_nlink; /* * Update mtime of parent node */ if (mp->am_parent && mp->am_parent->am_mnt) - mp->am_parent->am_fattr.na_mtime.nt_seconds = clocktime(); - - free_map(mp); + clocktime(&mp->am_parent->am_fattr.na_mtime); + + if (mp->am_parent && (mp->am_flags & AMF_REMOUNT)) { + char *fname = strdup(mp->am_name); + am_node *mp_parent = mp->am_parent; + mntfs *mf_parent = mp_parent->am_mnt; + int error = 0; + + free_map(mp); + plog(XLOG_INFO, "am_unmounted: remounting %s", fname); + mp = mf_parent->mf_ops->lookup_child(mp_parent, fname, &error, VLOOK_CREATE); + if (mp && error < 0) + mp = mf_parent->mf_ops->mount_child(mp, &error); + if (error > 0) { + errno = error; + plog(XLOG_ERROR, "am_unmounted: could not remount %s: %m", fname); + } + XFREE(fname); + } else + /* + * We have a race here. + * If this node has a pending mount and amd is going down (unmounting + * everything in the process), then we could potentially free it here + * while a struct continuation still has a reference to it. So when + * amfs_cont is called, it blows up. + * We avoid the race by refusing to free any nodes that have + * pending mounts (defined as having a non-NULL am_mfarray). + */ + if (!mp->am_mfarray) + free_map(mp); } @@ -406,7 +762,7 @@ top: am_set_mypid(); foreground = 0; } else { /* parent process, has one more child */ - NumChild++; + NumChildren++; } return pid; @@ -419,10 +775,9 @@ background(void) int pid = dofork(); if (pid == 0) { -#ifdef DEBUG dlog("backgrounded"); -#endif /* DEBUG */ foreground = 0; - } + } else + dlog("forked process %d", pid); return pid; } diff --git a/contrib/amd/amd/clock.c b/contrib/amd/amd/clock.c index 9f1a068..0caa7df 100644 --- a/contrib/amd/amd/clock.c +++ b/contrib/amd/amd/clock.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1989 Jan-Simon Pendry * Copyright (c) 1989 Imperial College of Science, Technology & Medicine * Copyright (c) 1989 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: clock.c,v 1.4.2.4 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/clock.c * */ @@ -59,14 +58,13 @@ #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 */ + callout_fun *c_fn; /* Function to call */ + opaque_t c_arg; /* Argument to pass to call */ time_t c_time; /* Time of call */ int c_id; /* Unique identifier */ }; @@ -87,7 +85,7 @@ time_t next_softclock; /* Time of next call to softclock() */ /* * Global assumption: valid id's are non-zero. */ -#define CID_ALLOC(struct ) (++callout_id) +#define CID_ALLOC() (++callout_id) #define CID_UNDEF (0) @@ -121,22 +119,22 @@ free_callout(callout *cp) /* * Schedule a callout. * - * (*fn)(closure) will be called at clocktime() + secs + * (*fn)(fn_arg) will be called at clocktime(NULL) + secs */ int -timeout(u_int secs, void (*fn) (voidp), voidp closure) +timeout(u_int secs, callout_fun *fn, opaque_t fn_arg) { callout *cp, *cp2; - time_t t = clocktime() + secs; + time_t t = clocktime(NULL) + secs; /* * Allocate and fill in a new callout structure */ callout *cpnew = alloc_callout(); - cpnew->c_closure = closure; + cpnew->c_arg = fn_arg; cpnew->c_fn = fn; cpnew->c_time = t; - cpnew->c_id = CID_ALLOC(struct ); + cpnew->c_id = CID_ALLOC(); if (t < next_softclock) next_softclock = t; @@ -189,10 +187,8 @@ reschedule_timeouts(time_t now, time_t then) 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 %ld seconds", cp->c_id, (long) (cp->c_time - now)); -#endif /* DEBUG */ next_softclock = cp->c_time = now; } } @@ -212,14 +208,14 @@ softclock(void) if (task_notify_todo) do_task_notify(); - now = clocktime(); + now = clocktime(NULL); /* * While there are more callouts waiting... */ while ((cp = callouts.c_next) && cp->c_time <= now) { /* - * Extract first from list, save fn & closure and + * Extract first from list, save fn & fn_arg and * unlink callout from list and free. * Finally call function. * @@ -228,12 +224,12 @@ softclock(void) * function will call timeout() * and try to allocate a callout */ - void (*fn) (voidp) = cp->c_fn; - voidp closure = cp->c_closure; + callout_fun *fn = cp->c_fn; + opaque_t fn_arg = cp->c_arg; callouts.c_next = cp->c_next; free_callout(cp); - (*fn) (closure); + (*fn) (fn_arg); } } while (task_notify_todo); diff --git a/contrib/amd/amd/conf.c b/contrib/amd/amd/conf.c index 90bbdda..2fc587b 100644 --- a/contrib/amd/amd/conf.c +++ b/contrib/amd/amd/conf.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: conf.c,v 1.7.2.8 2004/01/21 04:04:58 ib42 Exp $ + * File: am-utils/amd/conf.c * */ @@ -76,12 +75,18 @@ struct _func_map { * FORWARD DECLARATIONS: */ static int gopt_arch(const char *val); +static int gopt_auto_attrcache(const char *val); static int gopt_auto_dir(const char *val); +static int gopt_autofs_use_lofs(const char *val); static int gopt_browsable_dirs(const char *val); static int gopt_cache_duration(const char *val); static int gopt_cluster(const char *val); +static int gopt_debug_mtab_file(const char *val); static int gopt_debug_options(const char *val); static int gopt_dismount_interval(const char *val); +static int gopt_domain_strip(const char *val); +static int gopt_exec_map_timeout(const char *val); +static int gopt_forced_unmounts(const char *val); static int gopt_full_os(const char *val); static int gopt_fully_qualified_hosts(const char *val); static int gopt_hesiod_base(const char *val); @@ -90,21 +95,34 @@ static int gopt_ldap_base(const char *val); static int gopt_ldap_cache_maxmem(const char *val); static int gopt_ldap_cache_seconds(const char *val); static int gopt_ldap_hostports(const char *val); +static int gopt_ldap_proto_version(const char *val); static int gopt_local_domain(const char *val); +static int gopt_localhost_address(const char *val); static int gopt_log_file(const char *val); static int gopt_log_options(const char *val); +static int gopt_map_defaults(const char *val); static int gopt_map_options(const char *val); +static int gopt_map_reload_interval(const char *val); static int gopt_map_type(const char *val); static int gopt_mount_type(const char *val); static int gopt_pid_file(const char *val); static int gopt_portmap_program(const char *val); +static int gopt_preferred_amq_port(const char *val); +static int gopt_nfs_allow_any_interface(const char *val); static int gopt_nfs_allow_insecure_port(const char *val); static int gopt_nfs_proto(const char *val); static int gopt_nfs_retransmit_counter(const char *val); +static int gopt_nfs_retransmit_counter_udp(const char *val); +static int gopt_nfs_retransmit_counter_tcp(const char *val); +static int gopt_nfs_retransmit_counter_toplvl(const char *val); static int gopt_nfs_retry_interval(const char *val); +static int gopt_nfs_retry_interval_udp(const char *val); +static int gopt_nfs_retry_interval_tcp(const char *val); +static int gopt_nfs_retry_interval_toplvl(const char *val); static int gopt_nfs_vers(const char *val); static int gopt_nis_domain(const char *val); static int gopt_normalize_hostnames(const char *val); +static int gopt_normalize_slashes(const char *val); static int gopt_os(const char *val); static int gopt_osver(const char *val); static int gopt_plock(const char *val); @@ -114,33 +132,43 @@ static int gopt_restart_mounts(const char *val); static int gopt_search_path(const char *val); static int gopt_selectors_in_defaults(const char *val); static int gopt_show_statfs_entries(const char *val); +static int gopt_truncate_log(const char *val); static int gopt_unmount_on_exit(const char *val); +static int gopt_use_tcpwrappers(const char *val); static int gopt_vendor(const char *val); static int process_global_option(const char *key, const char *val); -static int process_regular_map(cf_map_t *cfm); +static int process_one_regular_map(const cf_map_t *cfm); static int process_regular_option(const char *section, const char *key, const char *val, cf_map_t *cfm); static int ropt_browsable_dirs(const char *val, cf_map_t *cfm); static int ropt_map_name(const char *val, cf_map_t *cfm); +static int ropt_map_defaults(const char *val, cf_map_t *cfm); static int ropt_map_options(const char *val, cf_map_t *cfm); static int ropt_map_type(const char *val, cf_map_t *cfm); static int ropt_mount_type(const char *val, cf_map_t *cfm); static int ropt_search_path(const char *val, cf_map_t *cfm); static int ropt_tag(const char *val, cf_map_t *cfm); -static void reset_cf_map(cf_map_t *cfm); +static void init_cf_map(cf_map_t *cfm); /* * STATIC VARIABLES: */ -static cf_map_t cur_map; +static cf_map_t *head_map, *cur_map; + static struct _func_map glob_functable[] = { {"arch", gopt_arch}, + {"auto_attrcache", gopt_auto_attrcache}, {"auto_dir", gopt_auto_dir}, + {"autofs_use_lofs", gopt_autofs_use_lofs}, {"browsable_dirs", gopt_browsable_dirs}, {"cache_duration", gopt_cache_duration}, {"cluster", gopt_cluster}, + {"debug_mtab_file", gopt_debug_mtab_file}, {"debug_options", gopt_debug_options}, {"dismount_interval", gopt_dismount_interval}, + {"domain_strip", gopt_domain_strip}, + {"exec_map_timeout", gopt_exec_map_timeout}, + {"forced_unmounts", gopt_forced_unmounts}, {"fully_qualified_hosts", gopt_fully_qualified_hosts}, {"full_os", gopt_full_os}, {"hesiod_base", gopt_hesiod_base}, @@ -149,21 +177,34 @@ static struct _func_map glob_functable[] = { {"ldap_cache_maxmem", gopt_ldap_cache_maxmem}, {"ldap_cache_seconds", gopt_ldap_cache_seconds}, {"ldap_hostports", gopt_ldap_hostports}, + {"ldap_proto_version", gopt_ldap_proto_version}, {"local_domain", gopt_local_domain}, + {"localhost_address", gopt_localhost_address}, {"log_file", gopt_log_file}, {"log_options", gopt_log_options}, + {"map_defaults", gopt_map_defaults}, {"map_options", gopt_map_options}, + {"map_reload_interval", gopt_map_reload_interval}, {"map_type", gopt_map_type}, {"mount_type", gopt_mount_type}, {"pid_file", gopt_pid_file}, {"portmap_program", gopt_portmap_program}, + {"preferred_amq_port", gopt_preferred_amq_port}, + {"nfs_allow_any_interface", gopt_nfs_allow_any_interface}, {"nfs_allow_insecure_port", gopt_nfs_allow_insecure_port}, {"nfs_proto", gopt_nfs_proto}, {"nfs_retransmit_counter", gopt_nfs_retransmit_counter}, + {"nfs_retransmit_counter_udp", gopt_nfs_retransmit_counter_udp}, + {"nfs_retransmit_counter_tcp", gopt_nfs_retransmit_counter_tcp}, + {"nfs_retransmit_counter_toplvl", gopt_nfs_retransmit_counter_toplvl}, {"nfs_retry_interval", gopt_nfs_retry_interval}, + {"nfs_retry_interval_udp", gopt_nfs_retry_interval_udp}, + {"nfs_retry_interval_tcp", gopt_nfs_retry_interval_tcp}, + {"nfs_retry_interval_toplvl", gopt_nfs_retry_interval_toplvl}, {"nfs_vers", gopt_nfs_vers}, {"nis_domain", gopt_nis_domain}, {"normalize_hostnames", gopt_normalize_hostnames}, + {"normalize_slashes", gopt_normalize_slashes}, {"os", gopt_os}, {"osver", gopt_osver}, {"plock", gopt_plock}, @@ -174,61 +215,43 @@ static struct _func_map glob_functable[] = { {"selectors_on_default", gopt_selectors_in_defaults}, {"selectors_in_defaults", gopt_selectors_in_defaults}, {"show_statfs_entries", gopt_show_statfs_entries}, + {"truncate_log", gopt_truncate_log}, {"unmount_on_exit", gopt_unmount_on_exit}, + {"use_tcpwrappers", gopt_use_tcpwrappers}, {"vendor", gopt_vendor}, {NULL, NULL} }; /* - * Reset a map. + * Initialize a map from [global] defaults. */ static void -reset_cf_map(cf_map_t *cfm) +init_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 + * 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! + * (2): I'm assigning pointers directly from the global map. */ /* 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_defaults from [global] */ + cfm->cfm_defaults = gopt.map_defaults; + /* 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; /* @@ -242,7 +265,7 @@ reset_cf_map(cf_map_t *cfm) /* - * Process configuration file options. + * Process configuration file options (called from YACC parser). * Return 0 if OK, 1 otherwise. */ int @@ -251,50 +274,67 @@ 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", + 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 global section, process kv pairs one at a time. */ if (STREQ(section, "global")) { /* * Check if a regular map was configured before "global", - * and process it as needed. + * and warn about it. */ - 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; + if (cur_map && cur_map->cfm_dir) { + static short printed_this_error; + if (!printed_this_error) { + fprintf(stderr, "found regular map \"%s\" before global one.\n", + cur_map->cfm_dir); + printed_this_error = 1; + } } /* process the global option first */ ret = process_global_option(key, val); - /* 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. + * Otherwise we found a non-global option: store it after some testing. */ - /* 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; + /* initialize (static) global list head and current map pointer */ + if (!head_map && !cur_map) { + cur_map = CALLOC(cf_map_t); + if (!cur_map) { + perror("calloc"); + exit(1); + } + /* initialize first head map from global defaults */ + init_cf_map(cur_map); + head_map = cur_map; + } + + /* check if we found a new map, then allocate and initialize it */ + if (cur_map->cfm_dir && !STREQ(cur_map->cfm_dir, section)) { + /* allocate new map struct */ + cf_map_t *tmp_map = CALLOC(cf_map_t); + if (!tmp_map) { + perror("calloc"); + exit(1); + } + /* initialize it from global defaults */ + init_cf_map(tmp_map); + /* append it to end of linked list */ + cur_map->cfm_next = tmp_map; + cur_map = tmp_map; } /* now process a single entry of a regular map */ - return process_regular_option(section, key, val, &cur_map); + return process_regular_option(section, key, val, cur_map); } @@ -332,6 +372,18 @@ gopt_arch(const char *val) static int +gopt_auto_attrcache(const char *val) +{ + gopt.auto_attrcache = atoi(val); + if (gopt.auto_attrcache < 0) { + fprintf(stderr, "conf: bad attrcache value: \"%s\"\n", val); + return 1; + } + return 0; +} + + +static int gopt_auto_dir(const char *val) { gopt.auto_dir = strdup((char *)val); @@ -340,6 +392,22 @@ gopt_auto_dir(const char *val) static int +gopt_autofs_use_lofs(const char *val) +{ + if (STREQ(val, "yes")) { + gopt.flags |= CFM_AUTOFS_USE_LOFS; + return 0; + } else if (STREQ(val, "no")) { + gopt.flags &= ~CFM_AUTOFS_USE_LOFS; + return 0; + } + + fprintf(stderr, "conf: unknown value to autofs_use_lofs \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int gopt_browsable_dirs(const char *val) { if (STREQ(val, "full")) { @@ -377,10 +445,18 @@ gopt_cluster(const char *val) static int +gopt_debug_mtab_file(const char *val) +{ + gopt.debug_mtab_file = strdup((char*)val); + return 0; +} + + +static int gopt_debug_options(const char *val) { #ifdef DEBUG - usage += debug_option(strdup((char *)val)); + usage += debug_option((char *)val); return 0; #else /* not DEBUG */ fprintf(stderr, "%s: not compiled with DEBUG option -- sorry.\n", @@ -401,6 +477,74 @@ gopt_dismount_interval(const char *val) static int +gopt_domain_strip(const char *val) +{ + if (STREQ(val, "yes")) { + gopt.flags |= CFM_DOMAIN_STRIP; + return 0; + } else if (STREQ(val, "no")) { + gopt.flags &= ~CFM_DOMAIN_STRIP; + return 0; + } + + fprintf(stderr, "conf: unknown value to domain_strip \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int +gopt_exec_map_timeout(const char *val) +{ + gopt.exec_map_timeout = atoi(val); + if (gopt.exec_map_timeout <= 0) + gopt.exec_map_timeout = AMFS_EXEC_MAP_TIMEOUT; /* default exec map timeout */ + return 0; +} + + +static int +gopt_forced_unmounts(const char *val) +{ + if (STREQ(val, "yes")) { +#if !defined(MNT2_GEN_OPT_DETACH) && !defined(MNT2_GEN_OPT_FORCE) + fprintf(stderr, "conf: forced_unmounts unsupported on this system.\n"); + return 1; +#else /* defined(MNT2_GEN_OPT_DETACH) || defined(MNT2_GEN_OPT_FORCE) */ +# ifdef __linux__ + /* + * HACK ALERT: Linux has had MNT_FORCE since 2.2, but it hasn't gotten + * stable until 2.4. And it had MNT_DETACH since 2.4, but it hasn't + * gotten stable since 2.6. So alert users if they're trying to use a + * feature that may not work well on their older kernel. + */ + { + struct utsname un; + if (uname(&un) >= 0) { +# ifdef MNT2_GEN_OPT_FORCE + if (strcmp(un.release, "2.4.0") < 0) + fprintf(stderr, "warning: forced-unmounts (MNT_FORCE) may not work well before 2.4.0\n"); +# endif /* MNT2_GEN_OPT_FORCE */ +# ifdef MNT2_GEN_OPT_DETACH + if (strcmp(un.release, "2.6.0") < 0) + fprintf(stderr, "warning: lazy-unmounts (MNT_DETACH) may not work well before 2.6.0\n"); +# endif /* MNT2_GEN_OPT_DETACH */ + } + } +# endif /* __linux__ */ + gopt.flags |= CFM_FORCED_UNMOUNTS; + return 0; +#endif /* defined(MNT2_GEN_OPT_DETACH) || defined(MNT2_GEN_OPT_FORCE) */ + } else if (STREQ(val, "no")) { + gopt.flags &= ~CFM_FORCED_UNMOUNTS; + return 0; + } + + fprintf(stderr, "conf: unknown value to unmount_on_exit \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int gopt_full_os(const char *val) { gopt.op_sys_full = strdup((char *)val); @@ -462,6 +606,14 @@ gopt_local_domain(const char *val) static int +gopt_localhost_address(const char *val) +{ + gopt.localhost_address = strdup((char *)val); + return 0; +} + + +static int gopt_ldap_base(const char *val) { #ifdef HAVE_MAP_LDAP @@ -527,6 +679,44 @@ gopt_ldap_hostports(const char *val) static int +gopt_ldap_proto_version(const char *val) +{ +#ifdef HAVE_MAP_LDAP + char *end; + + gopt.ldap_proto_version = strtol((char *)val, &end, 10); + if (end == val) { + fprintf(stderr, "conf: bad ldap_proto_version option: %s\n",val); + return 1; + } + + if (gopt.ldap_proto_version < 0 || gopt.ldap_proto_version > LDAP_VERSION_MAX) { + fprintf(stderr, "conf: bad ldap_proto_version option value: %s\n",val); + return 1; + } + switch (gopt.ldap_proto_version) { + /* XXX: what about LDAP_VERSION1? */ + case LDAP_VERSION2: +#ifdef LDAP_VERSION3 + case LDAP_VERSION3: +#endif /* LDAP_VERSION3 */ +#ifdef LDAP_VERSION4 + case LDAP_VERSION4: +#endif /* LDAP_VERSION4 */ + break; + default: + fprintf(stderr, "conf: unsupported ldap_proto_version option value: %s\n",val); + return 1; + } + return 0; +#else /* not HAVE_MAP_LDAP */ + fprintf(stderr, "conf: ldap_proto_version option ignored. No LDAP support available.\n"); + return 1; +#endif /* not HAVE_MAP_LDAP */ +} + + +static int gopt_log_file(const char *val) { gopt.logfile = strdup((char *)val); @@ -537,7 +727,15 @@ gopt_log_file(const char *val) static int gopt_log_options(const char *val) { - usage += switch_option(strdup((char *)val)); + usage += switch_option((char *)val); + return 0; +} + + +static int +gopt_map_defaults(const char *val) +{ + gopt.map_defaults = strdup((char *)val); return 0; } @@ -551,6 +749,16 @@ gopt_map_options(const char *val) static int +gopt_map_reload_interval(const char *val) +{ + gopt.map_reload_interval = atoi(val); + if (gopt.map_reload_interval <= 0) + gopt.map_reload_interval = ONE_HOUR; + return 0; +} + + +static int gopt_map_type(const char *val) { /* check if map type exist */ @@ -567,9 +775,14 @@ static int gopt_mount_type(const char *val) { if (STREQ(val, "autofs")) { - fprintf(stderr, "conf: no autofs support available, turning it off\n"); - gopt.flags &= ~CFM_MOUNT_TYPE_AUTOFS; +#ifdef HAVE_FS_AUTOFS + gopt.flags |= CFM_MOUNT_TYPE_AUTOFS; + amd_use_autofs++; return 0; +#else /* not HAVE_FS_AUTOFS */ + fprintf(stderr, "conf: no autofs support available\n"); + return 1; +#endif /* not HAVE_FS_AUTOFS */ } else if (STREQ(val, "nfs")) { gopt.flags &= ~CFM_MOUNT_TYPE_AUTOFS; return 0; @@ -583,7 +796,7 @@ gopt_mount_type(const char *val) static int gopt_portmap_program(const char *val) { - gopt.portmap_program = atoi(val); + gopt.portmap_program = atol(val); /* * allow alternate program numbers to be no more than 10 offset from * official amd program number (300019). @@ -602,6 +815,35 @@ gopt_portmap_program(const char *val) static int +gopt_preferred_amq_port(const char *val) +{ + gopt.preferred_amq_port = atoi(val); + + /* + * No need to check value: preferred_amq_port is an unsigned short and 0 + * is a valid number, meaning "any port". + */ + return 0; /* all is OK */ +} + + +static int +gopt_nfs_allow_any_interface(const char *val) +{ + if (STREQ(val, "yes")) { + gopt.flags |= CFM_NFS_ANY_INTERFACE; + return 0; + } else if (STREQ(val, "no")) { + gopt.flags &= ~CFM_NFS_ANY_INTERFACE; + return 0; + } + + fprintf(stderr, "conf: unknown value to nfs_allow_insecure_port \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int gopt_nfs_allow_insecure_port(const char *val) { if (STREQ(val, "yes")) { @@ -632,7 +874,34 @@ gopt_nfs_proto(const char *val) static int gopt_nfs_retransmit_counter(const char *val) { - gopt.amfs_auto_retrans = atoi(val); + int i; + + for (i=0; i<AMU_TYPE_MAX; ++i) + gopt.amfs_auto_retrans[i] = atoi(val); + return 0; +} + + +static int +gopt_nfs_retransmit_counter_udp(const char *val) +{ + gopt.amfs_auto_retrans[AMU_TYPE_UDP] = atoi(val); + return 0; +} + + +static int +gopt_nfs_retransmit_counter_tcp(const char *val) +{ + gopt.amfs_auto_retrans[AMU_TYPE_TCP] = atoi(val); + return 0; +} + + +static int +gopt_nfs_retransmit_counter_toplvl(const char *val) +{ + gopt.amfs_auto_retrans[AMU_TYPE_TOPLVL] = atoi(val); return 0; } @@ -640,7 +909,34 @@ gopt_nfs_retransmit_counter(const char *val) static int gopt_nfs_retry_interval(const char *val) { - gopt.amfs_auto_timeo = atoi(val); + int i; + + for (i=0; i<AMU_TYPE_MAX; ++i) + gopt.amfs_auto_timeo[i] = atoi(val); + return 0; +} + + +static int +gopt_nfs_retry_interval_udp(const char *val) +{ + gopt.amfs_auto_timeo[AMU_TYPE_UDP] = atoi(val); + return 0; +} + + +static int +gopt_nfs_retry_interval_tcp(const char *val) +{ + gopt.amfs_auto_timeo[AMU_TYPE_TCP] = atoi(val); + return 0; +} + + +static int +gopt_nfs_retry_interval_toplvl(const char *val) +{ + gopt.amfs_auto_timeo[AMU_TYPE_TOPLVL] = atoi(val); return 0; } @@ -689,6 +985,22 @@ gopt_normalize_hostnames(const char *val) static int +gopt_normalize_slashes(const char *val) +{ + if (STREQ(val, "yes")) { + gopt.flags |= CFM_NORMALIZE_SLASHES; + return 0; + } else if (STREQ(val, "no")) { + gopt.flags &= ~CFM_NORMALIZE_SLASHES; + return 0; + } + + fprintf(stderr, "conf: unknown value to normalize_slashes \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int gopt_os(const char *val) { gopt.op_sys = strdup((char *)val); @@ -740,7 +1052,9 @@ static int gopt_print_version(const char *val) { if (STREQ(val, "yes")) { - fputs(get_version_string(), stderr); + char *vers = get_version_string(); + fputs(vers, stderr); + XFREE(vers); return 0; } else if (STREQ(val, "no")) { return 0; @@ -808,6 +1122,22 @@ gopt_show_statfs_entries(const char *val) static int +gopt_truncate_log(const char *val) +{ + if (STREQ(val, "yes")) { + gopt.flags |= CFM_TRUNCATE_LOG; + return 0; + } else if (STREQ(val, "no")) { + gopt.flags &= ~CFM_TRUNCATE_LOG; + return 0; + } + + fprintf(stderr, "conf: unknown value to truncate_log \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int gopt_unmount_on_exit(const char *val) { if (STREQ(val, "yes")) { @@ -824,6 +1154,27 @@ gopt_unmount_on_exit(const char *val) static int +gopt_use_tcpwrappers(const char *val) +{ +#if defined(HAVE_TCPD_H) && defined(HAVE_LIBWRAP) + if (STREQ(val, "yes")) { + gopt.flags |= CFM_USE_TCPWRAPPERS; + return 0; + } else if (STREQ(val, "no")) { + gopt.flags &= ~CFM_USE_TCPWRAPPERS; + return 0; + } +#else /* not defined(HAVE_TCPD_H) && defined(HAVE_LIBWRAP) */ + fprintf(stderr, "conf: no tcpd/libwrap support available\n"); + return 1; +#endif /* not defined(HAVE_TCPD_H) && defined(HAVE_LIBWRAP) */ + + fprintf(stderr, "conf: unknown value to use_tcpwrappers \"%s\"\n", val); + return 1; /* unknown value */ +} + + +static int gopt_vendor(const char *val) { gopt.op_sys_vendor = strdup((char *)val); @@ -857,6 +1208,9 @@ process_regular_option(const char *section, const char *key, const char *val, cf if (STREQ(key, "map_name")) return ropt_map_name(val, cfm); + if (STREQ(key, "map_defaults")) + return ropt_map_defaults(val, cfm); + if (STREQ(key, "map_options")) return ropt_map_options(val, cfm); @@ -906,6 +1260,14 @@ ropt_map_name(const char *val, cf_map_t *cfm) static int +ropt_map_defaults(const char *val, cf_map_t *cfm) +{ + cfm->cfm_defaults = strdup((char *)val); + return 0; +} + + +static int ropt_map_options(const char *val, cf_map_t *cfm) { cfm->cfm_opts = strdup((char *)val); @@ -930,9 +1292,14 @@ static int ropt_mount_type(const char *val, cf_map_t *cfm) { if (STREQ(val, "autofs")) { - fprintf(stderr, "conf: no autofs support available, turning it off\n"); - cfm->cfm_flags &= ~CFM_MOUNT_TYPE_AUTOFS; +#ifdef HAVE_FS_AUTOFS + cfm->cfm_flags |= CFM_MOUNT_TYPE_AUTOFS; + amd_use_autofs++; return 0; +#else /* not HAVE_FS_AUTOFS */ + fprintf(stderr, "conf: no autofs support available\n"); + return 1; +#endif /* not HAVE_FS_AUTOFS */ } else if (STREQ(val, "nfs")) { cfm->cfm_flags &= ~CFM_MOUNT_TYPE_AUTOFS; return 0; @@ -963,9 +1330,8 @@ ropt_tag(const char *val, cf_map_t *cfm) * Process one collected map. */ static int -process_regular_map(cf_map_t *cfm) +process_one_regular_map(const cf_map_t *cfm) { - if (!cfm->cfm_name) { fprintf(stderr, "conf: map_name must be defined for map \"%s\"\n", cfm->cfm_dir); return 1; @@ -989,22 +1355,52 @@ process_regular_map(cf_map_t *cfm) fprintf(stderr, "skipping map %s...\n", cfm->cfm_dir); } - reset_cf_map(cfm); return 0; } /* - * Process last map in conf file (if any) + * Process all regular maps in conf file (if any) */ int -process_last_regular_map(void) +process_all_regular_maps(void) { + cf_map_t *tmp_map = head_map; + /* * If the amd.conf file only has a [global] section (pretty useless - * IMHO), do not try to process a map that does not exist. + * IMHO), there's nothing to process */ - if (!cur_map.cfm_dir) + if (!tmp_map) return 0; - return process_regular_map(&cur_map); + + while (tmp_map) { + if (process_one_regular_map(tmp_map) != 0) + return 1; + tmp_map = tmp_map->cfm_next; + } + return 0; +} + + +/* + * Find a cf_map_t for a given map name. + * Return NULL if not found. + */ +cf_map_t * +find_cf_map(const char *name) +{ + + cf_map_t *tmp_map = head_map; + + if (!tmp_map || !name) + return NULL; + + while (tmp_map) { + if (STREQ(tmp_map->cfm_dir,name)) { + return tmp_map; + } + tmp_map = tmp_map->cfm_next; + } + return NULL; } diff --git a/contrib/amd/amd/conf_parse.y b/contrib/amd/amd/conf_parse.y index 992d321..b397b05 100644 --- a/contrib/amd/amd/conf_parse.y +++ b/contrib/amd/amd/conf_parse.y @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1989 Jan-Simon Pendry * Copyright (c) 1989 Imperial College of Science, Technology & Medicine * Copyright (c) 1989 The Regents of the University of California. @@ -37,7 +37,7 @@ * SUCH DAMAGE. * * - * $Id: conf_parse.y,v 1.4.2.5 2004/05/12 15:54:31 ezk Exp $ + * File: am-utils/amd/conf_parse.y * */ diff --git a/contrib/amd/amd/conf_tok.l b/contrib/amd/amd/conf_tok.l index 2216993..b572e4d 100644 --- a/contrib/amd/amd/conf_tok.l +++ b/contrib/amd/amd/conf_tok.l @@ -1,6 +1,6 @@ %{ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1989 Jan-Simon Pendry * Copyright (c) 1989 Imperial College of Science, Technology & Medicine * Copyright (c) 1989 The Regents of the University of California. @@ -38,7 +38,7 @@ * SUCH DAMAGE. * * - * $Id: conf_tok.l,v 1.3.2.5 2004/05/12 15:54:31 ezk Exp $ + * File: am-utils/amd/conf_tok.l * */ diff --git a/contrib/amd/amd/get_args.c b/contrib/amd/amd/get_args.c index df85683..12a7d07 100644 --- a/contrib/amd/amd/get_args.c +++ b/contrib/amd/amd/get_args.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: get_args.c,v 1.7.2.6 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/get_args.c * */ @@ -55,17 +54,12 @@ /* include auto-generated version file */ #include <build_version.h> -char *conf_file = "/etc/amd.conf"; /* default amd configuration file */ +char *amu_conf_file = "/etc/amd.conf"; /* default amd configuration file */ char *conf_tag = NULL; /* default conf file tags to use */ int usage = 0; int use_conf_file = 0; /* default don't use amd.conf file */ char *mnttab_file_name = NULL; /* symbol must be available always */ -#if 0 -#ifdef DEBUG -int debug_flags = D_AMQ /* Register AMQ */ - | D_DAEMON; /* Enter daemon mode */ -#endif /* DEBUG */ -#endif + /* * Return the version string (dynamic buffer) @@ -73,48 +67,61 @@ int debug_flags = D_AMQ /* Register AMQ */ char * get_version_string(void) { - static char *vers = NULL; + char *vers = NULL; char tmpbuf[1024]; char *wire_buf; int wire_buf_len = 0; + size_t len; /* max allocated length (to avoid buf overflow) */ - /* first get dynamic string listing all known networks */ + /* + * First get dynamic string listing all known networks. + * This could be a long list, if host has lots of interfaces. + */ wire_buf = print_wires(); if (wire_buf) wire_buf_len = strlen(wire_buf); - vers = xmalloc(2048 + wire_buf_len); - sprintf(vers, "%s\n%s\n%s\n%s\n", - "Copyright (c) 1997-2004 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", - gopt.op_sys_full, gopt.op_sys, gopt.op_sys_ver, gopt.op_sys_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); + len = 2048 + wire_buf_len; + vers = xmalloc(len); + xsnprintf(vers, len, "%s\n%s\n%s\n%s\n", + "Copyright (c) 1997-2006 Erez Zadok", + "Copyright (c) 1990 Jan-Simon Pendry", + "Copyright (c) 1990 Imperial College of Science, Technology & Medicine", + "Copyright (c) 1990 The Regents of the University of California."); + xsnprintf(tmpbuf, sizeof(tmpbuf), "%s version %s (build %d).\n", + PACKAGE_NAME, PACKAGE_VERSION, AMU_BUILD_VERSION); + strlcat(vers, tmpbuf, len); + xsnprintf(tmpbuf, sizeof(tmpbuf), "Report bugs to %s.\n", PACKAGE_BUGREPORT); + strlcat(vers, tmpbuf, len); + xsnprintf(tmpbuf, sizeof(tmpbuf), "Configured by %s@%s on date %s.\n", + USER_NAME, HOST_NAME, CONFIG_DATE); + strlcat(vers, tmpbuf, len); + xsnprintf(tmpbuf, sizeof(tmpbuf), "Built by %s@%s on date %s.\n", + BUILD_USER, BUILD_HOST, BUILD_DATE); + strlcat(vers, tmpbuf, len); + xsnprintf(tmpbuf, sizeof(tmpbuf), "cpu=%s (%s-endian), arch=%s, karch=%s.\n", + cpu, endian, gopt.arch, gopt.karch); + strlcat(vers, tmpbuf, len); + xsnprintf(tmpbuf, sizeof(tmpbuf), "full_os=%s, os=%s, osver=%s, vendor=%s, distro=%s.\n", + gopt.op_sys_full, gopt.op_sys, gopt.op_sys_ver, gopt.op_sys_vendor, DISTRO_NAME); + strlcat(vers, tmpbuf, len); + xsnprintf(tmpbuf, sizeof(tmpbuf), "domain=%s, host=%s, hostd=%s.\n", + hostdomain, am_get_hostname(), hostd); + strlcat(vers, tmpbuf, len); + + strlcat(vers, "Map support for: ", len); + mapc_showtypes(tmpbuf, sizeof(tmpbuf)); + strlcat(vers, tmpbuf, len); + strlcat(vers, ".\nAMFS: ", len); + ops_showamfstypes(tmpbuf, sizeof(tmpbuf)); + strlcat(vers, tmpbuf, len); + strlcat(vers, ", inherit.\nFS: ", len); /* hack: "show" that we support type:=inherit */ + ops_showfstypes(tmpbuf, sizeof(tmpbuf)); + strlcat(vers, tmpbuf, len); /* append list of networks if available */ if (wire_buf) { - strcat(vers, wire_buf); + strlcat(vers, wire_buf, len); XFREE(wire_buf); } @@ -122,13 +129,48 @@ get_version_string(void) } +static void +show_usage(void) +{ + fprintf(stderr, + "Usage: %s [-nprvHS] [-a mount_point] [-c cache_time] [-d domain]\n\ +\t[-k kernel_arch] [-l logfile%s\n\ +\t[-t timeout.retrans] [-w wait_timeout] [-A arch] [-C cluster_name]\n\ +\t[-o op_sys_ver] [-O op_sys_name]\n\ +\t[-F conf_file] [-T conf_tag]", am_get_progname(), +#ifdef HAVE_SYSLOG +# ifdef LOG_DAEMON + "|\"syslog[:facility]\"]" +# else /* not LOG_DAEMON */ + "|\"syslog\"]" +# endif /* not LOG_DAEMON */ +#else /* not HAVE_SYSLOG */ + "]" +#endif /* not HAVE_SYSLOG */ + ); + +#ifdef HAVE_MAP_NIS + fputs(" [-y nis-domain]\n", stderr); +#else /* not HAVE_MAP_NIS */ + fputc('\n', stderr); +#endif /* HAVE_MAP_NIS */ + + show_opts('x', xlog_opt); +#ifdef DEBUG + show_opts('D', dbg_opt); +#endif /* DEBUG */ + fprintf(stderr, "\t[directory mapname [-map_options]] ...\n"); +} + + void get_args(int argc, char *argv[]) { - int opt_ch; + int opt_ch, i; FILE *fp = stdin; - char getopt_arguments[] = "+nprvSa:c:d:k:l:o:t:w:x:y:C:D:F:T:O:H"; + char getopt_arguments[] = "+nprvSa:c:d:k:l:o:t:w:x:y:C:D:F:T:O:HA:"; char *getopt_args; + int print_version = 0; /* 1 means we should print version info */ #ifdef HAVE_GNU_GETOPT getopt_args = getopt_arguments; @@ -187,24 +229,30 @@ get_args(int argc, char *argv[]) break; case 't': - /* timeo.retrans */ + /* timeo.retrans (also affects toplvl mounts) */ { char *dot = strchr(optarg, '.'); + int i; if (dot) *dot = '\0'; if (*optarg) { - gopt.amfs_auto_timeo = atoi(optarg); + for (i=0; i<AMU_TYPE_MAX; ++i) + gopt.amfs_auto_timeo[i] = atoi(optarg); } if (dot) { - gopt.amfs_auto_retrans = atoi(dot + 1); + for (i=0; i<AMU_TYPE_MAX; ++i) + gopt.amfs_auto_retrans[i] = atoi(dot + 1); *dot = '.'; } } break; case 'v': - fputs(get_version_string(), stderr); - exit(0); + /* + * defer to print version info after every variable had been + * initialized. + */ + print_version++; break; case 'w': @@ -225,6 +273,10 @@ get_args(int argc, char *argv[]) #endif /* not HAVE_MAP_NIS */ break; + case 'A': + gopt.arch = optarg; + break; + case 'C': gopt.cluster = optarg; break; @@ -239,12 +291,13 @@ get_args(int argc, char *argv[]) break; case 'F': - conf_file = optarg; + amu_conf_file = optarg; use_conf_file = 1; break; case 'H': - goto show_usage; + show_usage(); + exit(1); break; case 'O': @@ -269,18 +322,19 @@ get_args(int argc, char *argv[]) * 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 (use_conf_file && amu_conf_file) { + fp = fopen(amu_conf_file, "r"); if (!fp) { char buf[128]; - sprintf(buf, "Amd configuration file (%s)", conf_file); + xsnprintf(buf, sizeof(buf), "Amd configuration file (%s)", + amu_conf_file); perror(buf); exit(1); } yyin = fp; yyparse(); fclose(fp); - if (process_last_regular_map() != 0) + if (process_all_regular_maps() != 0) exit(1); } @@ -293,8 +347,8 @@ get_args(int argc, char *argv[]) #endif /* DEBUG */ /* log information regarding amd.conf file */ - if (use_conf_file && conf_file) - plog(XLOG_INFO, "using configuration file %s", conf_file); + if (use_conf_file && amu_conf_file) + plog(XLOG_INFO, "using configuration file %s", amu_conf_file); #ifdef HAVE_MAP_LDAP /* ensure that if ldap_base is specified, that also ldap_hostports is */ @@ -304,8 +358,10 @@ get_args(int argc, char *argv[]) } #endif /* HAVE_MAP_LDAP */ - if (usage) - goto show_usage; + if (usage) { + show_usage(); + exit(1); + } while (optind <= argc - 2) { char *dir = argv[optind++]; @@ -327,76 +383,55 @@ get_args(int argc, char *argv[]) hostdomain = gopt.sub_domain; if (*hostdomain == '.') hostdomain++; - strcat(hostd, "."); - strcat(hostd, hostdomain); + xstrlcat(hostd, ".", sizeof(hostd)); + xstrlcat(hostd, hostdomain, sizeof(hostd)); #ifdef MOUNT_TABLE_ON_FILE -# ifdef DEBUG - if (debug_flags & D_MTAB) - mnttab_file_name = DEBUG_MNTTAB; + if (amuDebug(D_MTAB)) + if (gopt.debug_mtab_file) + mnttab_file_name = gopt.debug_mtab_file; /* user supplied debug mtab path */ + else + mnttab_file_name = DEBUG_MNTTAB_FILE; /* default debug mtab path */ else -# endif /* DEBUG */ mnttab_file_name = MNTTAB_FILE_NAME; #else /* not MOUNT_TABLE_ON_FILE */ -# ifdef DEBUG - if (debug_flags & D_MTAB) + if (amuDebug(D_MTAB)) dlog("-D mtab option ignored"); -# endif /* DEBUG */ # ifdef MNTTAB_FILE_NAME mnttab_file_name = MNTTAB_FILE_NAME; # endif /* MNTTAB_FILE_NAME */ #endif /* not MOUNT_TABLE_ON_FILE */ - if (switch_to_logfile(gopt.logfile, orig_umask) != 0) - plog(XLOG_USER, "Cannot switch logfile"); - /* * If the kernel architecture was not specified * then use the machine architecture. */ - if (gopt.karch == 0) + if (gopt.karch == NULL) gopt.karch = gopt.arch; - if (gopt.cluster == 0) + if (gopt.cluster == NULL) 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; + /* sanity checking, normalize values just in case (toplvl too) */ + for (i=0; i<AMU_TYPE_MAX; ++i) { + if (gopt.amfs_auto_timeo[i] == 0) + gopt.amfs_auto_timeo[i] = AMFS_AUTO_TIMEO; + if (gopt.amfs_auto_retrans[i] == 0) + gopt.amfs_auto_retrans[i] = AMFS_AUTO_RETRANS(i); + if (gopt.amfs_auto_retrans[i] == 0) + gopt.amfs_auto_retrans[i] = 3; /* under very unusual circumstances, could be zero */ + } } -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]", am_get_progname(), -#ifdef HAVE_SYSLOG -# ifdef LOG_DAEMON - "|\"syslog[:facility]\"]" -# else /* not LOG_DAEMON */ - "|\"syslog\"]" -# endif /* not LOG_DAEMON */ -#else /* not HAVE_SYSLOG */ - "]" -#endif /* not HAVE_SYSLOG */ - ); + /* finally print version string and exit, if asked for */ + if (print_version) { + fputs(get_version_string(), stderr); + exit(0); + } -#ifdef HAVE_MAP_NIS - fputs(" [-y nis-domain]\n", stderr); -#else /* not HAVE_MAP_NIS */ - fputc('\n', stderr); -#endif /* HAVE_MAP_NIS */ + if (switch_to_logfile(gopt.logfile, orig_umask, + (gopt.flags & CFM_TRUNCATE_LOG)) != 0) + plog(XLOG_USER, "Cannot switch logfile"); - show_opts('x', xlog_opt); -#ifdef DEBUG - show_opts('D', dbg_opt); -#endif /* DEBUG */ - fprintf(stderr, "\t[directory mapname [-map_options]] ...\n"); - exit(1); + return; } diff --git a/contrib/amd/amd/info_exec.c b/contrib/amd/amd/info_exec.c new file mode 100644 index 0000000..8ccab9a --- /dev/null +++ b/contrib/amd/amd/info_exec.c @@ -0,0 +1,423 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/info_exec.c + * + */ + +/* + * Get info from executable map + * + * Original from Erik Kline, 2004. + */ + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + +#define MAX_LINE_LEN 1500 + +/* forward declarations */ +int exec_init(mnt_map *m, char *map, time_t *tp); +int exec_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp); + + +/* + * a timed fgets() + */ +static char * +fgets_timed(char *s, int size, int rdfd, int secs) +{ + fd_set fds; + struct timeval timeo; + time_t start, now; + int rval=0, i=0; + + if (!s || size < 0 || rdfd < 0) + return 0; + + s[0] = 0; + if (size == 0) + return s; + + start = clocktime(NULL); + while (s[i] != '\n' && i < size-1) { + s[i+1] = 0; /* places the requisite trailing '\0' */ + + /* ready for reading */ + rval = read(rdfd, (void *)(s+i), 1); + if (rval == 1) { + if (s[i] == 0) { + rval = 0; + break; + } + i++; + continue; + } else if (rval == 0) { + break; + } else if (rval < 0 && errno != EAGAIN && errno != EINTR) { + plog(XLOG_WARNING, "fgets_timed read error: %m"); + break; + } + + timeo.tv_usec = 0; + now = clocktime(NULL) - start; + if (secs <= 0) + timeo.tv_sec = 0; + else if (now < secs) + timeo.tv_sec = secs - now; + else { + /* timed out (now>=secs) */ + plog(XLOG_WARNING, "executable map read timed out (> %d secs)", secs); + rval = -1; + break; + } + + FD_ZERO(&fds); + FD_SET(rdfd, &fds); + + rval = select(rdfd+1, &fds, 0, 0, &timeo); + if (rval < 0) { + /* error selecting */ + plog(XLOG_WARNING, "fgets_timed select error: %m"); + if (errno == EINTR) + continue; + rval = -1; + break; + } else if (rval == 0) { + /* timed out */ + plog(XLOG_WARNING, "executable map read timed out (> %d secs)", secs); + rval = -1; + break; + } + } + + if (rval > 0) + return s; + + close(rdfd); + return (rval == 0 ? s : 0); +} + + +static int +read_line(char *buf, int size, int fd) +{ + int done = 0; + + while (fgets_timed(buf, size, fd, gopt.exec_map_timeout)) { + int len = strlen(buf); + done += len; + if (len > 1 && buf[len - 2] == '\\' && + buf[len - 1] == '\n') { + buf += len - 2; + size -= len - 2; + *buf = '\n'; + buf[1] = '\0'; + } else { + return done; + } + } + + return done; +} + + +/* + * Try to locate a value in a query answer + */ +static int +exec_parse_qanswer(int fd, char *map, char *key, char **pval, time_t *tp) +{ + char qanswer[MAX_LINE_LEN], *dc = 0; + int chuck = 0; + int line_no = 0; + + while (read_line(qanswer, sizeof(qanswer), fd)) { + char *cp; + char *hash; + int len = strlen(qanswer); + line_no++; + + /* + * Make sure we got the whole line + */ + if (qanswer[len - 1] != '\n') { + plog(XLOG_WARNING, "line %d in \"%s\" is too long", line_no, map); + chuck = 1; + } else { + qanswer[len - 1] = '\0'; + } + + /* + * Strip comments + */ + hash = strchr(qanswer, '#'); + if (hash) + *hash = '\0'; + + /* + * Find beginning of value (query answer) + */ + for (cp = qanswer; *cp && !isascii((int)*cp) && !isspace((int)*cp); cp++) + ;; + + /* Ignore blank lines */ + if (!*cp) + goto again; + + /* + * Return a copy of the data + */ + dc = strdup(cp); + *pval = dc; + dlog("%s returns %s", key, dc); + + close(fd); + return 0; + + again: + /* + * If the last read didn't get a whole line then + * throw away the remainder before continuing... + */ + if (chuck) { + while (fgets_timed(qanswer, sizeof(qanswer), fd, gopt.exec_map_timeout) && + !strchr(qanswer, '\n')) ; + chuck = 0; + } + } + + return ENOENT; +} + + +static int +set_nonblock(int fd) +{ + int val; + + if (fd < 0) + return 0; + + if ((val = fcntl(fd, F_GETFL, 0)) < 0) { + plog(XLOG_WARNING, "set_nonblock fcntl F_GETFL error: %m"); + return 0; + } + + val |= O_NONBLOCK; + if (fcntl(fd, F_SETFL, val) < 0) { + plog(XLOG_WARNING, "set_nonblock fcntl F_SETFL error: %m"); + return 0; + } + + return 1; +} + + +static int +exec_map_open(char *emap, char *key) +{ + pid_t p1, p2; + int pdes[2], nullfd, i; + char *argv[3]; + + if (!emap) + return 0; + + argv[0] = emap; + argv[1] = key; + argv[2] = NULL; + + if ((nullfd = open("/dev/null", O_WRONLY|O_NOCTTY)) < 0) + return -1; + + if (pipe(pdes) < 0) { + close(nullfd); + return -1; + } + + switch ((p1 = vfork())) { + case -1: + /* parent: fork error */ + close(nullfd); + close(pdes[0]); + close(pdes[1]); + return -1; + case 0: + /* child #1 */ + p2 = vfork(); + switch (p2) { + case -1: + /* child #1: fork error */ + exit(errno); + case 0: + /* child #2: init will reap our status */ + if (pdes[1] != STDOUT_FILENO) { + dup2(pdes[1], STDOUT_FILENO); + close(pdes[1]); + } + + if (nullfd != STDERR_FILENO) { + dup2(nullfd, STDERR_FILENO); + close(nullfd); + } + + for (i=0; i<FD_SETSIZE; i++) + if (i != STDOUT_FILENO && i != STDERR_FILENO) + close(i); + + /* make the write descriptor non-blocking */ + if (!set_nonblock(STDOUT_FILENO)) { + close(STDOUT_FILENO); + exit(-1); + } + + execve(emap, argv, NULL); + exit(errno); /* in case execve failed */ + } + + /* child #1 */ + exit(0); + } + + /* parent */ + close(nullfd); + close(pdes[1]); + + /* anti-zombie insurance */ + while (waitpid(p1,0,0) < 0) + if (errno != EINTR) + exit(errno); + + /* make the read descriptor non-blocking */ + if (!set_nonblock(pdes[0])) { + close(pdes[0]); + return -1; + } + + return pdes[0]; +} + + +/* + * Check for various permissions on executable map without trying to + * fork a new executable-map process. + * + * return: >0 (errno) if failed + * 0 if ok + */ +static int +exec_check_perm(char *map) +{ + struct stat sb; + + /* sanity and permission checks */ + if (!map) { + dlog("exec_check_permission got a NULL map"); + return EINVAL; + } + if (stat(map, &sb)) { + plog(XLOG_ERROR, "map \"%s\" stat failure: %m", map); + return errno; + } + if (!S_ISREG(sb.st_mode)) { + plog(XLOG_ERROR, "map \"%s\" should be regular file", map); + return EINVAL; + } + if (sb.st_uid != 0) { + plog(XLOG_ERROR, "map \"%s\" owned by uid %u (must be 0)", map, (u_int) sb.st_uid); + return EACCES; + } + if (!(sb.st_mode & S_IXUSR)) { + plog(XLOG_ERROR, "map \"%s\" should be executable", map); + return EACCES; + } + if (sb.st_mode & (S_ISUID|S_ISGID)) { + plog(XLOG_ERROR, "map \"%s\" should not be setuid/setgid", map); + return EACCES; + } + if (sb.st_mode & S_IWOTH) { + plog(XLOG_ERROR, "map \"%s\" should not be world writeable", map); + return EACCES; + } + + return 0; /* all is well */ +} + + +int +exec_init(mnt_map *m, char *map, time_t *tp) +{ + /* + * Basically just test that the executable map can be found + * and has proper permissions. + */ + return exec_check_perm(map); +} + + +int +exec_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp) +{ + int mapfd, ret; + + if ((ret = exec_check_perm(map)) != 0) { + return ret; + } + + if (!key) + return 0; + + if (logfp) + fflush(logfp); + dlog("exec_search \"%s\", key: \"%s\"", map, key); + mapfd = exec_map_open(map, key); + + if (mapfd >= 0) { + if (tp) + *tp = clocktime(NULL); + + return exec_parse_qanswer(mapfd, map, key, pval, tp); + } + + return errno; +} diff --git a/contrib/amd/amd/info_file.c b/contrib/amd/amd/info_file.c index 8f78868..3777a2a 100644 --- a/contrib/amd/amd/info_file.c +++ b/contrib/amd/amd/info_file.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: info_file.c,v 1.3.2.5 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/info_file.c * */ @@ -55,10 +54,9 @@ #define MAX_LINE_LEN 1500 /* forward declarations */ -int file_init(mnt_map *m, char *map, time_t *tp); +int file_init_or_mtime(mnt_map *m, char *map, time_t *tp); int file_reload(mnt_map *m, char *map, void (*fn) (mnt_map *, char *, char *)); int file_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp); -int file_mtime(mnt_map *m, char *map, time_t *tp); static int @@ -87,7 +85,7 @@ read_line(char *buf, int size, FILE *fp) return done; } } - } while (size > 0 && !feof(fp)); + } while (size > 0 && !feof(fp) && !ferror(fp)); return done; } @@ -97,7 +95,12 @@ read_line(char *buf, int size, FILE *fp) * 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 *)) +file_search_or_reload(FILE *fp, + char *map, + char *key, + char **val, + mnt_map *m, + void (*fn) (mnt_map *m, char *, char *)) { char key_val[MAX_LINE_LEN]; int chuck = 0; @@ -161,9 +164,7 @@ search_or_reload_file(FILE *fp, char *map, char *key, char **val, mnt_map *m, vo (*fn) (m, strdup(kp), dc); } else { *val = dc; -#ifdef DEBUG dlog("%s returns %s", key, dc); -#endif /* DEBUG */ } if (!fn) return 0; @@ -196,7 +197,7 @@ file_open(char *map, time_t *tp) if (mapf && tp) { struct stat stb; if (fstat(fileno(mapf), &stb) < 0) - *tp = clocktime(); + *tp = clocktime(NULL); else *tp = stb.st_mtime; } @@ -205,7 +206,7 @@ file_open(char *map, time_t *tp) int -file_init(mnt_map *m, char *map, time_t *tp) +file_init_or_mtime(mnt_map *m, char *map, time_t *tp) { FILE *mapf = file_open(map, tp); @@ -223,7 +224,7 @@ 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); + int error = file_search_or_reload(mapf, map, 0, 0, m, fn); (void) fclose(mapf); return error; } @@ -243,23 +244,10 @@ file_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp) *tp = t; error = -1; } else { - error = search_or_reload_file(mapf, map, key, pval, 0, 0); + error = file_search_or_reload(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 index fdcdd56..e345c99 100644 --- a/contrib/amd/amd/info_hesiod.c +++ b/contrib/amd/amd/info_hesiod.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1989 Jan-Simon Pendry * Copyright (c) 1989 Imperial College of Science, Technology & Medicine * Copyright (c) 1989 The Regents of the University of California. @@ -36,9 +36,8 @@ * 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.6.2.4 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/info_hesiod.c * */ @@ -75,13 +74,11 @@ int hesiod_isup(mnt_map *m, char *map); 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) + if (!hesiod_context && hesiod_init(&hesiod_context) != 0) return ENOENT; #endif /* HAVE_HESIOD_INIT */ @@ -102,22 +99,21 @@ hesiod_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp) int error; #endif /* not HAVE_HESIOD_INIT */ -#ifdef DEBUG dlog("hesiod_search(m=%lx, map=%s, key=%s, pval=%lx tp=%lx)", (unsigned long) m, map, key, (unsigned long) pval, (unsigned long) tp); -#endif /* DEBUG */ - sprintf(hes_key, "%s.%s", key, map + HES_PREFLEN); + if (key[0] == '.') + return ENOENT; + + xsnprintf(hes_key, sizeof(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) + if (amuDebug(D_INFO)) _res.options |= RES_DEBUG; -#endif /* DEBUG */ #ifdef HAVE_HESIOD_INIT /* new style hesiod */ @@ -144,9 +140,7 @@ hesiod_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp) /* * 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; @@ -161,9 +155,7 @@ hesiod_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp) error = EINVAL; break; } -# ifdef DEBUG dlog("hesiod_search: Returning: %d", error); -# endif /* DEBUG */ return error; #endif /* not HAVE_HESIOD_INIT */ } @@ -183,9 +175,7 @@ hesiod_isup(mnt_map *m, char *map) static int last_status = 1; /* assume up by default */ error = hesiod_search(m, map, "/defaults", &val, &mtime); -#ifdef DEBUG dlog("hesiod_isup(%s): %s", map, strerror(error)); -#endif /* DEBUG */ if (error != 0 && error != ENOENT) { plog(XLOG_ERROR, "hesiod_isup: error getting `/defaults' entry in map %s: %m", map); diff --git a/contrib/amd/amd/info_ldap.c b/contrib/amd/amd/info_ldap.c index 77ae9c4..36ce1c8 100644 --- a/contrib/amd/amd/info_ldap.c +++ b/contrib/amd/amd/info_ldap.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1989 Jan-Simon Pendry * Copyright (c) 1989 Imperial College of Science, Technology & Medicine * Copyright (c) 1989 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: info_ldap.c,v 1.9.2.9 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/info_ldap.c * */ @@ -48,6 +47,20 @@ * LDAP Home Page: http://www.umich.edu/~rsug/ldap/ */ +/* + * WARNING: as of Linux Fedora Core 5 (which comes with openldap-2.3.9), the + * ldap.h headers deprecate several functions used in this file, such as + * ldap_unbind. You get compile errors about missing extern definitions. + * Those externs are still in <ldap.h>, but surrounded by an ifdef + * LDAP_DEPRECATED. I am turning on that ifdef here, under the assumption + * that the functions may be deprecated, but they still work for this + * (older?) version of the LDAP API. It gets am-utils to compile, but it is + * not clear if it will work perfectly. + */ +#ifndef LDAP_DEPRECATED +# define LDAP_DEPRECATED 1 +#endif /* not LDAP_DEPRECATED */ + #ifdef HAVE_CONFIG_H # include <config.h> #endif /* HAVE_CONFIG_H */ @@ -109,7 +122,7 @@ struct he_ent { * FORWARD DECLARATIONS: */ static int amu_ldap_rebind(ALD *a); -static int get_ldap_timestamp(LDAP *ld, char *map, time_t *ts); +static int get_ldap_timestamp(ALD *a, char *map, time_t *ts); /* @@ -167,13 +180,54 @@ cr_free(CR *c) } +/* + * Special ldap_unbind function to handle SIGPIPE. + * We first ignore SIGPIPE, in case a remote LDAP server was + * restarted, then we reinstall the handler. + */ +static int +amu_ldap_unbind(LDAP *ld) +{ + int e; +#ifdef HAVE_SIGACTION + struct sigaction sa; +#else /* not HAVE_SIGACTION */ + void (*handler)(int); +#endif /* not HAVE_SIGACTION */ + + dlog("amu_ldap_unbind()\n"); + +#ifdef HAVE_SIGACTION + sa.sa_handler = SIG_IGN; + sa.sa_flags = 0; + sigemptyset(&(sa.sa_mask)); + sigaddset(&(sa.sa_mask), SIGPIPE); + sigaction(SIGPIPE, &sa, &sa); /* set IGNORE, and get old action */ +#else /* not HAVE_SIGACTION */ + handler = signal(SIGPIPE, SIG_IGN); +#endif /* not HAVE_SIGACTION */ + + e = ldap_unbind(ld); + +#ifdef HAVE_SIGACTION + sigemptyset(&(sa.sa_mask)); + sigaddset(&(sa.sa_mask), SIGPIPE); + sigaction(SIGPIPE, &sa, NULL); +#else /* not HAVE_SIGACTION */ + (void) signal(SIGPIPE, handler); +#endif /* not HAVE_SIGACTION */ + + return e; +} + + static void ald_free(ALD *a) { he_free(a->hostent); cr_free(a->credentials); if (a->ldap != NULL) - ldap_unbind(a->ldap); + amu_ldap_unbind(a->ldap); XFREE(a); } @@ -184,27 +238,29 @@ amu_ldap_init(mnt_map *m, char *map, time_t *ts) ALD *aldh; CR *creds; + dlog("-> amu_ldap_init: map <%s>\n", map); + /* * XXX: by checking that map_type must be defined, aren't we * excluding the possibility of automatic searches through all * map types? */ if (!gopt.map_type || !STREQ(gopt.map_type, AMD_LDAP_TYPE)) { - return (ENOENT); - } -#ifdef DEBUG - else { + dlog("amu_ldap_init called with map_type <%s>\n", + (gopt.map_type ? gopt.map_type : "null")); + } else { dlog("Map %s is ldap\n", map); } -#endif /* DEBUG */ aldh = ALLOC(ALD); creds = ALLOC(CR); - aldh->ldap = NULL ; + aldh->ldap = NULL; aldh->hostent = string2he(gopt.ldap_hostports); if (aldh->hostent == NULL) { plog(XLOG_USER, "Unable to parse hostport %s for ldap map %s", gopt.ldap_hostports ? gopt.ldap_hostports : "(null)", map); + XFREE(creds); + XFREE(aldh); return (ENOENT); } creds->who = ""; @@ -212,22 +268,17 @@ amu_ldap_init(mnt_map *m, char *map, time_t *ts) creds->method = LDAP_AUTH_SIMPLE; aldh->credentials = creds; aldh->timestamp = 0; -#ifdef DEBUG + aldh->ldap = NULL; 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)) + if (get_ldap_timestamp(aldh, map, ts)) return (ENOENT); -#ifdef DEBUG - dlog("Got timestamp for map %s: %ld\n", map, *ts); -#endif /* DEBUG */ + dlog("Got timestamp for map %s: %ld\n", map, (u_long) *ts); return (0); } @@ -239,16 +290,21 @@ amu_ldap_rebind(ALD *a) LDAP *ld; HE_ENT *h; CR *c = a->credentials; - time_t now = clocktime(); + time_t now = clocktime(NULL); int try; + dlog("-> amu_ldap_rebind\n"); + if (a->ldap != NULL) { if ((a->timestamp - now) > AMD_LDAP_TTL) { -#ifdef DEBUG - dlog("Reestablishing ldap connection\n"); -#endif /* DEBUG */ - ldap_unbind(a->ldap); + dlog("Re-establishing ldap connection\n"); + amu_ldap_unbind(a->ldap); a->timestamp = now; + a->ldap = NULL; + } else { + /* Assume all is OK. If it wasn't we'll be back! */ + dlog("amu_ldap_rebind: timestamp OK\n"); + return (0); } } @@ -258,21 +314,34 @@ amu_ldap_rebind(ALD *a) plog(XLOG_WARNING, "Unable to ldap_open to %s:%d\n", h->host, h->port); break; } +#if LDAP_VERSION_MAX > LDAP_VERSION2 + /* handle LDAPv3 and heigher, if available and amd.conf-igured */ + if (gopt.ldap_proto_version > LDAP_VERSION2) { + if (!ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &gopt.ldap_proto_version)) { + dlog("amu_ldap_rebind: LDAP protocol version set to %ld\n", + gopt.ldap_proto_version); + } else { + plog(XLOG_WARNING, "Unable to set ldap protocol version to %ld\n", + gopt.ldap_proto_version); + break; + } + } +#endif /* LDAP_VERSION_MAX > LDAP_VERSION2 */ if (ldap_bind_s(ld, c->who, c->pw, c->method) != LDAP_SUCCESS) { plog(XLOG_WARNING, "Unable to ldap_bind to %s:%d as %s\n", h->host, h->port, c->who); break; } if (gopt.ldap_cache_seconds > 0) { -#ifdef HAVE_LDAP_ENABLE_CACHE +#if defined(HAVE_LDAP_ENABLE_CACHE) && defined(HAVE_EXTERN_LDAP_ENABLE_CACHE) ldap_enable_cache(ld, gopt.ldap_cache_seconds, gopt.ldap_cache_maxmem); -#else /* HAVE_LDAP_ENABLE_CACHE */ - plog(XLOG_WARNING, "ldap_enable_cache(%ld) does not exist on this system!\n", gopt.ldap_cache_seconds); -#endif /* HAVE_LDAP_ENABLE_CACHE */ - a->ldap = ld; - a->timestamp = now; - return (0); +#else /* not defined(HAVE_LDAP_ENABLE_CACHE) && defined(HAVE_EXTERN_LDAP_ENABLE_CACHE) */ + plog(XLOG_WARNING, "ldap_enable_cache(%ld) is not available on this system!\n", gopt.ldap_cache_seconds); +#endif /* not defined(HAVE_LDAP_ENABLE_CACHE) && defined(HAVE_EXTERN_LDAP_ENABLE_CACHE) */ } + a->ldap = ld; + a->timestamp = now; + return (0); } plog(XLOG_WARNING, "Exhausted list of ldap servers, looping.\n"); } @@ -283,24 +352,24 @@ amu_ldap_rebind(ALD *a) static int -get_ldap_timestamp(LDAP *ld, char *map, time_t *ts) +get_ldap_timestamp(ALD *a, char *map, time_t *ts) { struct timeval tv; char **vals, *end; char filter[MAXPATHLEN]; int i, err = 0, nentries = 0; - LDAPMessage *res, *entry; + LDAPMessage *res = NULL, *entry; + + dlog("-> get_ldap_timestamp: map <%s>\n", map); tv.tv_sec = 3; tv.tv_usec = 0; - sprintf(filter, AMD_LDAP_TSFILTER, map); -#ifdef DEBUG + xsnprintf(filter, sizeof(filter), AMD_LDAP_TSFILTER, map); dlog("Getting timestamp for map %s\n", map); dlog("Filter is: %s\n", filter); dlog("Base is: %s\n", gopt.ldap_base); -#endif /* DEBUG */ for (i = 0; i < AMD_LDAP_RETRIES; i++) { - err = ldap_search_st(ld, + err = ldap_search_st(a->ldap, gopt.ldap_base, LDAP_SCOPE_SUBTREE, filter, @@ -310,19 +379,32 @@ get_ldap_timestamp(LDAP *ld, char *map, time_t *ts) &res); if (err == LDAP_SUCCESS) break; -#ifdef DEBUG - dlog("Timestamp search timed out, trying again...\n"); -#endif /* DEBUG */ + if (res) { + ldap_msgfree(res); + res = NULL; + } + plog(XLOG_USER, "Timestamp LDAP search attempt %d failed: %s\n", + i + 1, ldap_err2string(err)); + if (err != LDAP_TIMEOUT) { + dlog("get_ldap_timestamp: unbinding...\n"); + amu_ldap_unbind(a->ldap); + a->ldap = NULL; + if (amu_ldap_rebind(a)) + return (ENOENT); + } + dlog("Timestamp search failed, trying again...\n"); } if (err != LDAP_SUCCESS) { *ts = 0; plog(XLOG_USER, "LDAP timestamp search failed: %s\n", ldap_err2string(err)); + if (res) + ldap_msgfree(res); return (ENOENT); } - nentries = ldap_count_entries(ld, res); + nentries = ldap_count_entries(a->ldap, res); if (nentries == 0) { plog(XLOG_USER, "No timestamp entry for map %s\n", map); *ts = 0; @@ -330,8 +412,8 @@ get_ldap_timestamp(LDAP *ld, char *map, time_t *ts) return (ENOENT); } - entry = ldap_first_entry(ld, res); - vals = ldap_get_values(ld, entry, AMD_LDAP_TSATTR); + entry = ldap_first_entry(a->ldap, res); + vals = ldap_get_values(a->ldap, entry, AMD_LDAP_TSATTR); if (ldap_count_values(vals) == 0) { plog(XLOG_USER, "Missing timestamp value for map %s\n", map); *ts = 0; @@ -339,9 +421,7 @@ get_ldap_timestamp(LDAP *ld, char *map, time_t *ts) ldap_msgfree(res); 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); @@ -352,7 +432,7 @@ get_ldap_timestamp(LDAP *ld, char *map, time_t *ts) } if (!*ts > 0) { plog(XLOG_USER, "Nonpositive timestamp %ld for map %s\n", - *ts, map); + (u_long) *ts, map); err = ENOENT; } } else { @@ -363,9 +443,7 @@ get_ldap_timestamp(LDAP *ld, char *map, time_t *ts) ldap_value_free(vals); ldap_msgfree(res); -#ifdef DEBUG - dlog("The timestamp for %s is %ld (err=%d)\n", map, *ts, err); -#endif /* DEBUG */ + dlog("The timestamp for %s is %ld (err=%d)\n", map, (u_long) *ts, err); return (err); } @@ -373,12 +451,15 @@ get_ldap_timestamp(LDAP *ld, char *map, time_t *ts) int amu_ldap_search(mnt_map *m, char *map, char *key, char **pval, time_t *ts) { - char **vals, filter[MAXPATHLEN]; + char **vals, filter[MAXPATHLEN], filter2[2 * MAXPATHLEN]; + char *f1, *f2; struct timeval tv; int i, err = 0, nvals = 0, nentries = 0; - LDAPMessage *entry, *res; + LDAPMessage *entry, *res = NULL; ALD *a = (ALD *) (m->map_data); + dlog("-> amu_ldap_search: map <%s>, key <%s>\n", map, key); + tv.tv_sec = 2; tv.tv_usec = 0; if (a == NULL) { @@ -388,43 +469,63 @@ amu_ldap_search(mnt_map *m, char *map, char *key, char **pval, time_t *ts) 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 */ + xsnprintf(filter, sizeof(filter), AMD_LDAP_FILTER, map, key); + /* "*" is special to ldap_search(); run through the filter escaping it. */ + f1 = filter; f2 = filter2; + while (*f1) { + if (*f1 == '*') { + *f2++ = '\\'; *f2++ = '2'; *f2++ = 'a'; + f1++; + } else { + *f2++ = *f1++; + } + } + *f2 = '\0'; + dlog("Search with filter: <%s>\n", filter2); for (i = 0; i < AMD_LDAP_RETRIES; i++) { err = ldap_search_st(a->ldap, gopt.ldap_base, LDAP_SCOPE_SUBTREE, - filter, + filter2, 0, 0, &tv, &res); if (err == LDAP_SUCCESS) break; + if (res) { + ldap_msgfree(res); + res = NULL; + } + plog(XLOG_USER, "LDAP search attempt %d failed: %s\n", + i + 1, ldap_err2string(err)); + if (err != LDAP_TIMEOUT) { + dlog("amu_ldap_search: unbinding...\n"); + amu_ldap_unbind(a->ldap); + a->ldap = NULL; + if (amu_ldap_rebind(a)) + return (ENOENT); + } } switch (err) { case LDAP_SUCCESS: break; case LDAP_NO_SUCH_OBJECT: -#ifdef DEBUG dlog("No object\n"); -#endif /* DEBUG */ - ldap_msgfree(res); + if (res) + ldap_msgfree(res); return (ENOENT); default: plog(XLOG_USER, "LDAP search failed: %s\n", ldap_err2string(err)); - ldap_msgfree(res); + if (res) + 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); @@ -438,9 +539,7 @@ amu_ldap_search(mnt_map *m, char *map, char *key, char **pval, time_t *ts) ldap_msgfree(res); 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; @@ -461,15 +560,13 @@ amu_ldap_mtime(mnt_map *m, char *map, time_t *ts) ALD *aldh = (ALD *) (m->map_data); if (aldh == NULL) { -#ifdef DEBUG dlog("LDAP panic: unable to find map data\n"); -#endif /* DEBUG */ return (ENOENT); } if (amu_ldap_rebind(aldh)) { return (ENOENT); } - if (get_ldap_timestamp(aldh->ldap, map, ts)) { + if (get_ldap_timestamp(aldh, map, ts)) { return (ENOENT); } return (0); diff --git a/contrib/amd/amd/info_ndbm.c b/contrib/amd/amd/info_ndbm.c index bd92c13..8a52ba9 100644 --- a/contrib/amd/amd/info_ndbm.c +++ b/contrib/amd/amd/info_ndbm.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1989 Jan-Simon Pendry * Copyright (c) 1989 Imperial College of Science, Technology & Medicine * Copyright (c) 1989 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: info_ndbm.c,v 1.3.2.4 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/info_ndbm.c * */ @@ -86,8 +85,8 @@ ndbm_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp) #ifdef DBM_SUFFIX char dbfilename[256]; - strcpy(dbfilename, map); - strcat(dbfilename, DBM_SUFFIX); + xstrlcpy(dbfilename, map, sizeof(dbfilename)); + xstrlcat(dbfilename, DBM_SUFFIX, sizeof(dbfilename)); error = stat(dbfilename, &stb); #else /* not DBM_SUFFIX */ error = fstat(dbm_pagfno(db), &stb); @@ -117,14 +116,14 @@ ndbm_init(mnt_map *m, char *map, time_t *tp) #ifdef DBM_SUFFIX char dbfilename[256]; - strcpy(dbfilename, map); - strcat(dbfilename, DBM_SUFFIX); + xstrlcpy(dbfilename, map, sizeof(dbfilename)); + xstrlcat(dbfilename, DBM_SUFFIX, sizeof(dbfilename)); error = stat(dbfilename, &stb); #else /* not DBM_SUFFIX */ error = fstat(dbm_pagfno(db), &stb); #endif /* not DBM_SUFFIX */ if (error < 0) - *tp = clocktime(); + *tp = clocktime(NULL); else *tp = stb.st_mtime; dbm_close(db); diff --git a/contrib/amd/amd/info_nis.c b/contrib/amd/amd/info_nis.c index b59123f..bcb87d5 100644 --- a/contrib/amd/amd/info_nis.c +++ b/contrib/amd/amd/info_nis.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1989 Jan-Simon Pendry * Copyright (c) 1989 Imperial College of Science, Technology & Medicine * Copyright (c) 1989 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: info_nis.c,v 1.6.2.5 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/info_nis.c * */ @@ -107,9 +106,7 @@ determine_nis_domain(void) } if (!*default_domain) { nis_not_running = 1; -#ifdef DEBUG plog(XLOG_WARNING, "NIS domain name is not set. NIS ignored."); -#endif /* DEBUG */ return ENOENT; } gopt.nis_domain = strdup(default_domain); @@ -144,12 +141,8 @@ callback(int status, char *key, int kl, char *val, int vl, char *data) /* 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; } @@ -350,9 +343,7 @@ nis_init(mnt_map *m, char *map, time_t *tp) /* NIS server found */ has_yp_order = 1; *tp = (time_t) order; -#ifdef DEBUG dlog("NIS master for %s@%s has order %lu", map, gopt.nis_domain, (unsigned long) order); -#endif /* DEBUG */ break; case YPERR_YPERR: /* NIS+ server found ! */ @@ -361,9 +352,7 @@ nis_init(mnt_map *m, char *map, time_t *tp) 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); } @@ -442,11 +431,9 @@ am_yp_all(char *indomain, char *inmap, struct ypall_callback *incallback) &outvallen); XFREE(outkey_old); } 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; diff --git a/contrib/amd/amd/info_nisplus.c b/contrib/amd/amd/info_nisplus.c index 46018bc..d392b48 100644 --- a/contrib/amd/amd/info_nisplus.c +++ b/contrib/amd/amd/info_nisplus.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1989 Jan-Simon Pendry * Copyright (c) 1989 Imperial College of Science, Technology & Medicine * Copyright (c) 1989 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: info_nisplus.c,v 1.3.2.5 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/info_nisplus.c * */ @@ -74,9 +73,7 @@ nisplus_callback(const nis_name key, const nis_object *value, voidp opaquedata) 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); @@ -95,6 +92,7 @@ nisplus_reload(mnt_map *m, char *map, void (*fn) ()) nis_result *result; char *org; /* if map does not have ".org_dir" then append it */ nis_name map_name; + size_t l; org = strstr(map, NISPLUS_ORGDIR); if (org == NULL) @@ -103,21 +101,20 @@ nisplus_reload(mnt_map *m, char *map, void (*fn) ()) org = ""; /* make some room for the NIS map_name */ - map_name = xmalloc(strlen(map) + sizeof(NISPLUS_ORGDIR)); + l = strlen(map) + sizeof(NISPLUS_ORGDIR); + map_name = xmalloc(l); if (map_name == NULL) { plog(XLOG_ERROR, "Unable to create map_name %s: %s", map, strerror(ENOMEM)); return ENOMEM; } - sprintf(map_name, "%s%s", map, org); + xsnprintf(map_name, l, "%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, @@ -145,10 +142,8 @@ nisplus_search_callback(const nis_name key, const nis_object *value, voidp opaqu { 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; @@ -166,6 +161,7 @@ nisplus_search(mnt_map *m, char *map, char *key, char **val, time_t *tp) struct nisplus_search_callback_data data; nis_name index; char *org; /* if map does not have ".org_dir" then append it */ + size_t l; org = strstr(map, NISPLUS_ORGDIR); if (org == NULL) @@ -174,14 +170,14 @@ nisplus_search(mnt_map *m, char *map, char *key, char **val, time_t *tp) 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) - ); + l = sizeof('[') /* for opening selection criteria */ + + sizeof(NISPLUS_KEY) + + strlen(key) + + sizeof(']') /* for closing selection criteria */ + + sizeof(',') /* + 1 for , separator */ + + strlen(map) + + sizeof(NISPLUS_ORGDIR); + index = xmalloc(l); if (index == NULL) { plog(XLOG_ERROR, "Unable to create index %s: %s", @@ -189,14 +185,12 @@ nisplus_search(mnt_map *m, char *map, char *key, char **val, time_t *tp) strerror(ENOMEM)); return ENOMEM; } - sprintf(index, "[%s%s],%s%s", NISPLUS_KEY, key, map, org); + xsnprintf(index, l, "[%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, @@ -220,11 +214,9 @@ nisplus_search(mnt_map *m, char *map, char *key, char **val, time_t *tp) 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)); @@ -233,23 +225,17 @@ nisplus_search(mnt_map *m, char *map, char *key, char **val, time_t *tp) 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; @@ -271,6 +257,7 @@ nisplus_init(mnt_map *m, char *map, time_t *tp) char *org; /* if map does not have ".org_dir" then append it */ nis_name map_name; int error = 0; + size_t l; org = strstr(map, NISPLUS_ORGDIR); if (org == NULL) @@ -279,7 +266,8 @@ nisplus_init(mnt_map *m, char *map, time_t *tp) org = ""; /* make some room for the NIS map_name */ - map_name = xmalloc(strlen(map) + sizeof(NISPLUS_ORGDIR)); + l = strlen(map) + sizeof(NISPLUS_ORGDIR); + map_name = xmalloc(l); if (map_name == NULL) { plog(XLOG_ERROR, "Unable to create map_name %s: %s", @@ -287,7 +275,7 @@ nisplus_init(mnt_map *m, char *map, time_t *tp) strerror(ENOMEM)); return ENOMEM; } - sprintf(map_name, "%s%s", map, org); + xsnprintf(map_name, l, "%s%s", map, org); result = nis_lookup(map_name, (EXPAND_NAME | FOLLOW_LINKS | FOLLOW_PATH)); @@ -300,10 +288,8 @@ nisplus_init(mnt_map *m, char *map, time_t *tp) } if (result->status != NIS_SUCCESS) { -#ifdef DEBUG dlog("NISplus init <%s>: %s (%d)", map, nis_sperrno(result->status), result->status); -#endif /* DEBUG */ error = ENOENT; } diff --git a/contrib/amd/amd/info_passwd.c b/contrib/amd/amd/info_passwd.c index a14ac12..c8bf388 100644 --- a/contrib/amd/amd/info_passwd.c +++ b/contrib/amd/amd/info_passwd.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: info_passwd.c,v 1.3.2.4 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/info_passwd.c * */ @@ -151,11 +150,11 @@ passwd_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp) do { q = strrchr(p, '/'); if (q) { - strcat(rhost, q + 1); - strcat(rhost, "."); + xstrlcat(rhost, q + 1, sizeof(rhost)); + xstrlcat(rhost, ".", sizeof(rhost)); *q = '\0'; } else { - strcat(rhost, p); + xstrlcat(rhost, p, sizeof(rhost)); } } while (q); @@ -176,11 +175,9 @@ passwd_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp) 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 + xsnprintf(val, sizeof(val), "var0:=%s;var1:=%s;var2:=%s;var3:=%s;%s", + dir+1, rhost, user, pw->pw_dir, p); dlog("passwd_search: map=%s key=%s -> %s", map, key, val); -#endif /* DEBUG */ if (q) *q = '.'; *pval = strdup(val); diff --git a/contrib/amd/amd/info_union.c b/contrib/amd/amd/info_union.c index 8613f62..8f0631c 100644 --- a/contrib/amd/amd/info_union.c +++ b/contrib/amd/amd/info_union.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: info_union.c,v 1.3.2.4 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/info_union.c * */ @@ -81,10 +80,12 @@ union_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp) char *mapd = strdup(map + UNION_PREFLEN); char **v = strsplit(mapd, ':', '\"'); char **p; + size_t l; for (p = v; p[1]; p++) ; - *pval = xmalloc(strlen(*p) + 5); - sprintf(*pval, "fs:=%s", *p); + l = strlen(*p) + 5; + *pval = xmalloc(l); + xsnprintf(*pval, l, "fs:=%s", *p); XFREE(mapd); XFREE(v); return 0; @@ -104,7 +105,7 @@ union_reload(mnt_map *m, char *map, void (*fn) (mnt_map *, char *, char *)) (*fn) (m, strdup("/defaults"), strdup("type:=link;opts:=nounmount;sublink:=${key}")); for (dir = v; *dir; dir++) { - int dlen; + size_t l; struct dirent *dp; DIR *dirp = opendir(*dir); @@ -112,11 +113,9 @@ union_reload(mnt_map *m, char *map, void (*fn) (mnt_map *, char *, char *)) plog(XLOG_USER, "Cannot read directory %s: %m", *dir); continue; } - dlen = strlen(*dir); + l = strlen(*dir) + 5; -#ifdef DEBUG dlog("Reading directory %s...", *dir); -#endif /* DEBUG */ while ((dp = readdir(dirp))) { char *val, *dpname = &dp->d_name[0]; if (dpname[0] == '.' && @@ -124,11 +123,9 @@ union_reload(mnt_map *m, char *map, void (*fn) (mnt_map *, char *, char *)) (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); + val = xmalloc(l); + xsnprintf(val, l + 5, "fs:=%s", *dir); (*fn) (m, strdup(dp->d_name), val); } closedir(dirp); @@ -138,9 +135,10 @@ union_reload(mnt_map *m, char *map, void (*fn) (mnt_map *, char *, char *)) * Add wildcard entry */ { - char *val = xmalloc(strlen(dir[-1]) + 5); + size_t l = strlen(*(dir-1)) + 5; + char *val = xmalloc(l); - sprintf(val, "fs:=%s", dir[-1]); + xsnprintf(val, l, "fs:=%s", *(dir-1)); (*fn) (m, strdup("*"), val); } XFREE(mapd); diff --git a/contrib/amd/amd/map.c b/contrib/amd/amd/map.c index dfec3a1..8696dfd 100644 --- a/contrib/amd/amd/map.c +++ b/contrib/amd/amd/map.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: map.c,v 1.6.2.8 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/map.c * */ @@ -50,7 +49,6 @@ #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++) /* @@ -70,11 +68,11 @@ 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 */ +static am_node *root_node; /* The root of the mount tree */ +static am_node **exported_ap = (am_node **) 0; +static int exported_ap_size = 0; +static int first_free_map = 0; /* First available free slot */ +static int last_used_map = -1; /* Last unavailable used slot */ /* @@ -103,9 +101,65 @@ static nfsfattr gen_fattr = }; /* forward declarations */ -static int unmount_node(am_node *mp); +static int unmount_node(opaque_t arg); static void exported_ap_free(am_node *mp); static void remove_am(am_node *mp); +static am_node *get_root_ap(char *dir); + + +/* + * Iterator functions for exported_ap[] + */ +am_node * +get_first_exported_ap(int *index) +{ + *index = -1; + return get_next_exported_ap(index); +} + + +am_node * +get_next_exported_ap(int *index) +{ + (*index)++; + while (*index < exported_ap_size) { + if (exported_ap[*index] != NULL) + return exported_ap[*index]; + (*index)++; + } + return NULL; +} + + +/* + * Get exported_ap by index + */ +am_node * +get_exported_ap(int index) +{ + if (index < 0 || index >= exported_ap_size) + return 0; + return exported_ap[index]; +} + + +/* + * Get exported_ap by path + */ +am_node * +path_to_exported_ap(char *path) +{ + int index; + am_node *mp; + + mp = get_first_exported_ap(&index); + while (mp != NULL) { + if (STREQ(mp->am_path, path)) + break; + mp = get_next_exported_ap(&index); + } + return mp; +} /* @@ -131,6 +185,46 @@ exported_ap_realloc_map(int nsize) } + +am_node * +get_ap_child(am_node *mp, char *fname) +{ + am_node *new_mp; + mntfs *mf = mp->am_mnt; + + /* + * Allocate a new map + */ + new_mp = exported_ap_alloc(); + if (new_mp) { + /* + * Fill it in + */ + init_map(new_mp, fname); + + /* + * Put it in the table + */ + insert_am(new_mp, mp); + + /* + * Fill in some other fields, + * path and mount point. + * + * bugfix: do not prepend old am_path if direct map + * <wls@astro.umd.edu> William Sebok + */ + new_mp->am_path = str3cat(new_mp->am_path, + (mf->mf_fsflags & FS_DIRECT) + ? "" + : mp->am_path, + *fname == '/' ? "" : "/", fname); + dlog("setting path to %s", new_mp->am_path); + } + + return new_mp; +} + /* * Allocate a new mount slot and create * a new node. @@ -154,7 +248,7 @@ exported_ap_alloc(void) */ mpp = exported_ap + first_free_map; mp = *mpp = ALLOC(struct am_node); - memset((char *) mp, 0, sizeof(*mp)); + memset((char *) mp, 0, sizeof(struct am_node)); mp->am_mapno = first_free_map++; @@ -199,8 +293,9 @@ exported_ap_free(am_node *mp) first_free_map = mp->am_mapno; /* - * Free the mount node + * Free the mount node, and zero out it's internal struct data. */ + memset((char *) mp, 0, sizeof(am_node)); XFREE(mp); } @@ -229,6 +324,10 @@ insert_am(am_node *mp, am_node *p_mp) if (mp->am_osib) mp->am_osib->am_ysib = mp; p_mp->am_child = mp; +#ifdef HAVE_FS_AUTOFS + if (p_mp->am_mnt->mf_flags & MFF_IS_AUTOFS) + mp->am_flags |= AMF_AUTOFS; +#endif /* HAVE_FS_AUTOFS */ } @@ -268,27 +367,26 @@ 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 = clocktime(&mp->am_fattr.na_atime); mp->am_ttl += mp->am_timeo; /* sun's -tl option */ } void -mk_fattr(am_node *mp, nfsftype vntype) +mk_fattr(nfsfattr *fattr, 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; + fattr->na_type = NFDIR; + fattr->na_mode = NFSMODE_DIR | 0555; + fattr->na_nlink = 2; + 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; + fattr->na_type = NFLNK; + fattr->na_mode = NFSMODE_LNK | 0777; + fattr->na_nlink = 1; + fattr->na_size = 0; break; default: plog(XLOG_FATAL, "Unknown fattr type %d - ignored", vntype); @@ -308,24 +406,30 @@ init_map(am_node *mp, char *dir) { /* * mp->am_mapno is initialized by exported_ap_alloc - * other fields don't need to be initialized. + * other fields don't need to be set to zero. */ mp->am_mnt = new_mntfs(); + mp->am_mfarray = 0; mp->am_name = strdup(dir); mp->am_path = strdup(dir); mp->am_gen = new_gen(); +#ifdef HAVE_FS_AUTOFS + mp->am_autofs_fh = 0; +#endif /* HAVE_FS_AUTOFS */ mp->am_timeo = gopt.am_timeo; mp->am_attr.ns_status = NFS_OK; mp->am_fattr = gen_fattr; mp->am_fattr.na_fsid = 42; mp->am_fattr.na_fileid = mp->am_gen; - mp->am_fattr.na_atime.nt_seconds = clocktime(); - mp->am_fattr.na_atime.nt_useconds = 0; + clocktime(&mp->am_fattr.na_atime); + /* next line copies a "struct nfstime" among several fields */ mp->am_fattr.na_mtime = mp->am_fattr.na_ctime = mp->am_fattr.na_atime; new_ttl(mp); mp->am_stats.s_mtime = mp->am_fattr.na_atime.nt_seconds; + mp->am_dev = -1; + mp->am_rdev = -1; } @@ -336,7 +440,6 @@ init_map(am_node *mp, char *dir) void free_map(am_node *mp) { - remove_am(mp); if (mp->am_link) @@ -353,188 +456,24 @@ free_map(am_node *mp) 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 != am_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++; + if (mp->am_mfarray) { + mntfs **temp_mf; + for (temp_mf = mp->am_mfarray; *temp_mf; temp_mf++) + free_mntfs(*temp_mf); + XFREE(mp->am_mfarray); } - 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 = am_mypid; - - /* - * ... the map number - */ - fp->fhh_id = mp->am_mapno; +#ifdef HAVE_FS_AUTOFS + if (mp->am_autofs_fh) + autofs_release_fh(mp); +#endif /* HAVE_FS_AUTOFS */ - /* - * ... 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). - */ + exported_ap_free(mp); } -am_node * -find_ap2(char *dir, am_node *mp) +static am_node * +find_ap_recursive(char *dir, am_node *mp) { if (mp) { am_node *mp2; @@ -545,10 +484,10 @@ find_ap2(char *dir, am_node *mp) STREQ(mp->am_mnt->mf_mount, dir)) return mp; - mp2 = find_ap2(dir, mp->am_osib); + mp2 = find_ap_recursive(dir, mp->am_osib); if (mp2) return mp2; - return find_ap2(dir, mp->am_child); + return find_ap_recursive(dir, mp->am_child); } return 0; @@ -567,7 +506,7 @@ find_ap(char *dir) 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]); + mp = find_ap_recursive(dir, exported_ap[i]); if (mp) { return mp; } @@ -603,22 +542,12 @@ find_mf(mntfs *mf) * the filehandles of the initial automount points. */ am_nfs_fh * -root_fh(char *dir) +get_root_nfs_fh(char *dir) { static am_nfs_fh nfh; - am_node *mp = root_ap(dir, TRUE); + am_node *mp = get_root_ap(dir); 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 %ld", (long) pid); -#endif /* DEBUG */ - } return &nfh; } @@ -631,8 +560,8 @@ root_fh(char *dir) } -am_node * -root_ap(char *dir, int path) +static am_node * +get_root_ap(char *dir) { am_node *mp = find_ap(dir); @@ -657,7 +586,7 @@ map_flush_srvr(fserver *fs) 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(); + mp->am_ttl = clocktime(NULL); done = 1; } } @@ -673,11 +602,23 @@ map_flush_srvr(fserver *fs) * automount node to be automounted. */ int -mount_auto_node(char *dir, voidp arg) +mount_auto_node(char *dir, opaque_t arg) { int error = 0; + am_node *mp = (am_node *) arg; + am_node *new_mp; + + new_mp = mp->am_mnt->mf_ops->lookup_child(mp, dir, &error, VLOOK_CREATE); + if (new_mp && error < 0) { + /* + * We can't allow the fileid of the root node to change. + * Should be ok to force it to 1, always. + */ + new_mp->am_gen = new_mp->am_fattr.na_fileid = 1; + + new_mp = mp->am_mnt->mf_ops->mount_child(new_mp, &error); + } - (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); @@ -696,7 +637,7 @@ mount_exported(void) /* * Iterate over all the nodes to be started */ - return root_keyiter((void (*)P((char *, voidp))) mount_auto_node, root_node); + return root_keyiter(mount_auto_node, root_node); } @@ -735,7 +676,7 @@ make_root_node(void) /* * Mount the root */ - root_mnt->mf_error = (*root_mnt->mf_ops->mount_fs) (root_node); + root_mnt->mf_error = root_mnt->mf_ops->mount_fs(root_node, root_mnt); } @@ -750,143 +691,136 @@ umount_exported(void) for (i = last_used_map; i >= 0; --i) { am_node *mp = exported_ap[i]; + mntfs *mf; - 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 (!mp) + 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); - } + 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 ((--immediate_abort < 0 && - !(mp->am_flags & AMF_ROOT) && mp->am_parent) || - (mf->mf_flags & MFF_RESTART)) { + if (!(mf->mf_fsflags & FS_DIRECTORY)) + /* + * When shutting down this had better + * look like a directory, otherwise it + * can't be unmounted! + */ + mk_fattr(&mp->am_fattr, NFDIR); - /* - * 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); + if ((--immediate_abort < 0 && + !(mp->am_flags & AMF_ROOT) && mp->am_parent) || + (mf->mf_flags & MFF_RESTART)) { - } else { + /* + * Just throw this node away without bothering to unmount it. If + * the server is not known to be up then don't discard the mounted + * on directory or Amd might hang... + */ + if (mf->mf_server && + (mf->mf_server->fs_flags & (FSF_DOWN | FSF_VALID)) != FSF_VALID) + mf->mf_flags &= ~MFF_MKMNT; + if (gopt.flags & CFM_UNMOUNT_ON_EXIT || mp->am_flags & AMF_AUTOFS) { + plog(XLOG_INFO, "on-exit attempt to unmount %s", mf->mf_mount); /* - * Any other node gets forcibly timed out. + * use unmount_mp, not unmount_node, so that unmounts be + * backgrounded as needed. */ - 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; + unmount_mp((opaque_t) mp); + } else { + am_unmounted(mp); } + exported_ap[i] = 0; + } else { + /* + * Any other node gets forcibly timed out. + */ + mp->am_flags &= ~AMF_NOTIMEOUT; + mp->am_mnt->mf_flags &= ~MFF_RSTKEEP; + mp->am_ttl = 0; + mp->am_timeo = 1; + mp->am_timeo_w = 0; } } } +/* + * Try to mount a file system. Can be called directly or in a sub-process by run_task. + * + * Warning: this function might be running in a child process context. + * Don't expect any changes made here to survive in the parent amd process. + */ +int +mount_node(opaque_t arg) +{ + am_node *mp = (am_node *) arg; + mntfs *mf = mp->am_mnt; + int error = 0; + +#ifdef HAVE_FS_AUTOFS + if (mp->am_flags & AMF_AUTOFS) + error = autofs_mount_fs(mp, mf); + else +#endif /* HAVE_FS_AUTOFS */ + if (!(mf->mf_flags & MFF_MOUNTED)) + error = mf->mf_ops->mount_fs(mp, mf); + + if (error > 0) + dlog("mount_node: call to mf_ops->mount_fs(%s) failed: %s", + mp->am_path, strerror(error)); + return error; +} + + static int -unmount_node(am_node *mp) +unmount_node(opaque_t arg) { + am_node *mp = (am_node *) arg; mntfs *mf = mp->am_mnt; - int error; + int error = 0; - if ((mf->mf_flags & MFF_ERROR) || mf->mf_refc > 1) { + if (mf->mf_flags & MFF_ERROR) { /* * 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; + dlog("No-op unmount of error node %s", mf->mf_info); } else { -#ifdef DEBUG - dlog("Unmounting %s (%s)", mf->mf_mount, mf->mf_info); -#endif /* DEBUG */ - error = (*mf->mf_ops->umount_fs) (mp); + dlog("Unmounting <%s> <%s> (%s) flags %x", + mp->am_path, mf->mf_mount, mf->mf_info, mf->mf_flags); +#ifdef HAVE_FS_AUTOFS + if (mp->am_flags & AMF_AUTOFS) + error = autofs_umount_fs(mp, mf); + else +#endif /* HAVE_FS_AUTOFS */ + if (mf->mf_refc == 1) + error = mf->mf_ops->umount_fs(mp, mf); } + /* do this again, it might have changed */ + mf = mp->am_mnt; if (error) { errno = error; /* XXX */ -#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 MFF_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) +free_map_if_success(int rc, int term, opaque_t arg) { - am_node *mp = (am_node *) closure; + am_node *mp = (am_node *) arg; mntfs *mf = mp->am_mnt; + wchan_t wchan = get_mntfs_wchan(mf); /* * Not unmounting any more @@ -912,14 +846,22 @@ free_map_if_success(int rc, int term, voidp closure) am_unmounted(mp); } #endif /* DEBUG */ +#ifdef HAVE_FS_AUTOFS + if (mp->am_flags & AMF_AUTOFS) + autofs_umount_failed(mp); +#endif /* HAVE_FS_AUTOFS */ amd_stats.d_uerr++; } else if (rc) { - if (mf->mf_ops == &amfs_program_ops || rc == EBUSY) { + if (mf->mf_ops == &amfs_program_ops || 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); - } + else + plog(XLOG_ERROR, "%s: unmount: %s", mp->am_path, strerror(rc)); +#ifdef HAVE_FS_AUTOFS + if (mf->mf_flags & MFF_IS_AUTOFS) + autofs_get_mp(mp); + if (mp->am_flags & AMF_AUTOFS) + autofs_umount_failed(mp); +#endif /* HAVE_FS_AUTOFS */ amd_stats.d_uerr++; } else { am_unmounted(mp); @@ -928,16 +870,21 @@ free_map_if_success(int rc, int term, voidp closure) /* * Wakeup anything waiting for this unmount */ - wakeup((voidp) mf); + wakeup(wchan); } -static int +int unmount_mp(am_node *mp) { int was_backgrounded = 0; mntfs *mf = mp->am_mnt; +#ifdef notdef + plog(XLOG_INFO, "\"%s\" on %s timed out (flags 0x%x)", + mp->am_path, mp->am_mnt->mf_mount, (int) mf->mf_flags); +#endif /* notdef */ + #ifndef MNT2_NFS_OPT_SYMTTL /* * This code is needed to defeat Solaris 2.4's (and newer) symlink @@ -946,56 +893,48 @@ unmount_mp(am_node *mp) * symlink-cache at mount time (such as Irix 5.x and 6.x). -Erez. * * Additionally, Linux currently ignores the nt_useconds field, - * so we must update the nt_seconds field every time. + * so we must update the nt_seconds field every time if clocktime(NULL) + * didn't return a new number of seconds. */ - if (mp->am_parent) + if (mp->am_parent) { + time_t last = mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime.nt_seconds; + clocktime(&mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime); /* defensive programming... can't we assert the above condition? */ - mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime.nt_seconds++; + if (last == (time_t) mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime.nt_seconds) + mp->am_parent->am_attr.ns_u.ns_attr_u.na_mtime.nt_seconds++; + } #endif /* not MNT2_NFS_OPT_SYMTTL */ -#ifdef notdef - plog(XLOG_INFO, "\"%s\" on %s timed out", mp->am_path, mp->am_mnt->mf_mount); -#endif /* notdef */ + if (mf->mf_refc == 1 && !FSRV_ISUP(mf->mf_server)) { + /* + * Don't try to unmount from a server that is known to be down + */ + if (!(mf->mf_flags & MFF_LOGDOWN)) { + /* Only log this once, otherwise gets a bit boring */ + plog(XLOG_STATS, "file server %s is down - timeout of \"%s\" ignored", mf->mf_server->fs_host, mp->am_path); + mf->mf_flags |= MFF_LOGDOWN; + } + return 0; + } + + dlog("\"%s\" on %s timed out", mp->am_path, mp->am_mnt->mf_mount); + mf->mf_flags |= MFF_UNMOUNTING; - if ((mf->mf_ops->fs_flags & FS_UBACKGROUND) && +#ifdef HAVE_FS_AUTOFS + if (mf->mf_flags & MFF_IS_AUTOFS) + autofs_release_mp(mp); +#endif /* HAVE_FS_AUTOFS */ + + if ((mf->mf_fsflags & FS_UBACKGROUND) && (mf->mf_flags & MFF_MOUNTED)) { - 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 */ - } + dlog("Trying unmount in background"); + run_task(unmount_node, (opaque_t) mp, + free_map_if_success, (opaque_t) mp); + was_backgrounded = 1; } 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 + free_map_if_success(unmount_node((opaque_t) mp), 0, (opaque_t) mp); dlog("unmount attempt done"); -#endif /* DEBUG */ } return was_backgrounded; @@ -1003,25 +942,23 @@ unmount_mp(am_node *mp) void -timeout_mp(voidp v) +timeout_mp(opaque_t v) /* argument not used?! */ { int i; time_t t = NEVER; - time_t now = clocktime(); - int backoff = NumChild / 4; + time_t now = clocktime(NULL); + int backoff = NumChildren / 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. + * Just continue if nothing mounted */ - if (!mp || (mp->am_flags & AMF_NOTIMEOUT)) + if (!mp) continue; /* @@ -1031,6 +968,17 @@ timeout_mp(voidp v) if (!mf) continue; +#ifdef HAVE_FS_AUTOFS + if (mf->mf_flags & MFF_IS_AUTOFS && mp->am_autofs_ttl != NEVER) { + if (now >= mp->am_autofs_ttl) + autofs_timeout_mp(mp); + t = smallest_t(t, mp->am_autofs_ttl); + } +#endif /* HAVE_FS_AUTOFS */ + + if (mp->am_flags & AMF_NOTIMEOUT) + continue; + /* * Don't delete last reference to a restarted filesystem. */ @@ -1087,9 +1035,7 @@ timeout_mp(voidp v) } if (t == NEVER) { -#ifdef DEBUG dlog("No further timeouts"); -#endif /* DEBUG */ t = now + ONE_HOUR; } @@ -1108,9 +1054,7 @@ timeout_mp(voidp v) */ if ((int) amd_state >= (int) Finishing) t = now + 1; -#ifdef DEBUG dlog("Next mount timeout in %lds", (long) (t - now)); -#endif /* DEBUG */ timeout_mp_id = timeout(t - now, timeout_mp, 0); } diff --git a/contrib/amd/amd/mapc.c b/contrib/amd/amd/mapc.c index 591c4f3..d7efdbc 100644 --- a/contrib/amd/amd/mapc.c +++ b/contrib/amd/amd/mapc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1989 Jan-Simon Pendry * Copyright (c) 1989 Imperial College of Science, Technology & Medicine * Copyright (c) 1989 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: mapc.c,v 1.7.2.7 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/mapc.c * */ @@ -85,8 +84,6 @@ #define MREC_PART 1 #define MREC_NONE 0 -#define MAX_CHAIN 2048 - static struct opt_tab mapc_opt[] = { {"all", MAPC_ALL}, @@ -201,12 +198,17 @@ extern int ndbm_search(mnt_map *, char *, char *, char **, time_t *); extern int ndbm_mtime(mnt_map *, char *, time_t *); #endif /* HAVE_MAP_NDBM */ +/* EXECUTABLE MAPS */ +#ifdef HAVE_MAP_EXEC +extern int exec_init(mnt_map *, char *, time_t *); +extern int exec_search(mnt_map *, char *, char *, char **, time_t *); +#endif /* HAVE_MAP_EXEC */ + /* FILE MAPS */ #ifdef HAVE_MAP_FILE -extern int file_init(mnt_map *, char *, time_t *); +extern int file_init_or_mtime(mnt_map *, char *, time_t *); extern int file_reload(mnt_map *, char *, add_fn *); extern int file_search(mnt_map *, char *, char *, char **, time_t *); -extern int file_mtime(mnt_map *, char *, time_t *); #endif /* HAVE_MAP_FILE */ @@ -302,14 +304,25 @@ static map_type maptypes[] = #ifdef HAVE_MAP_FILE { "file", - file_init, + file_init_or_mtime, file_reload, NULL, /* isup function */ file_search, - file_mtime, + file_init_or_mtime, MAPC_ALL }, #endif /* HAVE_MAP_FILE */ +#ifdef HAVE_MAP_EXEC + { + "exec", + exec_init, + error_reload, + NULL, /* isup function */ + exec_search, + error_mtime, + MAPC_INC + }, +#endif /* HAVE_MAP_EXEC */ { "error", error_init, @@ -337,16 +350,27 @@ kvhash_of(char *key) void -mapc_showtypes(char *buf) +mapc_showtypes(char *buf, size_t l) { - map_type *mt; - char *sep = ""; + map_type *mt=NULL, *lastmt; + int linesize = 0, i; + i = sizeof(maptypes) / sizeof(maptypes[0]); + lastmt = maptypes + i; buf[0] = '\0'; - for (mt = maptypes; mt < maptypes + sizeof(maptypes) / sizeof(maptypes[0]); mt++) { - strcat(buf, sep); - strcat(buf, mt->name); - sep = ", "; + for (mt = maptypes; mt < lastmt; mt++) { + xstrlcat(buf, mt->name, l); + if (mt == (lastmt-1)) + break; /* if last one, don't do xstrlcat's that follows */ + linesize += strlen(mt->name); + if (--i > 0) { + xstrlcat(buf, ", ", l); + linesize += 2; + } + if (linesize > 54) { + linesize = 0; + xstrlcat(buf, "\n\t\t ", l); + } } } @@ -386,9 +410,7 @@ mapc_add_kv(mnt_map *m, char *key, char *val) regex_t re; #endif /* HAVE_REGEXEC */ -#ifdef DEBUG dlog("add_kv: %s -> %s", key, val); -#endif /* DEBUG */ #ifdef HAVE_REGEXEC if (MAPC_ISRE(m)) { @@ -398,7 +420,7 @@ mapc_add_kv(mnt_map *m, char *key, char *val) /* * Make sure the string is bound to the start and end */ - sprintf(pattern, "^%s$", key); + xsnprintf(pattern, sizeof(pattern), "^%s$", key); retval = regcomp(&re, pattern, REG_ICASE); if (retval != 0) { char errstr[256]; @@ -492,11 +514,12 @@ mapc_find_wildcard(mnt_map *m) * Do a map reload. * Attempt to reload without losing current data by switching the hashes * round. + * If reloading was needed and succeeded, return 1; else return 0. */ -static void +static int mapc_reload_map(mnt_map *m) { - int error; + int error, ret = 0; kv *maphash[NKVHASH], *tmphash[NKVHASH]; time_t t; @@ -512,11 +535,9 @@ mapc_reload_map(mnt_map *m) if (m->reloads != 0 && do_mapc_reload != 0) { if (t <= m->modify) { plog(XLOG_INFO, "reload of map %s is not needed (in sync)", m->map_name); -#ifdef DEBUG dlog("map %s last load time is %d, last modify time is %d", m->map_name, (int) m->modify, (int) t); -#endif /* DEBUG */ - return; + return ret; } } @@ -524,9 +545,7 @@ mapc_reload_map(mnt_map *m) memcpy((voidp) maphash, (voidp) m->kvhash, sizeof(m->kvhash)); memset((voidp) m->kvhash, 0, sizeof(m->kvhash)); -#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) { if (m->reloads == 0) @@ -547,15 +566,15 @@ mapc_reload_map(mnt_map *m) mapc_clear(m); memcpy((voidp) m->kvhash, (voidp) tmphash, sizeof(m->kvhash)); m->modify = t; + ret = 1; } 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 ret; } @@ -567,7 +586,7 @@ mapc_create(char *map, char *opt, const char *type) { mnt_map *m = ALLOC(struct mnt_map); map_type *mt; - time_t modify; + time_t modify = 0; int alloc = 0; cmdoption(opt, mapc_opt, &alloc); @@ -603,9 +622,7 @@ mapc_create(char *map, char *opt, const char *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; } @@ -652,9 +669,7 @@ mapc_create(char *map, char *opt, const char *type) #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; @@ -667,6 +682,8 @@ mapc_create(char *map, char *opt, const char *type) m->refc = 1; m->wildcard = 0; m->reloads = 0; + /* Unfortunately with current code structure, this cannot be initialized here */ + m->cfm = NULL; /* * synchronize cache with reality @@ -732,7 +749,7 @@ mapc_find(char *map, char *opt, const char *maptype) * add it to the list of maps */ ITER(m, mnt_map, &map_list_head) - if (STREQ(m->map_name, map)) + if (STREQ(m->map_name, map)) return mapc_dup(m); m = mapc_create(map, opt, maptype); ins_que(&m->hdr, &map_list_head); @@ -745,9 +762,9 @@ mapc_find(char *map, char *opt, const char *maptype) * Free a map. */ void -mapc_free(voidp v) +mapc_free(opaque_t arg) { - mnt_map *m = v; + mnt_map *m = (mnt_map *) arg; /* * Decrement the reference count. @@ -883,12 +900,15 @@ mapc_meta_search(mnt_map *m, char *key, char **pval, int recurse) * For example: * "src/gnu/gcc" -> "src / gnu / *" -> "src / *" */ - strcpy(wildname, key); + xstrlcpy(wildname, key, sizeof(wildname)); while (error && (subp = strrchr(wildname, '/'))) { - strcpy(subp, "/*"); -#ifdef DEBUG + /* + * sizeof space left in subp is sizeof wildname minus what's left + * after the strchr above returned a pointer inside wildname into + * subp. + */ + xstrlcpy(subp, "/*", sizeof(wildname) - (subp - wildname)); dlog("mapc recurses on %s", wildname); -#endif /* DEBUG */ error = mapc_meta_search(m, wildname, pval, MREC_PART); if (error) *subp = 0; @@ -917,25 +937,42 @@ mapc_search(mnt_map *m, char *key, char **pval) static void mapc_sync(mnt_map *m) { - if (m->alloc != MAPC_ROOT) { + int need_mtime_update = 0; - /* do not clear map if map service is down */ - if (m->isup) { - if (!((*m->isup)(m, m->map_name))) { - plog(XLOG_ERROR, "mapc_sync: map %s is down: not clearing map", m->map_name); - return; - } + if (m->alloc == MAPC_ROOT) + return; /* nothing to do */ + + /* do not clear map if map service is down */ + if (m->isup) { + if (!((*m->isup)(m, m->map_name))) { + plog(XLOG_ERROR, "mapc_sync: map %s is down: not clearing map", m->map_name); + return; } + } + + if (m->alloc >= MAPC_ALL) { + /* mapc_reload_map() always works */ + need_mtime_update = mapc_reload_map(m); + } else { + mapc_clear(m); + /* + * Attempt to find the wildcard entry + */ + mapc_find_wildcard(m); + need_mtime_update = 1; /* because mapc_clear always works */ + } - if (m->alloc >= MAPC_ALL) { - /* mapc_reload_map() always works */ - mapc_reload_map(m); + /* + * To be safe, update the mtime of the mnt_map's own node, so that the + * kernel will flush all of its cached entries. + */ + if (need_mtime_update && m->cfm) { + am_node *mp = find_ap(m->cfm->cfm_dir); + if (mp) { + clocktime(&mp->am_fattr.na_mtime); } else { - mapc_clear(m); - /* - * Attempt to find the wildcard entry - */ - mapc_find_wildcard(m); + plog(XLOG_ERROR, "cannot find map %s to update its mtime", + m->cfm->cfm_dir); } } } @@ -972,7 +1009,7 @@ mapc_reload(void) static int root_init(mnt_map *m, char *map, time_t *tp) { - *tp = clocktime(); + *tp = clocktime(NULL); return STREQ(map, ROOT_MAP) ? 0 : ENOENT; } @@ -1007,36 +1044,39 @@ root_newmap(const char *dir, const char *opts, const char *map, const cf_map_t * if (cfm) { if (map) { - sprintf(str, "cache:=mapdefault;type:=toplvl;fs:=\"%s\"", - get_full_path(map, cfm->cfm_search_path, cfm->cfm_type)); + xsnprintf(str, sizeof(str), + "cache:=mapdefault;type:=toplvl;mount_type:=%s;fs:=\"%s\"", + cfm->cfm_flags & CFM_MOUNT_TYPE_AUTOFS ? "autofs" : "nfs", + get_full_path(map, cfm->cfm_search_path, cfm->cfm_type)); if (opts && opts[0] != '\0') { - strcat(str, ";"); - strcat(str, opts); + xstrlcat(str, ";", sizeof(str)); + xstrlcat(str, opts, sizeof(str)); } if (cfm->cfm_flags & CFM_BROWSABLE_DIRS_FULL) - strcat(str, ";opts:=rw,fullybrowsable"); + xstrlcat(str, ";opts:=rw,fullybrowsable", sizeof(str)); if (cfm->cfm_flags & CFM_BROWSABLE_DIRS) - strcat(str, ";opts:=rw,browsable"); + xstrlcat(str, ";opts:=rw,browsable", sizeof(str)); if (cfm->cfm_type) { - strcat(str, ";maptype:="); - strcat(str, cfm->cfm_type); + xstrlcat(str, ";maptype:=", sizeof(str)); + xstrlcat(str, cfm->cfm_type, sizeof(str)); } } else { - strcpy(str, opts); + xstrlcpy(str, opts, sizeof(str)); } } else { if (map) - sprintf(str, "cache:=mapdefault;type:=toplvl;fs:=\"%s\";%s", - map, opts ? opts : ""); + xsnprintf(str, sizeof(str), + "cache:=mapdefault;type:=toplvl;fs:=\"%s\";%s", + map, opts ? opts : ""); else - strcpy(str, opts); + xstrlcpy(str, opts, sizeof(str)); } mapc_repl_kv(root_map, strdup((char *)dir), strdup(str)); } int -mapc_keyiter(mnt_map *m, void (*fn) (char *, voidp), voidp arg) +mapc_keyiter(mnt_map *m, key_fun *fn, opaque_t arg) { int i; int c = 0; @@ -1056,10 +1096,10 @@ mapc_keyiter(mnt_map *m, void (*fn) (char *, voidp), voidp arg) /* * Iterate on the root map and call (*fn)() on the key of all the nodes. - * Finally throw away the root map. + * Returns the number of entries in the root map. */ int -root_keyiter(void (*fn)(char *, voidp), voidp arg) +root_keyiter(key_fun *fn, opaque_t arg) { if (root_map) { int c = mapc_keyiter(root_map, fn, arg); @@ -1071,132 +1111,6 @@ root_keyiter(void (*fn)(char *, voidp), voidp arg) /* - * 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) 2; /* monotonically increasing */ - 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; - } - - if (mp->am_pref) - preflen = strlen(mp->am_pref); - - /* iterate over keys */ - for (i = 0; i < NKVHASH; i++) { - kv *k; - for (k = mmp->kvhash[i]; k ; k = k->next) { - - /* - * Skip unwanted entries which are either not real entries or - * very difficult to interpret (wildcards...) This test needs - * lots of improvement. Any takers? - */ - key = k->key; - if (!key) - continue; - - /* Skip '*' */ - if (!fully_browsable && strchr(key, '*')) - continue; - - /* - * If the map has a prefix-string then check if the key starts with - * this string, and if it does, skip over this prefix. If it has a - * prefix and it doesn't match the start of the key, skip it. - */ - if (preflen && (preflen <= (strlen(key)))) { - if (!NSTREQ(key, mp->am_pref, preflen)) - continue; - key += preflen; - } else if (preflen) { - continue; - } - - /* 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 @@ -1260,15 +1174,15 @@ get_full_path(const char *map, const char *path, const char *type) return map; /* now break path into components, and search in each */ - strcpy(component, path); + xstrlcpy(component, path, sizeof(component)); str = strtok(component, ":"); do { - strcpy(full_path, str); + xstrlcpy(full_path, str, sizeof(full_path)); len = strlen(full_path); if (full_path[len - 1] != '/') /* add trailing "/" if needed */ - strcat(full_path, "/"); - strcat(full_path, map); + xstrlcat(full_path, "/", sizeof(full_path)); + xstrlcat(full_path, map, sizeof(full_path)); if (access(full_path, R_OK) == 0) return full_path; str = strtok(NULL, ":"); diff --git a/contrib/amd/amd/mntfs.c b/contrib/amd/amd/mntfs.c index 02bcae3..6021838 100644 --- a/contrib/amd/amd/mntfs.c +++ b/contrib/amd/amd/mntfs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: mntfs.c,v 1.5.2.5 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/mntfs.c * */ @@ -71,12 +70,14 @@ static void init_mntfs(mntfs *mf, am_ops *ops, am_opts *mo, char *mp, char *info, char *auto_opts, char *mopts, char *remopts) { mf->mf_ops = ops; + mf->mf_fsflags = ops->nfs_fs_flags; mf->mf_fo = mo; mf->mf_mount = strdup(mp); mf->mf_info = strdup(info); mf->mf_auto = strdup(auto_opts); mf->mf_mopts = strdup(mopts); mf->mf_remopts = strdup(remopts); + mf->mf_loopdev = NULL; mf->mf_refc = 1; mf->mf_flags = 0; mf->mf_error = -1; @@ -104,17 +105,25 @@ alloc_mntfs(am_ops *ops, am_opts *mo, char *mp, char *info, char *auto_opts, cha } +/* find a matching mntfs in our list */ mntfs * -find_mntfs(am_ops *ops, am_opts *mo, char *mp, char *info, char *auto_opts, char *mopts, char *remopts) +locate_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 */ + dlog("Locating mntfs reference to (%s,%s)", mp, info); ITER(mf, mntfs, &mfhead) { - if (STREQ(mf->mf_mount, mp)) { + /* + * For backwards compatibility purposes, we treat already-mounted + * filesystems differently and only require a match of their mount point, + * not of their server info. After all, there is little we can do if + * the user asks us to mount two different things onto the same mount: one + * will always cover the other one. + */ + if (STREQ(mf->mf_mount, mp) && + ((mf->mf_flags & MFF_MOUNTED && !(mf->mf_fsflags & FS_DIRECT)) + || (STREQ(mf->mf_info, info) && mf->mf_ops == ops))) { /* * Handle cases where error ops are involved */ @@ -125,35 +134,32 @@ find_mntfs(am_ops *ops, am_opts *mo, char *mp, char *info, char *auto_opts, char */ 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; + return dup_mntfs(mf); } - if ((mf->mf_flags & MFF_RESTART) && amd_state == Run) { + dlog("mf->mf_flags = %#x", mf->mf_flags); + mf->mf_fo = mo; + if ((mf->mf_flags & MFF_RESTART) && amd_state < Finishing) { /* * Restart a previously mounted filesystem. */ - 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 + * If we are restarting an amd internal filesystem, + * we need to initialize it a bit. + * + * We know it's internal because it is marked as toplvl. */ - mf2->mf_private = (voidp) dup_mntfs(mf); - mf2->mf_prfree = free_mntfs; - return mf2; + if (mf->mf_ops == &amfs_toplvl_ops) { + mf->mf_ops = ops; + mf->mf_info = strealloc(mf->mf_info, info); + ops->mounted(mf); /* XXX: not right, but will do for now */ + } + + return mf; } - mf->mf_fo = mo; if (!(mf->mf_flags & (MFF_MOUNTED | MFF_MOUNTING | MFF_UNMOUNTING))) { fserver *fs; mf->mf_flags &= ~MFF_ERROR; @@ -164,7 +170,7 @@ find_mntfs(am_ops *ops, am_opts *mo, char *mp, char *info, char *auto_opts, char mf->mf_info = strealloc(mf->mf_info, info); if (mf->mf_private && mf->mf_prfree) { - (*mf->mf_prfree) (mf->mf_private); + mf->mf_prfree(mf->mf_private); mf->mf_private = 0; } @@ -177,6 +183,18 @@ find_mntfs(am_ops *ops, am_opts *mo, char *mp, char *info, char *auto_opts, char } /* end of "if (STREQ(mf-> ..." */ } /* end of ITER */ + return 0; +} + + +/* find a matching mntfs in our list, create a new one if none is found */ +mntfs * +find_mntfs(am_ops *ops, am_opts *mo, char *mp, char *info, char *auto_opts, char *mopts, char *remopts) +{ + mntfs *mf = locate_mntfs(ops, mo, mp, info, auto_opts, mopts, remopts); + if (mf) + return mf; + return alloc_mntfs(ops, mo, mp, info, auto_opts, mopts, remopts); } @@ -189,7 +207,7 @@ new_mntfs(void) static void -uninit_mntfs(mntfs *mf, int rmd) +uninit_mntfs(mntfs *mf) { if (mf->mf_auto) XFREE(mf->mf_auto); @@ -202,12 +220,6 @@ uninit_mntfs(mntfs *mf, int rmd) 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); @@ -237,7 +249,7 @@ discard_mntfs(voidp v) /* * Free memory */ - uninit_mntfs(mf, TRUE); + uninit_mntfs(mf); XFREE(mf); --mntfs_allocated; @@ -260,17 +272,27 @@ flush_mntfs(void) void -free_mntfs(voidp v) +free_mntfs(opaque_t arg) { - mntfs *mf = v; + mntfs *mf = (mntfs *) arg; + + dlog("free_mntfs <%s> type %s mf_refc %d flags %x", + mf->mf_mount, mf->mf_ops->fs_type, mf->mf_refc, mf->mf_flags); /* * We shouldn't ever be called to free something that has * a non-positive refcount. Something is badly wrong if * we have been! Ignore the request for now... */ - if(mf->mf_refc <= 0) { - plog(XLOG_ERROR, "IGNORING free_mntfs for <%s>: refc %d, flags %x", + if (mf->mf_refc <= 0) { + plog(XLOG_ERROR, "IGNORING free_mntfs for <%s>: refc %d, flags %x (bug?)", + mf->mf_mount, mf->mf_refc, mf->mf_flags); + return; + } + + /* don't discard last reference of a restarted/kept mntfs */ + if (mf->mf_refc == 1 && mf->mf_flags & MFF_RSTKEEP) { + plog(XLOG_ERROR, "IGNORING free_mntfs for <%s>: refc %d, flags %x (restarted)", mf->mf_mount, mf->mf_refc, mf->mf_flags); return; } @@ -292,15 +314,12 @@ free_mntfs(voidp v) mf->mf_ops->fs_type, mf->mf_mount); } - if (mf->mf_ops->fs_flags & FS_DISCARD) { -#ifdef DEBUG + if (mf->mf_fsflags & FS_DISCARD) { 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 { @@ -309,7 +328,6 @@ free_mntfs(voidp v) } 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); } } @@ -322,7 +340,7 @@ realloc_mntfs(mntfs *mf, am_ops *ops, am_opts *mo, char *mp, char *info, char *a mntfs *mf2; if (mf->mf_refc == 1 && - mf->mf_ops == &amfs_inherit_ops && + mf->mf_flags & MFF_RESTART && STREQ(mf->mf_mount, mp)) { /* * If we are inheriting then just return @@ -344,23 +362,5 @@ realloc_mntfs(mntfs *mf, am_ops *ops, am_opts *mo, char *mp, char *info, char *a mf2 = find_mntfs(ops, mo, mp, info, auto_opts, mopts, remopts); free_mntfs(mf); -#if 0 - /* - * XXX: EZK IS THIS RIGHT??? - * The next "if" statement is what supposedly fixes bgmount() in - * that it will actually use the ops structure of the next mount - * entry, if the previous one failed. - */ - if (mf2 && - ops && - mf2->mf_ops != ops && - mf2->mf_ops != &amfs_inherit_ops && - mf2->mf_ops != &amfs_toplvl_ops && - mf2->mf_ops != &amfs_error_ops) { - plog(XLOG_WARNING, "realloc_mntfs: copy fallback ops \"%s\" over \"%s\"", - ops->fs_type, mf2->mf_ops->fs_type); - mf2->mf_ops = ops; - } -#endif return mf2; } diff --git a/contrib/amd/amd/nfs_prot_svc.c b/contrib/amd/amd/nfs_prot_svc.c index 39d1d45..0dd6992 100644 --- a/contrib/amd/amd/nfs_prot_svc.c +++ b/contrib/amd/amd/nfs_prot_svc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1989 Jan-Simon Pendry * Copyright (c) 1989 Imperial College of Science, Technology & Medicine * Copyright (c) 1989 The Regents of the University of California. @@ -36,9 +36,8 @@ * 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 1.5.2.6 2004/01/21 04:04:58 ib42 Exp $ + * File: am-utils/amd/nfs_prot_svc.c * */ @@ -69,7 +68,7 @@ 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; +SVCXPRT *current_transp; /* typedefs */ typedef char *(*nfssvcproc_t)(voidp, struct svc_req *); @@ -98,30 +97,60 @@ nfs_program_2(struct svc_req *rqstp, SVCXPRT *transp) char *result; xdrproc_t xdr_argument, xdr_result; nfssvcproc_t local; + +#ifdef HAVE_TRANSPORT_TYPE_TLI + /* + * On TLI systems we don't use an INET network type, but a "ticlts" (see + * /etc/netconfig and conf/transp_tli.c:create_nfs_service). This means + * that packets could only come from the loopback interface, and we don't + * need to check them and filter possibly spoofed packets. Therefore we + * only need to check if the UID caller is correct. + */ +# ifdef HAVE___RPC_GET_LOCAL_UID + uid_t u; + /* extern definition for an internal libnsl function */ + extern int __rpc_get_local_uid(SVCXPRT *transp, uid_t *uid); + if (__rpc_get_local_uid(transp, &u) >= 0 && u != 0) { + plog(XLOG_WARNING, "ignoring request from UID %ld, must be 0", (long) u); + return; + } +# else /* not HAVE___RPC_GET_LOCAL_UID */ + dlog("cannot verify local uid for rpc request"); +# endif /* HAVE___RPC_GET_LOCAL_UID */ +#else /* not HAVE_TRANPORT_TYPE_TLI */ struct sockaddr_in *sinp; char dq[20], dq2[28]; - sinp = amu_svc_getcaller(rqstp->rq_xprt); -#ifdef MNT2_NFS_OPT_RESVPORT +# ifdef MNT2_NFS_OPT_RESVPORT /* Verify that the request comes from a reserved port */ - if (ntohs(sinp->sin_port) >= IPPORT_RESERVED) && + if (sinp && + ntohs(sinp->sin_port) >= IPPORT_RESERVED && !(gopt.flags & CFM_NFS_INSECURE_PORT)) { plog(XLOG_WARNING, "ignoring request from %s:%u, port not reserved", - inet_dquad(dq, sinp->sin_addr.s_addr), + inet_dquad(dq, sizeof(dq), sinp->sin_addr.s_addr), ntohs(sinp->sin_port)); return; } -#endif /* MNT2_NFS_OPT_RESVPORT */ +# endif /* MNT2_NFS_OPT_RESVPORT */ /* if the address does not match, ignore the request */ - if (sinp->sin_addr.s_addr && sinp->sin_addr.s_addr != myipaddr.s_addr) { - plog(XLOG_WARNING, "ignoring request from %s:%u, expected %s", - inet_dquad(dq, sinp->sin_addr.s_addr), - ntohs(sinp->sin_port), - inet_dquad(dq2, myipaddr.s_addr)); - return; + if (sinp && (sinp->sin_addr.s_addr != myipaddr.s_addr)) { + if (gopt.flags & CFM_NFS_ANY_INTERFACE) { + if (!is_interface_local(sinp->sin_addr.s_addr)) { + plog(XLOG_WARNING, "ignoring request from %s:%u, not a local interface", + inet_dquad(dq, sizeof(dq), sinp->sin_addr.s_addr), + ntohs(sinp->sin_port)); + } + } else { + plog(XLOG_WARNING, "ignoring request from %s:%u, expected %s", + inet_dquad(dq, sizeof(dq), sinp->sin_addr.s_addr), + ntohs(sinp->sin_port), + inet_dquad(dq2, sizeof(dq2), myipaddr.s_addr)); + return; + } } +#endif /* not HAVE_TRANPORT_TYPE_TLI */ - nfs_program_2_transp = NULL; + current_transp = NULL; switch (rqstp->rq_proc) { @@ -158,7 +187,7 @@ nfs_program_2(struct svc_req *rqstp, SVCXPRT *transp) * be stored in the am_node structure and later used for * quick_reply(). */ - nfs_program_2_transp = transp; + current_transp = transp; break; case NFSPROC_READLINK: @@ -256,7 +285,7 @@ nfs_program_2(struct svc_req *rqstp, SVCXPRT *transp) } result = (*local) (&argument, rqstp); - nfs_program_2_transp = NULL; + current_transp = NULL; if (result != NULL && !svc_sendreply(transp, (XDRPROC_T_TYPE) xdr_result, diff --git a/contrib/amd/amd/nfs_start.c b/contrib/amd/amd/nfs_start.c index 1bf6df1..f6aba94 100644 --- a/contrib/amd/amd/nfs_start.c +++ b/contrib/amd/amd/nfs_start.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: nfs_start.c,v 1.5.2.7 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/nfs_start.c * */ @@ -52,8 +51,8 @@ # define SELECT_MAXWAIT 16 #endif /* not SELECT_MAXWAIT */ -SVCXPRT *nfsxprt; -u_short nfs_port; +SVCXPRT *nfsxprt = NULL; +u_short nfs_port = 0; #ifndef HAVE_SIGACTION # define MASKED_SIGS (sigmask(SIGINT)|sigmask(SIGTERM)|sigmask(SIGCHLD)|sigmask(SIGHUP)) @@ -92,7 +91,9 @@ checkup(void) } } -#endif /* DEBUG */ +#else /* not DEBUG */ +#define checkup() +#endif /* not DEBUG */ static int @@ -121,10 +122,6 @@ do_select(int smask, int fds, fd_set *fdp, struct timeval *tvp) } 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. @@ -151,9 +148,9 @@ do_select(int smask, int fds, fd_set *fdp, struct timeval *tvp) /* * Perhaps reload the cache? */ - if (do_mapc_reload < clocktime()) { + if (do_mapc_reload < clocktime(NULL)) { mapc_reload(); - do_mapc_reload = clocktime() + ONE_HOUR; + do_mapc_reload = clocktime(NULL) + gopt.map_reload_interval; } return nsel; } @@ -168,26 +165,17 @@ 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); } @@ -203,7 +191,7 @@ run_rpc(void) int smask = sigblock(MASKED_SIGS); #endif /* not HAVE_SIGACTION */ - next_softclock = clocktime(); + next_softclock = clocktime(NULL); amd_state = Run; @@ -216,31 +204,27 @@ run_rpc(void) struct timeval tvv; int nsel; time_t now; -#ifdef HAVE_SVC_GETREQSET fd_set readfds; +#ifdef HAVE_SVC_GETREQSET 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); +# ifdef HAVE_FD_SET_FDS_BITS 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 */ +# else /* not HAVE_FD_SET_FDS_BITS */ + readfds = svc_fds; +# endif /* not HAVE_FD_SET_FDS_BITS */ #endif /* not HAVE_SVC_GETREQSET */ + FD_SET(fwd_sock, &readfds); -#ifdef DEBUG checkup(); -#endif /* DEBUG */ /* * If the full timeout code is not called, * then recompute the time delta manually. */ - now = clocktime(); + now = clocktime(NULL); if (next_softclock <= now) { if (amd_state == Finishing) @@ -251,29 +235,30 @@ run_rpc(void) } tvv.tv_usec = 0; - if (amd_state == Finishing && last_used_map < 0) { + if (amd_state == Finishing && get_exported_ap(0) == NULL) { flush_mntfs(); amd_state = Quit; break; } + +#ifdef HAVE_FS_AUTOFS + autofs_add_fdset(&readfds); +#endif /* HAVE_FS_AUTOFS */ + if (tvv.tv_sec <= 0) tvv.tv_sec = SELECT_MAXWAIT; -#ifdef DEBUG if (tvv.tv_sec) { dlog("Select waits for %ds", (int) 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; } plog(XLOG_ERROR, "select: %m"); @@ -284,22 +269,22 @@ run_rpc(void) default: /* - * Read all pending NFS responses at once to avoid having responses. + * 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); } +#ifdef HAVE_FS_AUTOFS + if (nsel) + nsel = autofs_handle_fdset(&readfds, nsel); +#endif /* HAVE_FS_AUTOFS */ + if (nsel) { /* * Anything left must be a normal @@ -308,11 +293,11 @@ run_rpc(void) #ifdef HAVE_SVC_GETREQSET svc_getreqset(&readfds); #else /* not HAVE_SVC_GETREQSET */ -# ifdef FD_SET +# ifdef HAVE_FD_SET_FDS_BITS svc_getreq(readfds.fds_bits[0]); -# else /* not FD_SET */ +# else /* not HAVE_FD_SET_FDS_BITS */ svc_getreq(readfds); -# endif /* not FD_SET */ +# endif /* not HAVE_FD_SET_FDS_BITS */ #endif /* not HAVE_SVC_GETREQSET */ } break; @@ -344,26 +329,16 @@ mount_automounter(int ppid) 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 */ /* - * Create the nfs service for amd + * This must be done first, because it attempts to bind + * to various UDP ports and we don't want anything else + * potentially taking over those ports before we get a chance + * to reserve them. */ -#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; + if (gopt.flags & CFM_RESTART_EXISTING_MOUNTS) + restart_automounter_nodes(); /* * Start RPC forwarding @@ -385,6 +360,46 @@ mount_automounter(int ppid) restart(); /* + * Create the nfs service for amd + * If nfs_port is already initialized, it means we + * already created the service during restart_automounter_nodes(). + */ + if (nfs_port == 0) { + ret = create_nfs_service(&soNFS, &nfs_port, &nfsxprt, nfs_program_2); + if (ret != 0) + return ret; + } + xsnprintf(pid_fsname, sizeof(pid_fsname), "%s:(pid%ld,port%u)", + am_get_hostname(), (long) am_mypid, nfs_port); + + /* security: if user sets -D amq, don't even create listening socket */ + if (!amuDebug(D_AMQ)) { + ret = create_amq_service(&udp_soAMQ, + &udp_amqp, + &udp_amqncp, + &tcp_soAMQ, + &tcp_amqp, + &tcp_amqncp, + gopt.preferred_amq_port); + if (ret != 0) + return ret; + } + +#ifdef HAVE_FS_AUTOFS + if (amd_use_autofs) { + /* + * Create the autofs service for amd. + */ + ret = create_autofs_service(); + /* if autofs service fails it is OK if using a test amd */ + if (ret != 0) { + plog(XLOG_WARNING, "autofs service registration failed, turning off autofs support"); + amd_use_autofs = 0; + } + } +#endif /* HAVE_FS_AUTOFS */ + + /* * Mount the top-level auto-mountpoints */ nmount = mount_exported(); @@ -401,41 +416,26 @@ mount_automounter(int ppid) return 0; } -#ifdef DEBUG - amuDebug(D_AMQ) { -#endif /* DEBUG */ + if (!amuDebug(D_AMQ)) { /* * 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 */ + ret = amu_svc_register(tcp_amqp, get_amd_program_number(), AMQ_VERSION, + amq_program_1, IPPROTO_TCP, tcp_amqncp); if (ret != 1) { plog(XLOG_FATAL, "unable to register (AMQ_PROGRAM=%d, AMQ_VERSION, tcp)", get_amd_program_number()); return 3; } -#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 */ + ret = amu_svc_register(udp_amqp, get_amd_program_number(), AMQ_VERSION, + amq_program_1, IPPROTO_UDP, udp_amqncp); if (ret != 1) { plog(XLOG_FATAL, "unable to register (AMQ_PROGRAM=%d, AMQ_VERSION, udp)", get_amd_program_number()); return 4; } - -#ifdef DEBUG } -#endif /* DEBUG */ /* * Start timeout_mp rolling diff --git a/contrib/amd/amd/nfs_subr.c b/contrib/amd/amd/nfs_subr.c index c216b3e..80d3ca8 100644 --- a/contrib/amd/amd/nfs_subr.c +++ b/contrib/amd/amd/nfs_subr.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: nfs_subr.c,v 1.6.2.6 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/nfs_subr.c * */ @@ -57,19 +56,45 @@ # define nfs_error(e) ((nfsstat)(e)) #endif /* nfs_error */ +/* + * File Handle structure + * + * This is interpreted by indexing the exported array + * by fhh_id (for old-style filehandles), or by retrieving + * the node name from fhh_path (for new-style filehandles). + * + * The whole structure is mapped onto a standard fhandle_t + * when transmitted. + */ +struct am_fh { + u_int fhh_gen; /* generation number */ + union { + struct { + int fhh_type; /* old or new am_fh */ + pid_t fhh_pid; /* process id */ + int fhh_id; /* map id */ + } s; + char fhh_path[NFS_FHSIZE-sizeof(u_int)]; /* path to am_node */ + } u; +}; + + /* forward declarations */ +/* converting am-filehandles to mount-points */ +static am_node *fh_to_mp3(am_nfs_fh *fhp, int *rp, int vop); +static am_node *fh_to_mp(am_nfs_fh *fhp); static void count_map_entries(const am_node *mp, u_int *out_blocks, u_int *out_bfree, u_int *out_bavail); static char * -do_readlink(am_node *mp, int *error_return, nfsattrstat **attrpp) +do_readlink(am_node *mp, int *error_return) { char *ln; /* - * If there is a readlink method, then use - * that, otherwise if a link exists use - * that, otherwise use the mount point. + * If there is a readlink method then use it, + * otherwise if a link exists use that, + * otherwise use the mount point. */ if (mp->am_mnt->mf_ops->readlink) { int retry = 0; @@ -86,8 +111,6 @@ do_readlink(am_node *mp, int *error_return, nfsattrstat **attrpp) } else { ln = mp->am_mnt->mf_mount; } - if (attrpp) - *attrpp = &mp->am_attr; return ln; } @@ -108,46 +131,38 @@ nfsproc_getattr_2_svc(am_nfs_fh *argp, struct svc_req *rqstp) static nfsattrstat res; am_node *mp; int retry; - time_t now = clocktime(); + time_t now = clocktime(NULL); -#ifdef DEBUG - amuDebug(D_TRACE) + if (amuDebug(D_TRACE)) plog(XLOG_DEBUG, "getattr:"); -#endif /* DEBUG */ - mp = fh_to_mp2(argp, &retry); + mp = fh_to_mp3(argp, &retry, VLOOK_CREATE); if (mp == 0) { - -#ifdef DEBUG - amuDebug(D_TRACE) + if (amuDebug(D_TRACE)) plog(XLOG_DEBUG, "\tretry=%d", retry); -#endif /* DEBUG */ if (retry < 0) { amd_stats.d_drops++; 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, mtime=%ld", - mp->am_path, - (int) attrp->ns_u.ns_attr_u.na_size, - (long) attrp->ns_u.ns_attr_u.na_mtime.nt_seconds); -#endif /* DEBUG */ - - /* Delay unmount of what was looked up */ - if (mp->am_timeo_w < 4 * gopt.am_timeo_w) - mp->am_timeo_w += gopt.am_timeo_w; - mp->am_ttl = now + mp->am_timeo_w; - - mp->am_stats.s_getattr++; - return attrp; + return &res; } + res = mp->am_attr; + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "\tstat(%s), size = %d, mtime=%ld.%ld", + mp->am_path, + (int) res.ns_u.ns_attr_u.na_size, + (long) res.ns_u.ns_attr_u.na_mtime.nt_seconds, + (long) res.ns_u.ns_attr_u.na_mtime.nt_useconds); + + /* Delay unmount of what was looked up */ + if (mp->am_timeo_w < 4 * gopt.am_timeo_w) + mp->am_timeo_w += gopt.am_timeo_w; + mp->am_ttl = now + mp->am_timeo_w; + + mp->am_stats.s_getattr++; return &res; } @@ -184,18 +199,16 @@ nfsproc_lookup_2_svc(nfsdiropargs *argp, struct svc_req *rqstp) uid_t uid; gid_t gid; -#ifdef DEBUG - amuDebug(D_TRACE) + if (amuDebug(D_TRACE)) plog(XLOG_DEBUG, "lookup:"); -#endif /* DEBUG */ /* finally, find the effective uid/gid from RPC request */ if (getcreds(rqstp, &uid, &gid, nfsxprt) < 0) plog(XLOG_ERROR, "cannot get uid/gid from RPC credentials"); - sprintf(opt_uid, "%d", (int) uid); - sprintf(opt_gid, "%d", (int) gid); + xsnprintf(opt_uid, sizeof(uid_str), "%d", (int) uid); + xsnprintf(opt_gid, sizeof(gid_str), "%d", (int) gid); - mp = fh_to_mp2(&argp->da_fhandle, &retry); + mp = fh_to_mp3(&argp->da_fhandle, &retry, VLOOK_CREATE); if (mp == 0) { if (retry < 0) { amd_stats.d_drops++; @@ -205,11 +218,11 @@ nfsproc_lookup_2_svc(nfsdiropargs *argp, struct svc_req *rqstp) } 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 (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "\tlookup(%s, %s)", mp->am_path, argp->da_name); + ap = mp->am_mnt->mf_ops->lookup_child(mp, argp->da_name, &error, VLOOK_CREATE); + if (ap && error < 0) + ap = mp->am_mnt->mf_ops->mount_child(ap, &error); if (ap == 0) { if (error < 0) { amd_stats.d_drops++; @@ -237,7 +250,7 @@ nfsproc_lookup_2_svc(nfsdiropargs *argp, struct svc_req *rqstp) void -quick_reply(am_node *mp, int error) +nfs_quick_reply(am_node *mp, int error) { SVCXPRT *transp = mp->am_transp; nfsdiropres res; @@ -271,11 +284,8 @@ quick_reply(am_node *mp, int error) /* * Free up transp. It's only used for one reply. */ - XFREE(transp); - mp->am_transp = NULL; -#ifdef DEBUG + XFREE(mp->am_transp); dlog("Quick reply sent for %s", mp->am_mnt->mf_mount); -#endif /* DEBUG */ } } @@ -287,12 +297,10 @@ nfsproc_readlink_2_svc(am_nfs_fh *argp, struct svc_req *rqstp) am_node *mp; int retry; -#ifdef DEBUG - amuDebug(D_TRACE) + if (amuDebug(D_TRACE)) plog(XLOG_DEBUG, "readlink:"); -#endif /* DEBUG */ - mp = fh_to_mp2(argp, &retry); + mp = fh_to_mp3(argp, &retry, VLOOK_CREATE); if (mp == 0) { readlink_retry: if (retry < 0) { @@ -301,15 +309,12 @@ nfsproc_readlink_2_svc(am_nfs_fh *argp, struct svc_req *rqstp) } res.rlr_status = nfs_error(retry); } else { - char *ln = do_readlink(mp, &retry, (nfsattrstat **) 0); + char *ln = do_readlink(mp, &retry); 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 */ + if (amuDebug(D_TRACE) && ln) + plog(XLOG_DEBUG, "\treadlink(%s) = %s", mp->am_path, ln); res.rlr_u.rlr_data_u = ln; mp->am_stats.s_readlink++; } @@ -388,12 +393,10 @@ unlink_or_rmdir(nfsdiropargs *argp, struct svc_req *rqstp, int unlinkp) goto out; } -#ifdef DEBUG - amuDebug(D_TRACE) + if (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); + mp = mp->am_mnt->mf_ops->lookup_child(mp, argp->da_name, &retry, VLOOK_DELETE); if (mp == 0) { /* * Ignore retries... @@ -503,12 +506,10 @@ nfsproc_readdir_2_svc(nfsreaddirargs *argp, struct svc_req *rqstp) am_node *mp; int retry; -#ifdef DEBUG - amuDebug(D_TRACE) + if (amuDebug(D_TRACE)) plog(XLOG_DEBUG, "readdir:"); -#endif /* DEBUG */ - mp = fh_to_mp2(&argp->rda_fhandle, &retry); + mp = fh_to_mp3(&argp->rda_fhandle, &retry, VLOOK_CREATE); if (mp == 0) { if (retry < 0) { amd_stats.d_drops++; @@ -516,10 +517,8 @@ nfsproc_readdir_2_svc(nfsreaddirargs *argp, struct svc_req *rqstp) } res.rdr_status = nfs_error(retry); } else { -#ifdef DEBUG - amuDebug(D_TRACE) + if (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)); @@ -538,12 +537,10 @@ nfsproc_statfs_2_svc(am_nfs_fh *argp, struct svc_req *rqstp) int retry; mntent_t mnt; -#ifdef DEBUG - amuDebug(D_TRACE) + if (amuDebug(D_TRACE)) plog(XLOG_DEBUG, "statfs:"); -#endif /* DEBUG */ - mp = fh_to_mp2(argp, &retry); + mp = fh_to_mp3(argp, &retry, VLOOK_CREATE); if (mp == 0) { if (retry < 0) { amd_stats.d_drops++; @@ -552,10 +549,8 @@ nfsproc_statfs_2_svc(am_nfs_fh *argp, struct svc_req *rqstp) res.sfr_status = nfs_error(retry); } else { nfsstatfsokres *fp; -#ifdef DEBUG - amuDebug(D_TRACE) + if (amuDebug(D_TRACE)) plog(XLOG_DEBUG, "\tstat_fs(%s)", mp->am_path); -#endif /* DEBUG */ /* * just return faked up file system information @@ -569,7 +564,7 @@ nfsproc_statfs_2_svc(am_nfs_fh *argp, struct svc_req *rqstp) 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")) { + if (amu_hasmntopt(&mnt, "browsable")) { count_map_entries(mp, &fp->sfrok_blocks, &fp->sfrok_bfree, @@ -629,3 +624,205 @@ out: *out_bfree = bfree; *out_bavail = bavail; } + + +/* + * Convert from file handle to automount node. + */ +static am_node * +fh_to_mp3(am_nfs_fh *fhp, int *rp, int vop) +{ + struct am_fh *fp = (struct am_fh *) fhp; + am_node *ap = 0; + + if (fp->u.s.fhh_type != 0) { + /* New filehandle type */ + int len = sizeof(*fhp) - sizeof(fp->fhh_gen); + char *path = xmalloc(len+1); + /* + * Because fhp is treated as a filehandle we use memcpy + * instead of xstrlcpy. + */ + memcpy(path, (char *) fp->u.fhh_path, len); + path[len] = '\0'; + /* dlog("fh_to_mp3: new filehandle: %s", path); */ + + ap = path_to_exported_ap(path); + XFREE(path); + } else { + /* dlog("fh_to_mp3: old filehandle: %d", fp->u.s.fhh_id); */ + /* + * Check process id matches + * If it doesn't then it is probably + * from an old kernel-cached filehandle + * which is now out of date. + */ + if (fp->u.s.fhh_pid != get_server_pid()) { + dlog("fh_to_mp3: wrong pid %ld != my pid %ld", + (long) fp->u.s.fhh_pid, get_server_pid()); + goto drop; + } + + /* + * Get hold of the supposed mount node + */ + ap = get_exported_ap(fp->u.s.fhh_id); + } + + /* + * Check the generation number in the node + * matches the one from the kernel. If not + * then the old node has been timed out and + * a new one allocated. + */ + if (ap != NULL && ap->am_gen != fp->fhh_gen) + ap = 0; + + /* + * If it doesn't exists then drop the request + */ + if (!ap) + goto drop; + +#if 0 + /* + * If the node is hung then locate a new node + * for it. This implements the replicated filesystem + * retries. + */ + if (ap->am_mnt && FSRV_ISDOWN(ap->am_mnt->mf_server) && ap->am_parent) { + int error; + am_node *orig_ap = ap; + + dlog("fh_to_mp3: %s (%s) is hung: lookup alternative file server", + orig_ap->am_path, orig_ap->am_mnt->mf_info); + + /* + * Update modify time of parent node. + * With any luck the kernel will re-stat + * the child node and get new information. + */ + clocktime(&orig_ap->am_fattr.na_mtime); + + /* + * Call the parent's lookup routine for an object + * with the same name. This may return -1 in error + * if a mount is in progress. In any case, if no + * mount node is returned the error code is propagated + * to the caller. + */ + if (vop == VLOOK_CREATE) { + ap = orig_ap->am_parent->am_mnt->mf_ops->lookup_child(orig_ap->am_parent, orig_ap->am_name, &error, vop); + if (ap && error < 0) + ap = orig_ap->am_parent->am_mnt->mf_ops->mount_child(ap, &error); + } else { + ap = 0; + error = ESTALE; + } + if (ap == 0) { + if (error < 0 && amd_state == Finishing) + error = ENOENT; + *rp = error; + return 0; + } + + /* + * Update last access to original node. This + * avoids timing it out and so sending ESTALE + * back to the kernel. + * XXX - Not sure we need this anymore (jsp, 90/10/6). + */ + new_ttl(orig_ap); + + } +#endif + + /* + * Disallow references to objects being unmounted, unless + * they are automount points. + */ + if (ap->am_mnt && (ap->am_mnt->mf_flags & MFF_UNMOUNTING) && + !(ap->am_flags & AMF_ROOT)) { + if (amd_state == Finishing) + *rp = ENOENT; + else + *rp = -1; + return 0; + } + new_ttl(ap); + +drop: + if (!ap || !ap->am_mnt) { + /* + * If we are shutting down then it is likely + * that this node has disappeared because of + * a fast timeout. To avoid things thrashing + * just pretend it doesn't exist at all. If + * ESTALE is returned, some NFS clients just + * keep retrying (stupid or what - if it's + * stale now, what's it going to be in 5 minutes?) + */ + if (amd_state == Finishing) + *rp = ENOENT; + else + *rp = ESTALE; + amd_stats.d_stale++; + } + + return ap; +} + + +static am_node * +fh_to_mp(am_nfs_fh *fhp) +{ + int dummy; + + return fh_to_mp3(fhp, &dummy, VLOOK_CREATE); +} + + +/* + * Convert from automount node to file handle. + */ +void +mp_to_fh(am_node *mp, am_nfs_fh *fhp) +{ + u_int pathlen; + struct am_fh *fp = (struct am_fh *) fhp; + + memset((char *) fhp, 0, sizeof(am_nfs_fh)); + + /* Store the generation number */ + fp->fhh_gen = mp->am_gen; + + pathlen = strlen(mp->am_path); + if (pathlen <= sizeof(*fhp) - sizeof(fp->fhh_gen)) { + /* dlog("mp_to_fh: new filehandle: %s", mp->am_path); */ + + /* + * Because fhp is treated as a filehandle we use memcpy instead of + * xstrlcpy. + */ + memcpy(fp->u.fhh_path, mp->am_path, pathlen); /* making a filehandle */ + } else { + /* + * Take the process id + */ + fp->u.s.fhh_pid = get_server_pid(); + + /* + * ... the map number + */ + fp->u.s.fhh_id = mp->am_mapno; + + /* + * ... and the generation number (previously stored) + * to make a "unique" triple that will never + * be reallocated except across reboots (which doesn't matter) + * or if we are unlucky enough to be given the same + * pid as a previous amd (very unlikely). + */ + /* dlog("mp_to_fh: old filehandle: %d", fp->u.s.fhh_id); */ + } +} diff --git a/contrib/amd/amd/ops_TEMPLATE.c b/contrib/amd/amd/ops_TEMPLATE.c index dcec280..3f74219 100644 --- a/contrib/amd/amd/ops_TEMPLATE.c +++ b/contrib/amd/amd/ops_TEMPLATE.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: ops_TEMPLATE.c,v 1.3.2.5 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/ops_TEMPLATE.c * */ @@ -62,16 +61,14 @@ /* 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 int foofs_mount(am_node *mp, mntfs *mf); +static int foofs_umount(am_node *mp, mntfs *mf); static am_node *foofs_lookuppn(am_node *mp, char *fname, int *error_return, int op); -static int foofs_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, int count); +static int foofs_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, u_int count); static am_node *foofs_readlink(am_node *mp, int *error_return); -static void foofs_mounted(mntfs *mf); -static void foofs_umounted(am_node *mp); -fserver *foofs_ffserver(mntfs *mf); +static void foofs_mounted(am_node *am, mntfs *mf); +static void foofs_umounted(am_node *mp, mntfs *mf); +static fserver *foofs_ffserver(mntfs *mf); /* @@ -84,16 +81,19 @@ am_ops foofs_ops = 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_lookup_child, /* lookup path-name */ + foofs_mount_child, /* mount path-name */ foofs_readdir, /* read directory */ foofs_readlink, /* read link */ foofs_mounted, /* after-mount extra actions */ foofs_umounted, /* after-umount extra actions */ foofs_ffserver, /* find a file server */ - FS_MKMNT | FS_BACKGROUND | FS_AMQINFO /* flags */ + foofs_get_wchan, /* return the waiting channel */ + FS_MKMNT | FS_BACKGROUND | FS_AMQINFO, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_TEMPLATE_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ }; @@ -220,7 +220,7 @@ foofs_lookuppn(am_node *mp, char *fname, int *error_return, int op) * 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) +foofs_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, u_int count) { int error = 0; @@ -284,10 +284,22 @@ foofs_umounted(am_node *mp) * Find a file server. * Returns: fserver of found server, or NULL if not found. */ -fserver * +static fserver * foofs_ffserver(mntfs *mf) { plog(XLOG_INFO, "entering foofs_ffserver..."); return NULL; } + + +/* + * Normally just return mf. Only inherit needs to do special tricks. + */ +static wchan_t * +foofs_get_wchan(mntfs *mf) +{ + plog(XLOG_INFO, "entering foofs_get_wchan..."); + + return mf; +} diff --git a/contrib/amd/amd/ops_cachefs.c b/contrib/amd/amd/ops_cachefs.c index 08c4875..d3e303b 100644 --- a/contrib/amd/amd/ops_cachefs.c +++ b/contrib/amd/amd/ops_cachefs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: ops_cachefs.c,v 1.3.2.4 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/ops_cachefs.c * */ @@ -55,8 +54,8 @@ /* 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); +static int cachefs_mount(am_node *am, mntfs *mf); +static int cachefs_umount(am_node *am, mntfs *mf); /* @@ -67,17 +66,20 @@ am_ops cachefs_ops = "cachefs", cachefs_match, cachefs_init, - amfs_auto_fmount, - cachefs_fmount, - amfs_auto_fumount, - cachefs_fumount, - amfs_error_lookuppn, + cachefs_mount, + cachefs_umount, + amfs_error_lookup_child, + amfs_error_mount_child, 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 + 0, /* cachefs_mounted */ + 0, /* cachefs_umounted */ + amfs_generic_find_srvr, + 0, /* cachefs_get_wchan */ + FS_MKMNT | FS_NOTIMEOUT | FS_UBACKGROUND | FS_AMQINFO, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_CACHEFS_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ }; @@ -94,9 +96,7 @@ cachefs_match(am_opts *fo) 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); @@ -113,7 +113,7 @@ cachefs_init(mntfs *mf) /* * Save cache directory name */ - if (mf->mf_refc == 1) { + if (!mf->mf_private) { mf->mf_private = (voidp) strdup(mf->mf_fo->opt_cachedir); mf->mf_prfree = (void (*)(voidp)) free; } @@ -128,7 +128,8 @@ cachefs_init(mntfs *mf) * cachedir is the cache directory ($cachedir) */ static int -mount_cachefs(char *mntpt, char *backdir, char *cachedir, char *opts) +mount_cachefs(char *mntdir, char *backdir, char *cachedir, + char *opts, int on_autofs) { cachefs_args_t ca; mntent_t mnt; @@ -142,12 +143,16 @@ mount_cachefs(char *mntpt, char *backdir, char *cachedir, char *opts) * Fill in the mount structure */ memset((voidp) &mnt, 0, sizeof(mnt)); - mnt.mnt_dir = mntpt; + mnt.mnt_dir = mntdir; mnt.mnt_fsname = backdir; mnt.mnt_type = MNTTAB_TYPE_CACHEFS; mnt.mnt_opts = opts; flags = compute_mount_flags(&mnt); +#ifdef HAVE_FS_AUTOFS + if (on_autofs) + flags |= autofs_compute_mount_flags(&mnt); +#endif /* HAVE_FS_AUTOFS */ /* Fill in cachefs mount arguments */ @@ -170,8 +175,13 @@ mount_cachefs(char *mntpt, char *backdir, char *cachedir, char *opts) /* 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); + /* + * Append cacheid and mountpoint. + * sizeof(cfs_cacheid) should be C_MAX_MOUNT_FSCDIRNAME as per + * <sys/fs/cachefs_fs.h> (checked on Solaris 8). + */ + xsnprintf(ca.cfs_cacheid, sizeof(ca.cfs_cacheid), + "%s:%s", ca.cfs_fsid, mntdir); /* convert '/' to '_' (Solaris does that...) */ cp = ca.cfs_cacheid; while ((cp = strpbrk(cp, "/")) != NULL) @@ -192,19 +202,21 @@ mount_cachefs(char *mntpt, char *backdir, char *cachedir, char *opts) /* * Call generic mount routine */ - return mount_fs(&mnt, flags, (caddr_t) &ca, 0, type, 0, NULL, mnttab_file_name); + return mount_fs(&mnt, flags, (caddr_t) &ca, 0, type, 0, NULL, mnttab_file_name, on_autofs); } static int -cachefs_fmount(mntfs *mf) +cachefs_mount(am_node *am, mntfs *mf) { + int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; int error; error = mount_cachefs(mf->mf_mount, mf->mf_fo->opt_rfs, mf->mf_fo->opt_cachedir, - mf->mf_mopts); + mf->mf_mopts, + on_autofs); if (error) { errno = error; /* according to Solaris, if errno==ESRCH, "options to not match" */ @@ -220,11 +232,12 @@ cachefs_fmount(mntfs *mf) static int -cachefs_fumount(mntfs *mf) +cachefs_umount(am_node *am, mntfs *mf) { + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; int error; - error = UMOUNT_FS(mf->mf_mount, mnttab_file_name); + error = UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); /* * In the case of cachefs, we must fsck the cache directory. Otherwise, @@ -239,7 +252,7 @@ cachefs_fumount(mntfs *mf) cachedir = (char *) mf->mf_private; plog(XLOG_INFO, "running fsck on cache directory \"%s\"", cachedir); - sprintf(cmd, "fsck -F cachefs %s", cachedir); + xsnprintf(cmd, sizeof(cmd), "fsck -F cachefs %s", cachedir); system(cmd); } diff --git a/contrib/amd/amd/ops_cdfs.c b/contrib/amd/amd/ops_cdfs.c index 6487e6d..40355ac 100644 --- a/contrib/amd/amd/ops_cdfs.c +++ b/contrib/amd/amd/ops_cdfs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: ops_cdfs.c,v 1.4.2.5 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/ops_cdfs.c * */ @@ -54,8 +53,8 @@ /* forward declarations */ static char *cdfs_match(am_opts *fo); -static int cdfs_fmount(mntfs *mf); -static int cdfs_fumount(mntfs *mf); +static int cdfs_mount(am_node *am, mntfs *mf); +static int cdfs_umount(am_node *am, mntfs *mf); /* * Ops structure @@ -65,17 +64,20 @@ am_ops cdfs_ops = "cdfs", cdfs_match, 0, /* cdfs_init */ - amfs_auto_fmount, - cdfs_fmount, - amfs_auto_fumount, - cdfs_fumount, - amfs_error_lookuppn, + cdfs_mount, + cdfs_umount, + amfs_error_lookup_child, + amfs_error_mount_child, amfs_error_readdir, 0, /* cdfs_readlink */ 0, /* cdfs_mounted */ 0, /* cdfs_umounted */ - find_amfs_auto_srvr, - FS_MKMNT | FS_UBACKGROUND | FS_AMQINFO + amfs_generic_find_srvr, + 0, /* cdfs_get_wchan */ + FS_MKMNT | FS_UBACKGROUND | FS_AMQINFO, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_CDFS_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ }; @@ -89,10 +91,8 @@ cdfs_match(am_opts *fo) 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 @@ -102,11 +102,11 @@ cdfs_match(am_opts *fo) static int -mount_cdfs(char *dir, char *fs_name, char *opts) +mount_cdfs(char *mntdir, char *fs_name, char *opts, int on_autofs) { cdfs_args_t cdfs_args; mntent_t mnt; - int genflags, cdfs_flags; + int genflags, cdfs_flags, retval; /* * Figure out the name of the file system type. @@ -120,13 +120,13 @@ mount_cdfs(char *dir, char *fs_name, char *opts) * Fill in the mount structure */ memset((voidp) &mnt, 0, sizeof(mnt)); - mnt.mnt_dir = dir; + mnt.mnt_dir = mntdir; mnt.mnt_fsname = fs_name; mnt.mnt_type = MNTTAB_TYPE_CDFS; mnt.mnt_opts = opts; #if defined(MNT2_CDFS_OPT_DEFPERM) && defined(MNTTAB_OPT_DEFPERM) - if (hasmntopt(&mnt, MNTTAB_OPT_DEFPERM)) + if (amu_hasmntopt(&mnt, MNTTAB_OPT_DEFPERM)) # ifdef MNT2_CDFS_OPT_DEFPERM cdfs_flags |= MNT2_CDFS_OPT_DEFPERM; # else /* not MNT2_CDFS_OPT_DEFPERM */ @@ -135,34 +135,38 @@ mount_cdfs(char *dir, char *fs_name, char *opts) #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)) + if (amu_hasmntopt(&mnt, MNTTAB_OPT_NODEFPERM)) cdfs_flags |= MNT2_CDFS_OPT_NODEFPERM; #endif /* MNTTAB_OPT_NODEFPERM */ #if defined(MNT2_CDFS_OPT_NOVERSION) && defined(MNTTAB_OPT_NOVERSION) - if (hasmntopt(&mnt, MNTTAB_OPT_NOVERSION)) + if (amu_hasmntopt(&mnt, MNTTAB_OPT_NOVERSION)) cdfs_flags |= MNT2_CDFS_OPT_NOVERSION; #endif /* defined(MNT2_CDFS_OPT_NOVERSION) && defined(MNTTAB_OPT_NOVERSION) */ #if defined(MNT2_CDFS_OPT_RRIP) && defined(MNTTAB_OPT_RRIP) - if (hasmntopt(&mnt, MNTTAB_OPT_RRIP)) + if (amu_hasmntopt(&mnt, MNTTAB_OPT_RRIP)) cdfs_flags |= MNT2_CDFS_OPT_RRIP; #endif /* defined(MNT2_CDFS_OPT_RRIP) && defined(MNTTAB_OPT_RRIP) */ #if defined(MNT2_CDFS_OPT_NORRIP) && defined(MNTTAB_OPT_NORRIP) - if (hasmntopt(&mnt, MNTTAB_OPT_NORRIP)) + if (amu_hasmntopt(&mnt, MNTTAB_OPT_NORRIP)) cdfs_flags |= MNT2_CDFS_OPT_NORRIP; #endif /* defined(MNT2_CDFS_OPT_NORRIP) && defined(MNTTAB_OPT_NORRIP) */ #if defined(MNT2_CDFS_OPT_GENS) && defined(MNTTAB_OPT_GENS) - if (hasmntopt(&mnt, MNTTAB_OPT_GENS)) + if (amu_hasmntopt(&mnt, MNTTAB_OPT_GENS)) cdfs_flags |= MNT2_CDFS_OPT_GENS; #endif /* defined(MNT2_CDFS_OPT_GENS) && defined(MNTTAB_OPT_GENS) */ #if defined(MNT2_CDFS_OPT_EXTATT) && defined(MNTTAB_OPT_EXTATT) - if (hasmntopt(&mnt, MNTTAB_OPT_EXTATT)) + if (amu_hasmntopt(&mnt, MNTTAB_OPT_EXTATT)) cdfs_flags |= MNT2_CDFS_OPT_EXTATT; #endif /* defined(MNT2_CDFS_OPT_EXTATT) && defined(MNTTAB_OPT_EXTATT) */ genflags = compute_mount_flags(&mnt); +#ifdef HAVE_FS_AUTOFS + if (on_autofs) + genflags |= autofs_compute_mount_flags(&mnt); +#endif /* HAVE_FS_AUTOFS */ #ifdef HAVE_CDFS_ARGS_T_FLAGS cdfs_args.flags = cdfs_flags; @@ -176,10 +180,6 @@ mount_cdfs(char *dir, char *fs_name, char *opts) cdfs_args.iso_pgthresh = hasmntval(&mnt, MNTTAB_OPT_PGTHRESH); #endif /* HAVE_CDFS_ARGS_T_ISO_PGTHRESH */ -#ifdef HAVE_CDFS_ARGS_T_FSPEC - cdfs_args.fspec = fs_name; -#endif /* HAVE_CDFS_ARGS_T_FSPEC */ - #ifdef HAVE_CDFS_ARGS_T_NORRIP /* XXX: need to provide norrip mount opt */ cdfs_args.norrip = 0; /* use Rock-Ridge Protocol extensions */ @@ -190,19 +190,26 @@ mount_cdfs(char *dir, char *fs_name, char *opts) cdfs_args.ssector = 0; /* use 1st session on disk */ #endif /* HAVE_CDFS_ARGS_T_SSECTOR */ +#ifdef HAVE_CDFS_ARGS_T_FSPEC + cdfs_args.fspec = fs_name; +#endif /* HAVE_CDFS_ARGS_T_FSPEC */ + /* * Call generic mount routine */ - return mount_fs(&mnt, genflags, (caddr_t) &cdfs_args, 0, type, 0, NULL, mnttab_file_name); + retval = mount_fs(&mnt, genflags, (caddr_t) &cdfs_args, 0, type, 0, NULL, mnttab_file_name, on_autofs); + + return retval; } static int -cdfs_fmount(mntfs *mf) +cdfs_mount(am_node *am, mntfs *mf) { + int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; int error; - error = mount_cdfs(mf->mf_mount, mf->mf_info, mf->mf_mopts); + error = mount_cdfs(mf->mf_mount, mf->mf_info, mf->mf_mopts, on_autofs); if (error) { errno = error; plog(XLOG_ERROR, "mount_cdfs: %m"); @@ -213,7 +220,9 @@ cdfs_fmount(mntfs *mf) static int -cdfs_fumount(mntfs *mf) +cdfs_umount(am_node *am, mntfs *mf) { - return UMOUNT_FS(mf->mf_mount, mnttab_file_name); + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + + return UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); } diff --git a/contrib/amd/amd/ops_efs.c b/contrib/amd/amd/ops_efs.c index 36f47ed..047fe1e 100644 --- a/contrib/amd/amd/ops_efs.c +++ b/contrib/amd/amd/ops_efs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: ops_efs.c,v 1.3.2.5 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/ops_efs.c * */ @@ -54,8 +53,8 @@ /* forward declarations */ static char *efs_match(am_opts *fo); -static int efs_fmount(mntfs *mf); -static int efs_fumount(mntfs *mf); +static int efs_mount(am_node *am, mntfs *mf); +static int efs_umount(am_node *am, mntfs *mf); /* * Ops structure @@ -65,17 +64,20 @@ am_ops efs_ops = "efs", efs_match, 0, /* efs_init */ - amfs_auto_fmount, - efs_fmount, - amfs_auto_fumount, - efs_fumount, - amfs_error_lookuppn, + efs_mount, + efs_umount, + amfs_error_lookup_child, + amfs_error_mount_child, amfs_error_readdir, 0, /* efs_readlink */ 0, /* efs_mounted */ 0, /* efs_umounted */ - find_amfs_auto_srvr, - FS_MKMNT | FS_NOTIMEOUT | FS_UBACKGROUND | FS_AMQINFO + amfs_generic_find_srvr, + 0, /* efs_get_wchan */ + FS_MKMNT | FS_NOTIMEOUT | FS_UBACKGROUND | FS_AMQINFO, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_EFS_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ }; @@ -91,9 +93,7 @@ efs_match(am_opts *fo) 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 @@ -103,7 +103,7 @@ efs_match(am_opts *fo) static int -mount_efs(char *dir, char *fs_name, char *opts) +mount_efs(char *mntdir, char *fs_name, char *opts, int on_autofs) { efs_args_t efs_args; mntent_t mnt; @@ -120,12 +120,16 @@ mount_efs(char *dir, char *fs_name, char *opts) * Fill in the mount structure */ memset((voidp) &mnt, 0, sizeof(mnt)); - mnt.mnt_dir = dir; + mnt.mnt_dir = mntdir; mnt.mnt_fsname = fs_name; mnt.mnt_type = MNTTAB_TYPE_EFS; mnt.mnt_opts = opts; flags = compute_mount_flags(&mnt); +#ifdef HAVE_FS_AUTOFS + if (on_autofs) + flags |= autofs_compute_mount_flags(&mnt); +#endif /* HAVE_FS_AUTOFS */ #ifdef HAVE_EFS_ARGS_T_FLAGS efs_args.flags = 0; /* XXX: fix this to correct flags */ @@ -137,16 +141,17 @@ mount_efs(char *dir, char *fs_name, char *opts) /* * Call generic mount routine */ - return mount_fs(&mnt, flags, (caddr_t) &efs_args, 0, type, 0, NULL, mnttab_file_name); + return mount_fs(&mnt, flags, (caddr_t) &efs_args, 0, type, 0, NULL, mnttab_file_name, on_autofs); } static int -efs_fmount(mntfs *mf) +efs_mount(am_node *am, mntfs *mf) { + int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; int error; - error = mount_efs(mf->mf_mount, mf->mf_info, mf->mf_mopts); + error = mount_efs(mf->mf_mount, mf->mf_info, mf->mf_mopts, on_autofs); if (error) { errno = error; plog(XLOG_ERROR, "mount_efs: %m"); @@ -158,7 +163,10 @@ efs_fmount(mntfs *mf) static int -efs_fumount(mntfs *mf) +efs_umount(am_node *am, mntfs *mf) { - return UMOUNT_FS(mf->mf_mount, mnttab_file_name); + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + + return UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); } + diff --git a/contrib/amd/amd/ops_lofs.c b/contrib/amd/amd/ops_lofs.c index 6d27d2c..26fdc9f 100644 --- a/contrib/amd/amd/ops_lofs.c +++ b/contrib/amd/amd/ops_lofs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: ops_lofs.c,v 1.3.2.5 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/ops_lofs.c * */ @@ -54,10 +53,8 @@ /* 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); - +static int lofs_mount(am_node *am, mntfs *mf); +static int lofs_umount(am_node *am, mntfs *mf); /* * Ops structure @@ -67,17 +64,20 @@ am_ops lofs_ops = "lofs", lofs_match, 0, /* lofs_init */ - amfs_auto_fmount, - lofs_fmount, - amfs_auto_fumount, - lofs_fumount, - amfs_error_lookuppn, + lofs_mount, + lofs_umount, + amfs_error_lookup_child, + amfs_error_mount_child, amfs_error_readdir, 0, /* lofs_readlink */ 0, /* lofs_mounted */ 0, /* lofs_umounted */ - find_amfs_auto_srvr, - FS_MKMNT | FS_NOTIMEOUT | FS_UBACKGROUND | FS_AMQINFO + amfs_generic_find_srvr, + 0, /* lofs_get_wchan */ + FS_MKMNT | FS_NOTIMEOUT | FS_UBACKGROUND | FS_AMQINFO, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_LOFS_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ }; @@ -91,10 +91,8 @@ lofs_match(am_opts *fo) 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 @@ -103,8 +101,8 @@ lofs_match(am_opts *fo) } -static int -mount_lofs(char *dir, char *fs_name, char *opts) +int +mount_lofs(char *mntdir, char *fs_name, char *opts, int on_autofs) { mntent_t mnt; int flags; @@ -118,26 +116,31 @@ mount_lofs(char *dir, char *fs_name, char *opts) * Fill in the mount structure */ memset((voidp) &mnt, 0, sizeof(mnt)); - mnt.mnt_dir = dir; + mnt.mnt_dir = mntdir; mnt.mnt_fsname = fs_name; mnt.mnt_type = MNTTAB_TYPE_LOFS; mnt.mnt_opts = opts; flags = compute_mount_flags(&mnt); +#ifdef HAVE_FS_AUTOFS + if (on_autofs) + flags |= autofs_compute_mount_flags(&mnt); +#endif /* HAVE_FS_AUTOFS */ /* * Call generic mount routine */ - return mount_fs(&mnt, flags, NULL, 0, type, 0, NULL, mnttab_file_name); + return mount_fs(&mnt, flags, NULL, 0, type, 0, NULL, mnttab_file_name, on_autofs); } static int -lofs_fmount(mntfs *mf) +lofs_mount(am_node *am, mntfs *mf) { + int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; int error; - error = mount_lofs(mf->mf_mount, mf->mf_info, mf->mf_mopts); + error = mount_lofs(mf->mf_mount, mf->mf_info, mf->mf_mopts, on_autofs); if (error) { errno = error; plog(XLOG_ERROR, "mount_lofs: %m"); @@ -148,7 +151,9 @@ lofs_fmount(mntfs *mf) static int -lofs_fumount(mntfs *mf) +lofs_umount(am_node *am, mntfs *mf) { - return UMOUNT_FS(mf->mf_mount, mnttab_file_name); + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + + return UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); } diff --git a/contrib/amd/amd/ops_mfs.c b/contrib/amd/amd/ops_mfs.c index 67d6e98..ccaa49c 100644 --- a/contrib/amd/amd/ops_mfs.c +++ b/contrib/amd/amd/ops_mfs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: ops_mfs.c,v 1.3.2.4 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/ops_mfs.c * */ diff --git a/contrib/amd/amd/ops_nfs.c b/contrib/amd/amd/ops_nfs.c index 5f6a474..a6a8585 100644 --- a/contrib/amd/amd/ops_nfs.c +++ b/contrib/amd/amd/ops_nfs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: ops_nfs.c,v 1.6.2.6 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/ops_nfs.c * */ @@ -64,7 +63,7 @@ */ #define FH_TTL (5 * 60) /* five minutes */ #define FH_TTL_ERROR (30) /* 30 seconds */ -#define FHID_ALLOC(struct) (++fh_id) +#define FHID_ALLOC() (++fh_id) /* * The NFS layer maintains a cache of file handles. @@ -92,19 +91,26 @@ typedef struct fh_cache fh_cache; struct fh_cache { qelem fh_q; /* List header */ - voidp fh_wchan; /* Wait channel */ + wchan_t fh_wchan; /* Wait channel */ int fh_error; /* Valid data? */ int fh_id; /* Unique id */ int fh_cid; /* Callout id */ u_long fh_nfs_version; /* highest NFS version on host */ am_nfs_handle_t fh_nfs_handle; /* Handle on filesystem */ + int fh_status; /* Status of last rpc */ struct sockaddr_in fh_sin; /* Address of mountd */ fserver *fh_fs; /* Server holding filesystem */ char *fh_path; /* Filesystem on host */ }; /* forward definitions */ -static int call_mountd(fh_cache *fp, u_long proc, fwd_fun f, voidp wchan); +static int nfs_init(mntfs *mf); +static char *nfs_match(am_opts *fo); +static int nfs_mount(am_node *am, mntfs *mf); +static int nfs_umount(am_node *am, mntfs *mf); +static void nfs_umounted(mntfs *mf); +static int call_mountd(fh_cache *fp, u_long proc, fwd_fun f, wchan_t wchan); +static int webnfs_lookup(fh_cache *fp, fwd_fun f, wchan_t wchan); static int fh_id = 0; /* globals */ @@ -119,25 +125,28 @@ am_ops nfs_ops = "nfs", nfs_match, nfs_init, - amfs_auto_fmount, - nfs_fmount, - amfs_auto_fumount, - nfs_fumount, - amfs_error_lookuppn, + nfs_mount, + nfs_umount, + amfs_error_lookup_child, + amfs_error_mount_child, amfs_error_readdir, 0, /* nfs_readlink */ 0, /* nfs_mounted */ nfs_umounted, find_nfs_srvr, - FS_MKMNT | FS_BACKGROUND | FS_AMQINFO + 0, /* nfs_get_wchan */ + FS_MKMNT | FS_BACKGROUND | FS_AMQINFO, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_NFS_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ }; static fh_cache * -find_nfs_fhandle_cache(voidp idv, int done) +find_nfs_fhandle_cache(opaque_t arg, int done) { fh_cache *fp, *fp2 = 0; - int id = (long) idv; /* for 64-bit archs */ + int id = (long) arg; /* for 64-bit archs */ ITER(fp, fh_cache, &fh_head) { if (fp->fh_id == id) { @@ -146,13 +155,11 @@ find_nfs_fhandle_cache(voidp idv, int done) } } -#ifdef DEBUG if (fp2) { dlog("fh cache gives fp %#lx, fs %s", (unsigned long) fp2, fp2->fh_path); } else { dlog("fh cache search failed"); } -#endif /* DEBUG */ if (fp2 && !done) { fp2->fh_error = ETIMEDOUT; @@ -164,14 +171,18 @@ find_nfs_fhandle_cache(voidp idv, int done) /* - * Called when a filehandle appears + * Called when a filehandle appears via the mount protocol */ static void -got_nfs_fh(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, voidp idv, int done) +got_nfs_fh_mount(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, opaque_t arg, int done) { fh_cache *fp; + struct fhstatus res; +#ifdef HAVE_FS_NFS3 + struct am_mountres3 res3; +#endif /* HAVE_FS_NFS3 */ - fp = find_nfs_fhandle_cache(idv, done); + fp = find_nfs_fhandle_cache(arg, done); if (!fp) return; @@ -180,28 +191,109 @@ got_nfs_fh(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, v * 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 + if (fp->fh_nfs_version == NFS_VERSION3) { + memset(&res3, 0, sizeof(res3)); + fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &res3, + (XDRPROC_T_TYPE) xdr_am_mountres3); + fp->fh_status = unx_error(res3.fhs_status); + memset(&fp->fh_nfs_handle.v3, 0, sizeof(am_nfs_fh3)); + fp->fh_nfs_handle.v3.am_fh3_length = res3.mountres3_u.mountinfo.fhandle.fhandle3_len; + memmove(fp->fh_nfs_handle.v3.am_fh3_data, + res3.mountres3_u.mountinfo.fhandle.fhandle3_val, + fp->fh_nfs_handle.v3.am_fh3_length); + } else { #endif /* HAVE_FS_NFS3 */ - fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &fp->fh_nfs_handle.v2, + memset(&res, 0, sizeof(res)); + fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &res, (XDRPROC_T_TYPE) xdr_fhstatus); + fp->fh_status = unx_error(res.fhs_status); + memmove(&fp->fh_nfs_handle.v2, &res.fhs_fh, NFS_FHSIZE); +#ifdef HAVE_FS_NFS3 + } +#endif /* HAVE_FS_NFS3 */ if (!fp->fh_error) { -#ifdef DEBUG dlog("got filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path); -#endif /* DEBUG */ + } else { + plog(XLOG_USER, "filehandle denied for %s:%s", fp->fh_fs->fs_host, fp->fh_path); + /* + * Force the error to be EACCES. It's debatable whether it should be + * ENOENT instead, but the server really doesn't give us any clues, and + * EACCES is more in line with the "filehandle denied" message. + */ + fp->fh_error = EACCES; + } + + /* + * Wakeup anything sleeping on this filehandle + */ + if (fp->fh_wchan) { + dlog("Calling wakeup on %#lx", (unsigned long) fp->fh_wchan); + wakeup(fp->fh_wchan); + } +} + +/* + * Called when a filehandle appears via WebNFS + */ +static void +got_nfs_fh_webnfs(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, opaque_t arg, int done) +{ + fh_cache *fp; + nfsdiropres res; +#ifdef HAVE_FS_NFS3 + am_LOOKUP3res res3; +#endif /* HAVE_FS_NFS3 */ + + fp = find_nfs_fhandle_cache(arg, done); + if (!fp) + return; + + /* + * retrieve the correct RPC reply for the file handle, based on the + * NFS protocol version. + */ +#ifdef HAVE_FS_NFS3 + if (fp->fh_nfs_version == NFS_VERSION3) { + memset(&res3, 0, sizeof(res3)); + fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &res3, + (XDRPROC_T_TYPE) xdr_am_LOOKUP3res); + fp->fh_status = unx_error(res3.status); + memset(&fp->fh_nfs_handle.v3, 0, sizeof(am_nfs_fh3)); + fp->fh_nfs_handle.v3.am_fh3_length = res3.res_u.ok.object.am_fh3_length; + memmove(fp->fh_nfs_handle.v3.am_fh3_data, + res3.res_u.ok.object.am_fh3_data, + fp->fh_nfs_handle.v3.am_fh3_length); + } else { +#endif /* HAVE_FS_NFS3 */ + memset(&res, 0, sizeof(res)); + fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &res, + (XDRPROC_T_TYPE) xdr_diropres); + fp->fh_status = unx_error(res.dr_status); + memmove(&fp->fh_nfs_handle.v2, &res.dr_u.dr_drok_u.drok_fhandle, NFS_FHSIZE); +#ifdef HAVE_FS_NFS3 + } +#endif /* HAVE_FS_NFS3 */ + + if (!fp->fh_error) { + dlog("got filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path); + } else { + plog(XLOG_USER, "filehandle denied for %s:%s", fp->fh_fs->fs_host, fp->fh_path); /* - * Wakeup anything sleeping on this filehandle + * Force the error to be EACCES. It's debatable whether it should be + * ENOENT instead, but the server really doesn't give us any clues, and + * EACCES is more in line with the "filehandle denied" message. */ - if (fp->fh_wchan) { -#ifdef DEBUG - dlog("Calling wakeup on %#lx", (unsigned long) fp->fh_wchan); -#endif /* DEBUG */ - wakeup(fp->fh_wchan); - } + fp->fh_error = EACCES; + } + + /* + * Wakeup anything sleeping on this filehandle + */ + if (fp->fh_wchan) { + dlog("Calling wakeup on %#lx", (unsigned long) fp->fh_wchan); + wakeup(fp->fh_wchan); } } @@ -212,8 +304,12 @@ 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; + if (fp->fh_fs == fs || fs == NULL) { + /* + * Only invalidate port info for non-WebNFS servers + */ + if (!(fp->fh_fs->fs_flags & FSF_WEBNFS)) + fp->fh_sin.sin_port = (u_short) 0; fp->fh_error = -1; } } @@ -221,15 +317,13 @@ flush_nfs_fhandle_cache(fserver *fs) static void -discard_fh(voidp v) +discard_fh(opaque_t arg) { - fh_cache *fp = v; + fh_cache *fp = (fh_cache *) arg; 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) @@ -242,88 +336,92 @@ discard_fh(voidp v) * Determine the file handle for a node */ static int -prime_nfs_fhandle_cache(char *path, fserver *fs, am_nfs_handle_t *fhbuf, voidp wchan) +prime_nfs_fhandle_cache(char *path, fserver *fs, am_nfs_handle_t *fhbuf, mntfs *mf) { fh_cache *fp, *fp_save = 0; int error; int reuse_id = FALSE; -#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", (int) 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) { + if (fs != fp->fh_fs || !STREQ(path, fp->fh_path)) + continue; /* skip to next ITER item */ + /* else we got a match */ + switch (fp->fh_error) { + case 0: + plog(XLOG_INFO, "prime_nfs_fhandle_cache: NFS version %d", (int) fp->fh_nfs_version); + + error = fp->fh_error = fp->fh_status; + + if (error == 0) { + if (mf->mf_flags & MFF_NFS_SCALEDOWN) { + fp_save = fp; + /* XXX: why reuse the ID? */ + reuse_id = TRUE; + break; + } + + if (fhbuf) { #ifdef HAVE_FS_NFS3 - if (fp->fh_nfs_version == NFS_VERSION3) - memmove((voidp) &(fhbuf->v3), (voidp) &(fp->fh_nfs_handle.v3), - sizeof(fp->fh_nfs_handle.v3)); - else + 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); + } } - + if (fp->fh_cid) + untimeout(fp->fh_cid); + fp->fh_cid = timeout(FH_TTL, discard_fh, (opaque_t) fp); + } else if (error == EACCES) { /* - * The error was returned from the remote mount daemon. - * Policy: this error will be cached for now... + * Now decode the file handle return code. */ - return error; + 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); + } - case -1: - /* - * Still thinking about it, but we can re-use. - */ - fp_save = fp; - reuse_id = TRUE; - break; + /* + * The error was returned from the remote mount daemon. + * Policy: this error will be cached for now... + */ + return error; - 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; - } + case -1: + /* + * Still thinking about it, but we can re-use. + */ + fp_save = fp; + reuse_id = TRUE; break; - } - } + + default: + /* + * Return the error. + * Policy: make sure we recompute if required again + * in case this was caused by a network failure. + * This can thrash mountd's though... If you find + * your mountd going slowly then: + * 1. Add a fork() loop to main. + * 2. Remove the call to innetgr() and don't use + * netgroups, especially if you don't use YP. + */ + error = fp->fh_error; + fp->fh_error = -1; + return error; + } /* end of switch statement */ + } /* end of ITER loop */ /* * Not in cache @@ -342,13 +440,13 @@ prime_nfs_fhandle_cache(char *path, fserver *fs, am_nfs_handle_t *fhbuf, voidp w ins_que(&fp->fh_q, &fh_head); } if (!reuse_id) - fp->fh_id = FHID_ALLOC(struct ); - fp->fh_wchan = wchan; + fp->fh_id = FHID_ALLOC(); + fp->fh_wchan = get_mntfs_wchan(mf); fp->fh_error = -1; - fp->fh_cid = timeout(FH_TTL, discard_fh, (voidp) fp); + fp->fh_cid = timeout(FH_TTL, discard_fh, (opaque_t) fp); /* - * if fs->fs_ip is null, remote server is probably down. + * If fs->fs_ip is null, remote server is probably down. */ if (!fs->fs_ip) { /* Mark the fileserver down and invalid again */ @@ -359,18 +457,25 @@ prime_nfs_fhandle_cache(char *path, fserver *fs, am_nfs_handle_t *fhbuf, voidp w } /* - * If the address has changed then don't try to re-use the - * port information + * Either fp has been freshly allocated or the address has changed. + * Initialize address and nfs version. Don't try to re-use the port + * information unless using WebNFS where the port is fixed either by + * the spec or the "port" mount option. */ if (fp->fh_sin.sin_addr.s_addr != fs->fs_ip->sin_addr.s_addr) { fp->fh_sin = *fs->fs_ip; - fp->fh_sin.sin_port = 0; + if (!(mf->mf_flags & MFF_WEBNFS)) + fp->fh_sin.sin_port = 0; fp->fh_nfs_version = fs->fs_version; } + fp->fh_fs = dup_srvr(fs); fp->fh_path = strdup(path); - error = call_mountd(fp, MOUNTPROC_MNT, got_nfs_fh, wchan); + if (mf->mf_flags & MFF_WEBNFS) + error = webnfs_lookup(fp, got_nfs_fh_webnfs, get_mntfs_wchan(mf)); + else + error = call_mountd(fp, MOUNTPROC_MNT, got_nfs_fh_mount, get_mntfs_wchan(mf)); if (error) { /* * Local error - cache for a short period @@ -378,7 +483,7 @@ prime_nfs_fhandle_cache(char *path, fserver *fs, am_nfs_handle_t *fhbuf, voidp w */ untimeout(fp->fh_cid); fp->fh_cid = timeout(error < 0 ? 2 * ALLOWED_MOUNT_TIME : FH_TTL_ERROR, - discard_fh, (voidp) fp); + discard_fh, (opaque_t) fp); fp->fh_error = error; } else { error = fp->fh_error; @@ -419,11 +524,11 @@ make_nfs_auth(void) static int -call_mountd(fh_cache *fp, u_long proc, fwd_fun f, voidp wchan) +call_mountd(fh_cache *fp, u_long proc, fwd_fun fun, wchan_t wchan) { struct rpc_msg mnt_msg; int len; - char iobuf[8192]; + char iobuf[UDPMSGSIZE]; int error; u_long mnt_version; @@ -434,17 +539,17 @@ call_mountd(fh_cache *fp, u_long proc, fwd_fun f, voidp wchan) } if (fp->fh_sin.sin_port == 0) { - u_short port; - error = nfs_srvr_port(fp->fh_fs, &port, wchan); + u_short mountd_port; + error = get_mountd_port(fp->fh_fs, &mountd_port, wchan); if (error) return error; - fp->fh_sin.sin_port = port; + fp->fh_sin.sin_port = mountd_port; } /* find the right version of the mount protocol */ #ifdef HAVE_FS_NFS3 if (fp->fh_nfs_version == NFS_VERSION3) - mnt_version = MOUNTVERS3; + mnt_version = AM_MOUNTVERS3; else #endif /* HAVE_FS_NFS3 */ mnt_version = MOUNTVERS; @@ -462,40 +567,128 @@ call_mountd(fh_cache *fp, u_long proc, fwd_fun f, voidp wchan) if (len > 0) { error = fwd_packet(MK_RPC_XID(RPC_XID_MOUNTD, fp->fh_id), - (voidp) iobuf, + iobuf, len, &fp->fh_sin, &fp->fh_sin, - (voidp) ((long) fp->fh_id), /* for 64-bit archs */ - f); + (opaque_t) ((long) fp->fh_id), /* cast to long needed for 64-bit archs */ + fun); } else { error = -len; } -/* - * It may be the case that we're sending to the wrong MOUNTD port. This - * occurs if mountd is restarted on the server after the port has been - * looked up and stored in the filehandle cache somewhere. The correct - * solution, if we're going to cache port numbers is to catch the ICMP - * port unreachable reply from the server and cause the portmap request - * to be redone. The quick solution here is to invalidate the MOUNTD - * port. - */ + /* + * It may be the case that we're sending to the wrong MOUNTD port. This + * occurs if mountd is restarted on the server after the port has been + * looked up and stored in the filehandle cache somewhere. The correct + * solution, if we're going to cache port numbers is to catch the ICMP + * port unreachable reply from the server and cause the portmap request + * to be redone. The quick solution here is to invalidate the MOUNTD + * port. + */ fp->fh_sin.sin_port = 0; return error; } +static int +webnfs_lookup(fh_cache *fp, fwd_fun fun, wchan_t wchan) +{ + struct rpc_msg wnfs_msg; + int len; + char iobuf[UDPMSGSIZE]; + int error; + u_long proc; + XDRPROC_T_TYPE xdr_fn; + voidp argp; + nfsdiropargs args; +#ifdef HAVE_FS_NFS3 + am_LOOKUP3args args3; +#endif + char *wnfs_path; + size_t l; + + if (!nfs_auth) { + error = make_nfs_auth(); + if (error) + return error; + } + + if (fp->fh_sin.sin_port == 0) { + /* FIXME: wrong, don't discard sin_port in the first place for WebNFS. */ + plog(XLOG_WARNING, "webnfs_lookup: port == 0 for nfs on %s, fixed", + fp->fh_fs->fs_host); + fp->fh_sin.sin_port = htons(NFS_PORT); + } + + /* + * Use native path like the rest of amd (cf. RFC 2054, 6.1). + */ + l = strlen(fp->fh_path) + 2; + wnfs_path = (char *) xmalloc(l); + wnfs_path[0] = 0x80; + xstrlcpy(wnfs_path + 1, fp->fh_path, l - 1); + + /* find the right program and lookup procedure */ +#ifdef HAVE_FS_NFS3 + if (fp->fh_nfs_version == NFS_VERSION3) { + proc = AM_NFSPROC3_LOOKUP; + xdr_fn = (XDRPROC_T_TYPE) xdr_am_LOOKUP3args; + argp = &args3; + /* WebNFS public file handle */ + args3.what.dir.am_fh3_length = 0; + args3.what.name = wnfs_path; + } else { +#endif /* HAVE_FS_NFS3 */ + proc = NFSPROC_LOOKUP; + xdr_fn = (XDRPROC_T_TYPE) xdr_diropargs; + argp = &args; + /* WebNFS public file handle */ + memset(&args.da_fhandle, 0, NFS_FHSIZE); + args.da_name = wnfs_path; +#ifdef HAVE_FS_NFS3 + } +#endif /* HAVE_FS_NFS3 */ + + plog(XLOG_INFO, "webnfs_lookup: NFS version %d", (int) fp->fh_nfs_version); + + rpc_msg_init(&wnfs_msg, NFS_PROGRAM, fp->fh_nfs_version, proc); + len = make_rpc_packet(iobuf, + sizeof(iobuf), + proc, + &wnfs_msg, + argp, + (XDRPROC_T_TYPE) xdr_fn, + nfs_auth); + + if (len > 0) { + error = fwd_packet(MK_RPC_XID(RPC_XID_WEBNFS, fp->fh_id), + iobuf, + len, + &fp->fh_sin, + &fp->fh_sin, + (opaque_t) ((long) fp->fh_id), /* cast to long needed for 64-bit archs */ + fun); + } else { + error = -len; + } + + XFREE(wnfs_path); + return error; +} + + /* * NFS needs the local filesystem, remote filesystem * remote hostname. * Local filesystem defaults to remote and vice-versa. */ -char * +static char * nfs_match(am_opts *fo) { char *xmtab; + size_t l; if (fo->opt_fs && !fo->opt_rfs) fo->opt_rfs = fo->opt_fs; @@ -511,12 +704,11 @@ nfs_match(am_opts *fo) /* * 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 + l = strlen(fo->opt_rhost) + strlen(fo->opt_rfs) + 2; + xmtab = (char *) xmalloc(l); + xsnprintf(xmtab, l, "%s:%s", fo->opt_rhost, fo->opt_rfs); dlog("NFS: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"", fo->opt_rhost, fo->opt_rfs, fo->opt_fs); -#endif /* DEBUG */ return xmtab; } @@ -525,24 +717,36 @@ nfs_match(am_opts *fo) /* * Initialize am structure for nfs */ -int +static int nfs_init(mntfs *mf) { int error; am_nfs_handle_t fhs; char *colon; - if (mf->mf_private) - return 0; + if (mf->mf_private) { + if (mf->mf_flags & MFF_NFS_SCALEDOWN) { + fserver *fs; + + /* tell remote mountd that we're done with this filehandle */ + mf->mf_ops->umounted(mf); + + mf->mf_prfree(mf->mf_private); + fs = mf->mf_ops->ffserver(mf); + free_srvr(mf->mf_server); + mf->mf_server = fs; + } else + return 0; + } colon = strchr(mf->mf_info, ':'); if (colon == 0) return ENOENT; - error = prime_nfs_fhandle_cache(colon + 1, mf->mf_server, &fhs, (voidp) mf); + error = prime_nfs_fhandle_cache(colon + 1, mf->mf_server, &fhs, mf); if (!error) { - mf->mf_private = (voidp) ALLOC(am_nfs_handle_t); - mf->mf_prfree = (void (*)(voidp)) free; + mf->mf_private = (opaque_t) ALLOC(am_nfs_handle_t); + mf->mf_prfree = (void (*)(opaque_t)) free; memmove(mf->mf_private, (voidp) &fhs, sizeof(fhs)); } return error; @@ -550,18 +754,20 @@ nfs_init(mntfs *mf) int -mount_nfs_fh(am_nfs_handle_t *fhp, char *dir, char *fs_name, char *opts, mntfs *mf) +mount_nfs_fh(am_nfs_handle_t *fhp, char *mntdir, char *fs_name, mntfs *mf) { MTYPE_TYPE type; char *colon; - char *xopts; + char *xopts=NULL, transp_timeo_opts[40], transp_retrans_opts[40]; char host[MAXHOSTNAMELEN + MAXPATHLEN + 2]; fserver *fs = mf->mf_server; u_long nfs_version = fs->fs_version; char *nfs_proto = fs->fs_proto; /* "tcp" or "udp" */ + int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; int error; int genflags; int retry; + int proto = AMU_TYPE_NONE; mntent_t mnt; nfs_args_t nfs_args; @@ -575,26 +781,48 @@ mount_nfs_fh(am_nfs_handle_t *fhp, char *dir, char *fs_name, char *opts, mntfs * #ifdef MOUNT_TABLE_ON_FILE *colon = '\0'; #endif /* MOUNT_TABLE_ON_FILE */ - strncpy(host, fs_name, sizeof(host)); + xstrlcpy(host, fs_name, sizeof(host)); #ifdef MOUNT_TABLE_ON_FILE *colon = ':'; #endif /* MOUNT_TABLE_ON_FILE */ #ifdef MAXHOSTNAMELEN /* most kernels have a name length restriction */ if (strlen(host) >= MAXHOSTNAMELEN) - strcpy(host + MAXHOSTNAMELEN - 3, ".."); + xstrlcpy(host + MAXHOSTNAMELEN - 3, "..", + sizeof(host) - MAXHOSTNAMELEN + 3); #endif /* MAXHOSTNAMELEN */ + /* + * Create option=VAL for udp/tcp specific timeouts and retrans values, but + * only if these options were specified. + */ + + transp_timeo_opts[0] = transp_retrans_opts[0] = '\0'; /* initialize */ + if (STREQ(nfs_proto, "udp")) + proto = AMU_TYPE_UDP; + else if (STREQ(nfs_proto, "tcp")) + proto = AMU_TYPE_TCP; + if (proto != AMU_TYPE_NONE) { + if (gopt.amfs_auto_timeo[proto] > 0) + xsnprintf(transp_timeo_opts, sizeof(transp_timeo_opts), "%s=%d,", + MNTTAB_OPT_TIMEO, gopt.amfs_auto_timeo[proto]); + if (gopt.amfs_auto_retrans[proto] > 0) + xsnprintf(transp_retrans_opts, sizeof(transp_retrans_opts), "%s=%d,", + MNTTAB_OPT_RETRANS, gopt.amfs_auto_retrans[proto]); + } + if (mf->mf_remopts && *mf->mf_remopts && !islocalnet(fs->fs_ip->sin_addr.s_addr)) { plog(XLOG_INFO, "Using remopts=\"%s\"", mf->mf_remopts); - xopts = strdup(mf->mf_remopts); + /* use transp_opts first, so map-specific opts will override */ + xopts = str3cat(xopts, transp_timeo_opts, transp_retrans_opts, mf->mf_remopts); } else { - xopts = strdup(opts); + /* use transp_opts first, so map-specific opts will override */ + xopts = str3cat(xopts, transp_timeo_opts, transp_retrans_opts, mf->mf_mopts); } memset((voidp) &mnt, 0, sizeof(mnt)); - mnt.mnt_dir = dir; + mnt.mnt_dir = mntdir; mnt.mnt_fsname = fs_name; mnt.mnt_opts = xopts; @@ -626,18 +854,19 @@ mount_nfs_fh(am_nfs_handle_t *fhp, char *dir, char *fs_name, char *opts, mntfs * } #endif /* HAVE_FS_NFS3 */ plog(XLOG_INFO, "mount_nfs_fh: NFS version %d", (int) 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); +#ifdef HAVE_FS_AUTOFS + if (on_autofs) + genflags |= autofs_compute_mount_flags(&mnt); +#endif /* HAVE_FS_AUTOFS */ /* setup the many fields and flags within nfs_args */ -#ifdef HAVE_TRANSPORT_TYPE_TLI compute_nfs_args(&nfs_args, &mnt, genflags, @@ -648,27 +877,14 @@ mount_nfs_fh(am_nfs_handle_t *fhp, char *dir, char *fs_name, char *opts, mntfs * 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) { + if (amuDebug(D_TRACE)) { print_nfs_args(&nfs_args, nfs_version); plog(XLOG_DEBUG, "Generic mount flags 0x%x used for NFS mount", genflags); } -#endif /* DEBUG */ error = mount_fs(&mnt, genflags, (caddr_t) &nfs_args, retry, type, - nfs_version, nfs_proto, mnttab_file_name); + nfs_version, nfs_proto, mnttab_file_name, on_autofs); XFREE(xopts); #ifdef HAVE_TRANSPORT_TYPE_TLI @@ -682,39 +898,62 @@ mount_nfs_fh(am_nfs_handle_t *fhp, char *dir, char *fs_name, char *opts, mntfs * static int -mount_nfs(char *dir, char *fs_name, char *opts, mntfs *mf) +nfs_mount(am_node *am, mntfs *mf) { + int error = 0; + mntent_t mnt; + if (!mf->mf_private) { - plog(XLOG_ERROR, "Missing filehandle for %s", fs_name); + plog(XLOG_ERROR, "Missing filehandle for %s", mf->mf_info); return EINVAL; } - return mount_nfs_fh((am_nfs_handle_t *) mf->mf_private, dir, fs_name, opts, mf); -} + mnt.mnt_opts = mf->mf_mopts; + if (amu_hasmntopt(&mnt, "softlookup") || + (amu_hasmntopt(&mnt, "soft") && !amu_hasmntopt(&mnt, "nosoftlookup"))) + am->am_flags |= AMF_SOFTLOOKUP; + error = mount_nfs_fh((am_nfs_handle_t *) mf->mf_private, + mf->mf_mount, + mf->mf_info, + mf); -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) +static int +nfs_umount(am_node *am, mntfs *mf) { - int error = UMOUNT_FS(mf->mf_mount, mnttab_file_name); + int unmount_flags, new_unmount_flags, error; + + unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + error = UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); + +#if defined(HAVE_UMOUNT2) && (defined(MNT2_GEN_OPT_FORCE) || defined(MNT2_GEN_OPT_DETACH)) + /* + * If the attempt to unmount failed with EBUSY, and this fserver was + * marked for forced unmounts, then use forced/lazy unmounts. + */ + if (error == EBUSY && + gopt.flags & CFM_FORCED_UNMOUNTS && + mf->mf_server->fs_flags & FSF_FORCE_UNMOUNT) { + plog(XLOG_INFO, "EZK: nfs_umount: trying forced/lazy unmounts"); + /* + * XXX: turning off the FSF_FORCE_UNMOUNT may not be perfectly + * incorrect. Multiple nodes may need to be timed out and restarted for + * a single hung fserver. + */ + mf->mf_server->fs_flags &= ~FSF_FORCE_UNMOUNT; + new_unmount_flags = unmount_flags | AMU_UMOUNT_FORCE | AMU_UMOUNT_DETACH; + error = UMOUNT_FS(mf->mf_mount, mnttab_file_name, new_unmount_flags); + } +#endif /* HAVE_UMOUNT2 && (MNT2_GEN_OPT_FORCE || MNT2_GEN_OPT_DETACH) */ /* * Here is some code to unmount 'restarted' file systems. @@ -743,12 +982,14 @@ nfs_fumount(mntfs *mf) if (NSTREQ(mf->mf_mount, new_mf->mf_mount, len) && new_mf->mf_mount[len] == '/') { - UMOUNT_FS(new_mf->mf_mount, mnttab_file_name); + new_unmount_flags = + (new_mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + UMOUNT_FS(new_mf->mf_mount, mnttab_file_name, new_unmount_flags); didsome = 1; } } if (didsome) - error = UMOUNT_FS(mf->mf_mount, mnttab_file_name); + error = UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); } if (error) return error; @@ -757,38 +998,35 @@ nfs_fumount(mntfs *mf) } -void -nfs_umounted(am_node *mp) +static void +nfs_umounted(mntfs *mf) { - /* - * 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; + /* + * No need to inform mountd when WebNFS is in use. + */ + if (mf->mf_flags & MFF_WEBNFS) + return; /* * Call the mount daemon on the server to announce that we are not using * the fs any more. * - * This is *wrong*. The mountd should be called when the fhandle is + * XXX: This is *wrong*. The mountd should be called when the fhandle is * flushed from the cache, and a reference held to the cached entry while * the fs is mounted... */ + fs = mf->mf_server; colon = path = strchr(mf->mf_info, ':'); if (fs && colon) { fh_cache f; -#ifdef DEBUG dlog("calling mountd for %s", mf->mf_info); -#endif /* DEBUG */ *path++ = '\0'; f.fh_path = path; f.fh_sin = *fs->fs_ip; @@ -797,8 +1035,8 @@ nfs_umounted(am_node *mp) 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); + prime_nfs_fhandle_cache(colon + 1, mf->mf_server, (am_nfs_handle_t *) 0, mf); + call_mountd(&f, MOUNTPROC_UMNT, (fwd_fun *) 0, (wchan_t) 0); *colon = ':'; } } diff --git a/contrib/amd/amd/ops_nfs3.c b/contrib/amd/amd/ops_nfs3.c index 8da2cb1..db3c7f9 100644 --- a/contrib/amd/amd/ops_nfs3.c +++ b/contrib/amd/amd/ops_nfs3.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: ops_nfs3.c,v 1.3.2.4 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/ops_nfs3.c * */ diff --git a/contrib/amd/amd/ops_nullfs.c b/contrib/amd/amd/ops_nullfs.c index 0ecb1a9..cf621ec 100644 --- a/contrib/amd/amd/ops_nullfs.c +++ b/contrib/amd/amd/ops_nullfs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: ops_nullfs.c,v 1.3.2.4 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/ops_nullfs.c * */ diff --git a/contrib/amd/amd/ops_pcfs.c b/contrib/amd/amd/ops_pcfs.c index f148e6d..527dc92 100644 --- a/contrib/amd/amd/ops_pcfs.c +++ b/contrib/amd/amd/ops_pcfs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: ops_pcfs.c,v 1.3.2.5 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/ops_pcfs.c * */ @@ -54,8 +53,8 @@ /* forward definitions */ static char *pcfs_match(am_opts *fo); -static int pcfs_fmount(mntfs *mf); -static int pcfs_fumount(mntfs *mf); +static int pcfs_mount(am_node *am, mntfs *mf); +static int pcfs_umount(am_node *am, mntfs *mf); /* * Ops structure @@ -65,17 +64,20 @@ am_ops pcfs_ops = "pcfs", pcfs_match, 0, /* pcfs_init */ - amfs_auto_fmount, - pcfs_fmount, - amfs_auto_fumount, - pcfs_fumount, - amfs_error_lookuppn, + pcfs_mount, + pcfs_umount, + amfs_error_lookup_child, + amfs_error_mount_child, amfs_error_readdir, 0, /* pcfs_readlink */ 0, /* pcfs_mounted */ 0, /* pcfs_umounted */ - find_amfs_auto_srvr, - FS_MKMNT | FS_UBACKGROUND | FS_AMQINFO + amfs_generic_find_srvr, + 0, /* pcfs_get_wchan */ + FS_MKMNT | FS_UBACKGROUND | FS_AMQINFO, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_PCFS_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ }; @@ -90,9 +92,7 @@ pcfs_match(am_opts *fo) 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 @@ -102,11 +102,17 @@ pcfs_match(am_opts *fo) static int -mount_pcfs(char *dir, char *fs_name, char *opts) +mount_pcfs(char *mntdir, char *fs_name, char *opts, int on_autofs) { pcfs_args_t pcfs_args; mntent_t mnt; int flags; +#if defined(HAVE_PCFS_ARGS_T_MASK) || defined(HAVE_PCFS_ARGS_T_DIRMASK) + int mask; +#endif /* defined(HAVE_PCFS_ARGS_T_MASK) || defined(HAVE_PCFS_ARGS_T_DIRMASK) */ +#if defined(HAVE_PCFS_ARGS_T_UID) || defined(HAVE_PCFS_ARGS_T_UID) + char *str; +#endif /* defined(HAVE_PCFS_ARGS_T_UID) || defined(HAVE_PCFS_ARGS_T_UID) */ /* * Figure out the name of the file system type. @@ -119,12 +125,18 @@ mount_pcfs(char *dir, char *fs_name, char *opts) * Fill in the mount structure */ memset((voidp) &mnt, 0, sizeof(mnt)); - mnt.mnt_dir = dir; + mnt.mnt_dir = mntdir; mnt.mnt_fsname = fs_name; mnt.mnt_type = MNTTAB_TYPE_PCFS; mnt.mnt_opts = opts; flags = compute_mount_flags(&mnt); +#ifdef HAVE_FS_AUTOFS + if (on_autofs) + flags |= autofs_compute_mount_flags(&mnt); +#endif /* HAVE_FS_AUTOFS */ + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "mount_pcfs: flags=0x%x", (u_int) flags); #ifdef HAVE_PCFS_ARGS_T_FSPEC pcfs_args.fspec = fs_name; @@ -132,14 +144,46 @@ mount_pcfs(char *dir, char *fs_name, char *opts) #ifdef HAVE_PCFS_ARGS_T_MASK pcfs_args.mask = 0777; /* this may be the msdos file modes */ + if ((mask = hasmntval(&mnt, MNTTAB_OPT_MASK)) > 0) + pcfs_args.mask = mask; + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "mount_pcfs: mask=%o (octal)", (u_int) pcfs_args.mask); #endif /* HAVE_PCFS_ARGS_T_MASK */ +#ifdef HAVE_PCFS_ARGS_T_DIRMASK + pcfs_args.dirmask = 0777; /* this may be the msdos dir modes */ + if ((mask = hasmntval(&mnt, MNTTAB_OPT_DIRMASK)) > 0) + pcfs_args.dirmask = mask; + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "mount_pcfs: dirmask=%o (octal)", (u_int) pcfs_args.dirmask); +#endif /* HAVE_PCFS_ARGS_T_DIRMASK */ + #ifdef HAVE_PCFS_ARGS_T_UID - pcfs_args.uid = 0; /* root */ + pcfs_args.uid = 0; /* default to root */ + if ((str = hasmntstr(&mnt, MNTTAB_OPT_USER)) != NULL) { + struct passwd *pw; + if ((pw = getpwnam(str)) != NULL) + pcfs_args.uid = pw->pw_uid; + else /* maybe used passed a UID number, not user name */ + pcfs_args.uid = atoi(str); /* atoi returns '0' if it failed */ + XFREE(str); + } + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "mount_pcfs: uid=%d", (int) pcfs_args.uid); #endif /* HAVE_PCFS_ARGS_T_UID */ #ifdef HAVE_PCFS_ARGS_T_GID - pcfs_args.gid = 0; /* wheel */ + pcfs_args.gid = 0; /* default to wheel/root group */ + if ((str = hasmntstr(&mnt, MNTTAB_OPT_GROUP)) != NULL) { + struct group *gr; + if ((gr = getgrnam(str)) != NULL) + pcfs_args.gid = gr->gr_gid; + else /* maybe used passed a GID number, not group name */ + pcfs_args.gid = atoi(str); /* atoi returns '0' if it failed */ + XFREE(str); + } + if (amuDebug(D_TRACE)) + plog(XLOG_DEBUG, "mount_pcfs: gid=%d", (int) pcfs_args.gid); #endif /* HAVE_PCFS_ARGS_T_GID */ #ifdef HAVE_PCFS_ARGS_T_SECONDSWEST @@ -152,16 +196,17 @@ mount_pcfs(char *dir, char *fs_name, char *opts) /* * Call generic mount routine */ - return mount_fs(&mnt, flags, (caddr_t) & pcfs_args, 0, type, 0, NULL, mnttab_file_name); + return mount_fs(&mnt, flags, (caddr_t) & pcfs_args, 0, type, 0, NULL, mnttab_file_name, on_autofs); } static int -pcfs_fmount(mntfs *mf) +pcfs_mount(am_node *am, mntfs *mf) { + int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; int error; - error = mount_pcfs(mf->mf_mount, mf->mf_info, mf->mf_mopts); + error = mount_pcfs(mf->mf_mount, mf->mf_info, mf->mf_mopts, on_autofs); if (error) { errno = error; plog(XLOG_ERROR, "mount_pcfs: %m"); @@ -173,7 +218,9 @@ pcfs_fmount(mntfs *mf) static int -pcfs_fumount(mntfs *mf) +pcfs_umount(am_node *am, mntfs *mf) { - return UMOUNT_FS(mf->mf_mount, mnttab_file_name); + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + + return UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); } diff --git a/contrib/amd/amd/ops_tfs.c b/contrib/amd/amd/ops_tfs.c index 911c01c..142c320 100644 --- a/contrib/amd/amd/ops_tfs.c +++ b/contrib/amd/amd/ops_tfs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: ops_tfs.c,v 1.3.2.4 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/ops_tfs.c * */ diff --git a/contrib/amd/amd/ops_tmpfs.c b/contrib/amd/amd/ops_tmpfs.c index ca2d515..eddd755 100644 --- a/contrib/amd/amd/ops_tmpfs.c +++ b/contrib/amd/amd/ops_tmpfs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: ops_tmpfs.c,v 1.3.2.4 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/ops_tmpfs.c * */ diff --git a/contrib/amd/amd/ops_ufs.c b/contrib/amd/amd/ops_ufs.c index 999a669..fd82a5c 100644 --- a/contrib/amd/amd/ops_ufs.c +++ b/contrib/amd/amd/ops_ufs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: ops_ufs.c,v 1.3.2.5 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/ops_ufs.c * */ @@ -54,8 +53,8 @@ /* forward declarations */ static char *ufs_match(am_opts *fo); -static int ufs_fmount(mntfs *mf); -static int ufs_fumount(mntfs *mf); +static int ufs_mount(am_node *am, mntfs *mf); +static int ufs_umount(am_node *am, mntfs *mf); /* * Ops structure @@ -65,17 +64,20 @@ am_ops ufs_ops = "ufs", ufs_match, 0, /* ufs_init */ - amfs_auto_fmount, - ufs_fmount, - amfs_auto_fumount, - ufs_fumount, - amfs_error_lookuppn, + ufs_mount, + ufs_umount, + amfs_error_lookup_child, + amfs_error_mount_child, amfs_error_readdir, 0, /* ufs_readlink */ 0, /* ufs_mounted */ 0, /* ufs_umounted */ - find_amfs_auto_srvr, - FS_MKMNT | FS_NOTIMEOUT | FS_UBACKGROUND | FS_AMQINFO + amfs_generic_find_srvr, + 0, /* ufs_get_wchan */ + FS_MKMNT | FS_NOTIMEOUT | FS_UBACKGROUND | FS_AMQINFO, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_UFS_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ }; @@ -91,9 +93,7 @@ ufs_match(am_opts *fo) 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 @@ -103,7 +103,7 @@ ufs_match(am_opts *fo) static int -mount_ufs(char *dir, char *fs_name, char *opts) +mount_ufs(char *mntdir, char *fs_name, char *opts, int on_autofs) { ufs_args_t ufs_args; mntent_t mnt; @@ -120,12 +120,16 @@ mount_ufs(char *dir, char *fs_name, char *opts) * Fill in the mount structure */ memset((voidp) &mnt, 0, sizeof(mnt)); - mnt.mnt_dir = dir; + mnt.mnt_dir = mntdir; mnt.mnt_fsname = fs_name; mnt.mnt_type = MNTTAB_TYPE_UFS; mnt.mnt_opts = opts; genflags = compute_mount_flags(&mnt); +#ifdef HAVE_FS_AUTOFS + if (on_autofs) + genflags |= autofs_compute_mount_flags(&mnt); +#endif /* HAVE_FS_AUTOFS */ #ifdef HAVE_UFS_ARGS_T_FLAGS ufs_args.flags = genflags; /* XXX: is this correct? */ @@ -146,16 +150,17 @@ mount_ufs(char *dir, char *fs_name, char *opts) /* * Call generic mount routine */ - return mount_fs(&mnt, genflags, (caddr_t) &ufs_args, 0, type, 0, NULL, mnttab_file_name); + return mount_fs(&mnt, genflags, (caddr_t) &ufs_args, 0, type, 0, NULL, mnttab_file_name, on_autofs); } static int -ufs_fmount(mntfs *mf) +ufs_mount(am_node *am, mntfs *mf) { + int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; int error; - error = mount_ufs(mf->mf_mount, mf->mf_info, mf->mf_mopts); + error = mount_ufs(mf->mf_mount, mf->mf_info, mf->mf_mopts, on_autofs); if (error) { errno = error; plog(XLOG_ERROR, "mount_ufs: %m"); @@ -167,7 +172,9 @@ ufs_fmount(mntfs *mf) static int -ufs_fumount(mntfs *mf) +ufs_umount(am_node *am, mntfs *mf) { - return UMOUNT_FS(mf->mf_mount, mnttab_file_name); + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + + return UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); } diff --git a/contrib/amd/amd/ops_umapfs.c b/contrib/amd/amd/ops_umapfs.c index 65e99cb..5ba735e 100644 --- a/contrib/amd/amd/ops_umapfs.c +++ b/contrib/amd/amd/ops_umapfs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: ops_umapfs.c,v 1.3.2.4 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/ops_umapfs.c * */ diff --git a/contrib/amd/amd/ops_unionfs.c b/contrib/amd/amd/ops_unionfs.c index 52059a0..73f8d66 100644 --- a/contrib/amd/amd/ops_unionfs.c +++ b/contrib/amd/amd/ops_unionfs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: ops_unionfs.c,v 1.3.2.4 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/ops_unionfs.c * */ diff --git a/contrib/amd/amd/ops_xfs.c b/contrib/amd/amd/ops_xfs.c index 4c9010b..e0e740b 100644 --- a/contrib/amd/amd/ops_xfs.c +++ b/contrib/amd/amd/ops_xfs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: ops_xfs.c,v 1.3.2.6 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/ops_xfs.c * */ @@ -54,8 +53,8 @@ /* forward declarations */ static char *xfs_match(am_opts *fo); -static int xfs_fmount(mntfs *mf); -static int xfs_fumount(mntfs *mf); +static int xfs_mount(am_node *am, mntfs *mf); +static int xfs_umount(am_node *am, mntfs *mf); /* * Ops structure @@ -65,17 +64,20 @@ am_ops xfs_ops = "xfs", xfs_match, 0, /* xfs_init */ - amfs_auto_fmount, - xfs_fmount, - amfs_auto_fumount, - xfs_fumount, - amfs_error_lookuppn, + xfs_mount, + xfs_umount, + amfs_error_lookup_child, + amfs_error_mount_child, amfs_error_readdir, 0, /* xfs_readlink */ 0, /* xfs_mounted */ 0, /* xfs_umounted */ - find_amfs_auto_srvr, - FS_MKMNT | FS_NOTIMEOUT | FS_UBACKGROUND | FS_AMQINFO + amfs_generic_find_srvr, + 0, /* xfs_get_wchan */ + FS_MKMNT | FS_NOTIMEOUT | FS_UBACKGROUND | FS_AMQINFO, /* nfs_fs_flags */ +#ifdef HAVE_FS_AUTOFS + AUTOFS_XFS_FS_FLAGS, +#endif /* HAVE_FS_AUTOFS */ }; @@ -91,9 +93,7 @@ xfs_match(am_opts *fo) 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 @@ -103,7 +103,7 @@ xfs_match(am_opts *fo) static int -mount_xfs(char *dir, char *fs_name, char *opts) +mount_xfs(char *mntdir, char *fs_name, char *opts, int on_autofs) { xfs_args_t xfs_args; mntent_t mnt; @@ -120,12 +120,16 @@ mount_xfs(char *dir, char *fs_name, char *opts) * Fill in the mount structure */ memset((voidp) &mnt, 0, sizeof(mnt)); - mnt.mnt_dir = dir; + mnt.mnt_dir = mntdir; mnt.mnt_fsname = fs_name; mnt.mnt_type = MNTTAB_TYPE_XFS; mnt.mnt_opts = opts; flags = compute_mount_flags(&mnt); +#ifdef HAVE_FS_AUTOFS + if (on_autofs) + flags |= autofs_compute_mount_flags(&mnt); +#endif /* HAVE_FS_AUTOFS */ #ifdef HAVE_XFS_ARGS_T_FLAGS xfs_args.flags = 0; /* XXX: fix this to correct flags */ @@ -137,16 +141,17 @@ mount_xfs(char *dir, char *fs_name, char *opts) /* * Call generic mount routine */ - return mount_fs(&mnt, flags, (caddr_t) &xfs_args, 0, type, 0, NULL, mnttab_file_name); + return mount_fs(&mnt, flags, (caddr_t) &xfs_args, 0, type, 0, NULL, mnttab_file_name, on_autofs); } static int -xfs_fmount(mntfs *mf) +xfs_mount(am_node *am, mntfs *mf) { + int on_autofs = mf->mf_flags & MFF_ON_AUTOFS; int error; - error = mount_xfs(mf->mf_mount, mf->mf_info, mf->mf_mopts); + error = mount_xfs(mf->mf_mount, mf->mf_info, mf->mf_mopts, on_autofs); if (error) { errno = error; plog(XLOG_ERROR, "mount_xfs: %m"); @@ -158,7 +163,9 @@ xfs_fmount(mntfs *mf) static int -xfs_fumount(mntfs *mf) +xfs_umount(am_node *am, mntfs *mf) { - return UMOUNT_FS(mf->mf_mount, mnttab_file_name); + int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0; + + return UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags); } diff --git a/contrib/amd/amd/opts.c b/contrib/amd/amd/opts.c index 92bceb2..a3d3534 100644 --- a/contrib/amd/amd/opts.c +++ b/contrib/amd/amd/opts.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1989 Jan-Simon Pendry * Copyright (c) 1989 Imperial College of Science, Technology & Medicine * Copyright (c) 1989 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: opts.c,v 1.8.2.7 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/opts.c * */ @@ -94,11 +93,13 @@ struct functable { * FORWARD DEFINITION: */ static int f_in_network(char *); +static int f_xhost(char *); static int f_netgrp(char *); static int f_netgrpd(char *); static int f_exists(char *); static int f_false(char *); static int f_true(char *); +static inline char *expand_options(char *key); /* * STATICS: @@ -112,7 +113,7 @@ static char *opt_key = nullstr; static char *opt_keyd = nullstr; static char *opt_map = nullstr; static char *opt_path = nullstr; -static char uid_str[12], gid_str[12]; +char uid_str[SIZEOF_UID_STR], gid_str[SIZEOF_GID_STR]; char *opt_uid = uid_str; char *opt_gid = gid_str; static char *vars[8]; @@ -121,11 +122,11 @@ static char *literal_dollar = "$"; /* ${dollar}: a literal '$' in maps */ /* * GLOBALS */ -struct am_opts fs_static; /* copy of the options to play with */ +static struct am_opts fs_static; /* copy of the options to play with */ /* - * Options in something corresponding to frequency of use so that + * Options in some order corresponding to frequency of use so that * first-match algorithm is sped up. */ static struct opt opt_fields[] = { @@ -191,6 +192,8 @@ static struct opt opt_fields[] = { &fs_static.opt_mount, 0, 0, FALSE }, { S("unmount"), &fs_static.opt_unmount, 0, 0, FALSE }, + { S("umount"), + &fs_static.opt_umount, 0, 0, FALSE }, { S("cache"), &fs_static.opt_cache, 0, 0, FALSE }, { S("user"), @@ -201,7 +204,6 @@ static struct opt opt_fields[] = { 0, &opt_dkey, 0, FALSE }, { S("key."), 0, &opt_keyd, 0, FALSE }, - /* XXX: should maptype really be a variable? I think selector. -Erez */ { S("maptype"), &fs_static.opt_maptype, 0, 0, FALSE }, { S("cachedir"), @@ -212,6 +214,8 @@ static struct opt opt_fields[] = { 0, &opt_uid, 0, FALSE }, { S("gid"), 0, &opt_gid, 0, FALSE }, + { S("mount_type"), + &fs_static.opt_mount_type, 0, 0, FALSE }, { S("dollar"), &literal_dollar, 0, 0, FALSE }, { S("var0"), @@ -235,6 +239,7 @@ static struct opt opt_fields[] = { static struct functable functable[] = { { "in_network", f_in_network }, + { "xhost", f_xhost }, { "netgrp", f_netgrp }, { "netgrpd", f_netgrpd }, { "exists", f_exists }, @@ -265,6 +270,7 @@ static opt_apply expansions[] = {&fs_static.opt_remopts, "${opts}"}, {&fs_static.opt_mount, 0}, {&fs_static.opt_unmount, 0}, + {&fs_static.opt_umount, 0}, {&fs_static.opt_cachedir, 0}, {&fs_static.opt_addopts, 0}, {0, 0}, @@ -286,6 +292,7 @@ static opt_apply to_free[] = {&fs_static.opt_remopts, 0}, {&fs_static.opt_mount, 0}, {&fs_static.opt_unmount, 0}, + {&fs_static.opt_umount, 0}, {&fs_static.opt_cachedir, 0}, {&fs_static.opt_addopts, 0}, {&vars[0], 0}, @@ -302,6 +309,7 @@ static opt_apply to_free[] = /* * expand backslash escape sequences + * (escaped slash is handled separately in normalize_slash) */ static char backslash(char **p) @@ -457,33 +465,99 @@ functable_lookup(char *key) } +/* + * Fill in the global structure fs_static by + * cracking the string opts. opts may be + * scribbled on at will. Does NOT evaluate options. + * Returns 0 on error, 1 if no syntax errors were discovered. + */ static int -eval_opts(char *opts, char *mapkey) +split_opts(char *opts, char *mapkey) { + char *o = opts; + char *f; + /* - * Fill in the global structure fs_static by - * cracking the string opts. opts may be - * scribbled on at will. + * For each user-specified option */ - char *o = opts; + for (f = opt(&o); *f; f = opt(&o)) { + struct opt *op; + char *eq = strchr(f, '='); + char *opt = NULL; + + if (!eq) + continue; + + if (*(eq-1) == '!' || + eq[1] == '=' || + eq[1] == '!') { /* != or == or =! */ + continue; /* we don't care about selectors */ + } + + if (*(eq-1) == ':') { /* := */ + *(eq-1) = '\0'; + } else { + /* old style assignment */ + eq[0] = '\0'; + } + opt = eq + 1; + + /* + * For each recognized option + */ + for (op = opt_fields; op->name; op++) { + /* + * Check whether they match + */ + if (FSTREQ(op->name, f)) { + if (op->sel_p) { + plog(XLOG_USER, "key %s: Can't assign to a selector (%s)", + mapkey, op->name); + return 0; + } + *op->optp = opt; /* actual assignment into fs_static */ + break; /* break out of for loop */ + } /* end of "if (FSTREQ(op->name, f))" statement */ + } /* end of "for (op = opt_fields..." statement */ + + if (!op->name) + plog(XLOG_USER, "key %s: Unrecognized key/option \"%s\"", mapkey, f); + } + + return 1; +} + + +/* + * Just evaluate selectors, which were split by split_opts. + * Returns 0 on error or no match, 1 if matched. + */ +static int +eval_selectors(char *opts, char *mapkey) +{ + char *o, *old_o; char *f; + int ret = 0; + + o = old_o = strdup(opts); /* * For each user-specified option */ - while (*(f = opt(&o))) { + for (f = opt(&o); *f; f = opt(&o)) { struct opt *op; - enum vs_opt vs_opt = VarAss; + enum vs_opt vs_opt; char *eq = strchr(f, '='); char *fx; IntFuncPtr func; char *opt = NULL; + char *arg; - if (!eq || eq[1] == '\0' || eq == f) { + if (!eq) { /* * No value, is it a function call? */ - char *arg = strchr(f, '('); + arg = strchr(f, '('); if (!arg || arg[1] == '\0' || arg == f) { /* @@ -502,43 +576,54 @@ eval_opts(char *opts, char *mapkey) } *fx = '\0'; + if (f[0] == '!') { + vs_opt = SelNE; + f++; + } else { + vs_opt = SelEQ; + } /* * look up f in functable and pass it arg. * func must return 0 on failure, and 1 on success. */ if ((func = functable_lookup(f))) { - 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_INFO, "executing negated function %s", &f[1]); - if ((*func) (arg)) { - return (0); - } + int funok; + + /* this allocates memory, don't forget to free */ + arg = expand_options(arg); + funok = func(arg); + XFREE(arg); + + if (vs_opt == SelNE) + funok = !funok; + if (!funok) + goto out; + continue; } else { plog(XLOG_USER, "key %s: unknown function \"%s\"", mapkey, f); - return (0); + goto out; + } + } else { + if (eq[1] == '\0' || eq == f) { + /* misformed selector */ + plog(XLOG_USER, "key %s: Bad selector \"%s\"", mapkey, f); + continue; } - } /* * Check what type of operation is happening * !=, =! is SelNE * == is SelEQ - * := is VarAss + * =, := is VarAss */ - if (eq[-1] == '!') { /* != */ + if (*(eq-1) == '!') { /* != */ vs_opt = SelNE; - eq[-1] = '\0'; - opt = eq + 1; - } else if (eq[-1] == ':') { /* := */ - vs_opt = VarAss; - eq[-1] = '\0'; + *(eq-1) = '\0'; opt = eq + 1; + } else if (*(eq-1) == ':') { /* := */ + continue; } else if (eq[1] == '=') { /* == */ vs_opt = SelEQ; eq[0] = '\0'; @@ -547,6 +632,9 @@ eval_opts(char *opts, char *mapkey) vs_opt = SelNE; eq[0] = '\0'; opt = eq + 2; + } else { + /* old style assignment */ + continue; } /* @@ -557,48 +645,47 @@ eval_opts(char *opts, char *mapkey) * 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) { + opt = expand_options(opt); + + if (op->sel_p != NULL) { + int selok; + if (op->case_insensitive) { + selok = STRCEQ(*op->sel_p, opt); + } else { + selok = STREQ(*op->sel_p, opt); + } + if (vs_opt == SelNE) + selok = !selok; + if (!selok) { plog(XLOG_MAP, "key %s: map selector %s (=%s) did not %smatch %s", mapkey, op->name, *op->sel_p, vs_opt == SelNE ? "mis" : "", opt); - return 0; + XFREE(opt); + goto out; } - /* check if to apply a function */ - if (op->fxn_p && - ((*op->fxn_p)(opt) == (vs_opt == SelNE))) { + XFREE(opt); + } + /* check if to apply a function */ + if (op->fxn_p) { + int funok; + + funok = op->fxn_p(opt); + if (vs_opt == SelNE) + funok = !funok; + if (!funok) { plog(XLOG_MAP, "key %s: map function %s did not %smatch %s", mapkey, op->name, vs_opt == SelNE ? "mis" : "", opt); - 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; + XFREE(opt); + goto out; } - *op->optp = opt; - break; - - } /* end of "switch (vs_opt)" statement */ + XFREE(opt); + } break; /* break out of for loop */ } } @@ -607,7 +694,12 @@ eval_opts(char *opts, char *mapkey) plog(XLOG_USER, "key %s: Unrecognized key/option \"%s\"", mapkey, f); } - return 1; + /* all is ok */ + ret = 1; + + out: + free(old_o); + return ret; } @@ -695,9 +787,9 @@ strip_selectors(char *opts, char *mapkey) * == is SelEQ * := is VarAss */ - if (eq[-1] == '!') { /* != */ + if (*(eq-1) == '!') { /* != */ vs_opt = SelNE; - } else if (eq[-1] == ':') { /* := */ + } else if (*(eq-1) == ':') { /* := */ vs_opt = VarAss; } else if (eq[1] == '=') { /* == */ vs_opt = SelEQ; @@ -715,9 +807,7 @@ strip_selectors(char *opts, char *mapkey) 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; } } @@ -738,27 +828,86 @@ f_in_network(char *arg) int status; if (!arg) - return FALSE; + return 0; status = is_network_member(arg); -#ifdef DEBUG - plog(XLOG_USER, "%s is %son a local network", - arg, (status ? "" : "not ")); -#endif /* DEBUG */ + dlog("%s is %son a local network", arg, (status ? "" : "not ")); return status; } +/* + * Test if arg is any of this host's names or aliases (CNAMES). + * Note: this function compares against the fully expanded host name (hostd). + * XXX: maybe we also need to compare against the stripped host name? + */ +static int +f_xhost(char *arg) +{ + struct hostent *hp; + char **cp; + + if (!arg) + return 0; + + /* simple test: does it match main host name? */ + if (STREQ(arg, opt_hostd)) + return 1; + + /* now find all of the names of "arg" and compare against opt_hostd */ + hp = gethostbyname(arg); + if (hp == NULL) { +#ifdef HAVE_HSTRERROR + plog(XLOG_ERROR, "gethostbyname xhost(%s): %s", arg, hstrerror(h_errno)); +#else /* not HAVE_HSTRERROR */ + plog(XLOG_ERROR, "gethostbyname xhost(%s): h_errno %d", arg, h_errno); +#endif /* not HAVE_HSTRERROR */ + return 0; + } + /* check primary name */ + if (hp->h_name) { + dlog("xhost: compare %s==%s", hp->h_name, opt_hostd); + if (STREQ(hp->h_name, opt_hostd)) { + plog(XLOG_INFO, "xhost(%s): matched h_name %s", arg, hp->h_name); + return 1; + } + } + /* check all aliases, if any */ + if (hp->h_aliases == NULL) { + dlog("gethostbyname(%s) has no aliases", arg); + return 0; + } + cp = hp->h_aliases; + while (*cp) { + dlog("xhost: compare alias %s==%s", *cp, opt_hostd); + if (STREQ(*cp, opt_hostd)) { + plog(XLOG_INFO, "xhost(%s): matched alias %s", arg, *cp); + return 1; + } + cp++; + } + /* nothing matched */ + return 0; +} + + /* test if this host (short hostname form) is in netgroup (arg) */ static int f_netgrp(char *arg) { int status; + char *ptr, *nhost; - status = innetgr(arg, opt_host, NULL, NULL); -#ifdef DEBUG - plog(XLOG_USER, "netgrp = %s status = %d host = %s", arg, status, opt_host); -#endif /* DEBUG */ + if ((ptr = strchr(arg, ',')) != NULL) { + *ptr = '\0'; + nhost = ptr + 1; + } else { + nhost = opt_host; + } + status = innetgr(arg, nhost, NULL, NULL); + dlog("netgrp = %s status = %d host = %s", arg, status, nhost); + if (ptr) + *ptr = ','; return status; } @@ -768,11 +917,18 @@ static int f_netgrpd(char *arg) { int status; + char *ptr, *nhost; - status = innetgr(arg, opt_hostd, NULL, NULL); -#ifdef DEBUG - plog(XLOG_USER, "netgrp = %s status = %d hostd = %s", arg, status, opt_hostd); -#endif /* DEBUG */ + if ((ptr = strchr(arg, ',')) != NULL) { + *ptr = '\0'; + nhost = ptr + 1; + } else { + nhost = opt_hostd; + } + status = innetgr(arg, nhost, NULL, NULL); + dlog("netgrp = %s status = %d hostd = %s", arg, status, nhost); + if (ptr) + *ptr = ','; return status; } @@ -814,7 +970,6 @@ free_op(opt_apply *p, int b) { if (*p->opt) { XFREE(*p->opt); - *p->opt = 0; } } @@ -825,9 +980,12 @@ free_op(opt_apply *p, int b) void normalize_slash(char *p) { - char *f = strchr(p, '/'); - char *f0 = f; + char *f, *f0; + if (!(gopt.flags & CFM_NORMALIZE_SLASHES)) + return; + + f0 = f = strchr(p, '/'); if (f) { char *t = f; do { @@ -849,6 +1007,9 @@ normalize_slash(char *p) /* assert(*f != '/'); */ /* keep copying up to next / */ while (*f && *f != '/') { + /* support escaped slashes '\/' */ + if (f[0] == '\\' && f[1] == '/') + f++; /* skip backslash */ *t++ = *f++; } @@ -863,22 +1024,20 @@ normalize_slash(char *p) /* * 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 + * If sel_p is true then old expand selectors, otherwise * don't expand selectors. */ -static void -expand_op(opt_apply *p, int sel_p) +static char * +expand_op(char *opt, int sel_p) { - static const char expand_error[] = "No space to expand \"%s\""; +#define EXPAND_ERROR "No space to expand \"%s\"" char expbuf[MAXPATHLEN + 1]; char nbuf[NLEN + 1]; char *ep = expbuf; - char *cp = *p->opt; + char *cp = opt; char *dp; struct opt *op; -#ifdef DEBUG - char *cp_orig = *p->opt; -#endif /* DEBUG */ + char *cp_orig = opt; while ((dp = strchr(cp, '$'))) { char ch; @@ -888,12 +1047,18 @@ expand_op(opt_apply *p, int sel_p) { int len = dp - cp; - if (BUFSPACE(ep, len)) { - strncpy(ep, cp, len); - ep += len; - } else { - plog(XLOG_ERROR, expand_error, *p->opt); - goto out; + if (len > 0) { + if (BUFSPACE(ep, len)) { + /* + * We use strncpy (not xstrlcpy) because 'ep' relies on its + * semantics. BUFSPACE guarantees that ep can hold len. + */ + strncpy(ep, cp, len); + ep += len; + } else { + plog(XLOG_ERROR, EXPAND_ERROR, opt); + goto out; + } } } @@ -903,7 +1068,7 @@ expand_op(opt_apply *p, int sel_p) if (BUFSPACE(ep, 1)) { *ep++ = '$'; } else { - plog(XLOG_ERROR, expand_error, *p->opt); + plog(XLOG_ERROR, EXPAND_ERROR, opt); goto out; } } else if (ch == '{') { @@ -924,7 +1089,7 @@ expand_op(opt_apply *p, int sel_p) /* * Just give up */ - plog(XLOG_USER, "No closing '}' in \"%s\"", *p->opt); + plog(XLOG_USER, "No closing '}' in \"%s\"", opt); goto out; } len = br_p - cp; @@ -939,7 +1104,7 @@ expand_op(opt_apply *p, int sel_p) todo = E_File; cp++; --len; - } else if (br_p[-1] == '/') { + } else if (*(br_p-1) == '/') { /* * Take all but the last component */ @@ -952,7 +1117,7 @@ expand_op(opt_apply *p, int sel_p) todo = E_Domain; cp++; --len; - } else if (br_p[-1] == '.') { + } else if (*(br_p-1) == '.') { /* * Take host name */ @@ -976,6 +1141,11 @@ expand_op(opt_apply *p, int sel_p) /* * Put the string into another buffer so * we can do comparisons. + * + * We use strncpy here (not xstrlcpy) because the dest is meant + * to be truncated and we don't want to log it as an error. The + * use of the BUFSPACE macro above guarantees the safe use of + * strncpy with nbuf. */ strncpy(nbuf, cp, len); nbuf[len] = '\0'; @@ -1003,12 +1173,12 @@ expand_op(opt_apply *p, int 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 ? "." : ""); + xsnprintf(xbuf, sizeof(xbuf), "${%s%s%s}", + todo == E_File ? "/" : + todo == E_Domain ? "." : "", + nbuf, + todo == E_Dir ? "/" : + todo == E_Host ? "." : ""); val = xbuf; /* * Make sure expansion doesn't @@ -1067,11 +1237,11 @@ expand_op(opt_apply *p, int sel_p) break; } - if (BUFSPACE(ep, vlen)) { - strcpy(ep, vptr); + if (BUFSPACE(ep, vlen+1)) { + xstrlcpy(ep, vptr, vlen+1); ep += vlen; } else { - plog(XLOG_ERROR, expand_error, *p->opt); + plog(XLOG_ERROR, EXPAND_ERROR, opt); goto out; } } @@ -1096,17 +1266,15 @@ expand_op(opt_apply *p, int sel_p) if (env) { int vlen = strlen(env); - if (BUFSPACE(ep, vlen)) { - strcpy(ep, env); + if (BUFSPACE(ep, vlen+1)) { + xstrlcpy(ep, env, vlen+1); ep += vlen; } else { - plog(XLOG_ERROR, expand_error, *p->opt); + plog(XLOG_ERROR, EXPAND_ERROR, opt); goto out; } -#ifdef DEBUG - amuDebug(D_STR) + if (amuDebug(D_STR)) plog(XLOG_DEBUG, "Environment gave \"%s\" -> \"%s\"", nbuf, env); -#endif /* DEBUG */ } else { plog(XLOG_USER, "Unknown sequence \"${%s}\"", nbuf); } @@ -1115,7 +1283,7 @@ expand_op(opt_apply *p, int sel_p) /* * Error, error */ - plog(XLOG_USER, "Unknown $ sequence in \"%s\"", *p->opt); + plog(XLOG_USER, "Unknown $ sequence in \"%s\"", opt); } } @@ -1123,33 +1291,33 @@ out: /* * Handle common case - no expansion */ - if (cp == *p->opt) { - *p->opt = strdup(cp); + if (cp == opt) { + opt = strdup(cp); } else { /* * Finish off the expansion */ - if (BUFSPACE(ep, strlen(cp))) { - strcpy(ep, cp); - /* ep += strlen(ep); */ + int vlen = strlen(cp); + if (BUFSPACE(ep, vlen+1)) { + xstrlcpy(ep, cp, vlen+1); + /* ep += vlen; */ } else { - plog(XLOG_ERROR, expand_error, *p->opt); + plog(XLOG_ERROR, EXPAND_ERROR, opt); } /* * Save the expansion */ - *p->opt = strdup(expbuf); + opt = strdup(expbuf); } - normalize_slash(*p->opt); + normalize_slash(opt); -#ifdef DEBUG - amuDebug(D_STR) { + if (amuDebug(D_STR)) { plog(XLOG_DEBUG, "Expansion of \"%s\"...", cp_orig); - plog(XLOG_DEBUG, "... is \"%s\"", *p->opt); + plog(XLOG_DEBUG, "......... is \"%s\"", opt); } -#endif /* DEBUG */ + return opt; } @@ -1160,15 +1328,15 @@ static void expand_opts(opt_apply *p, int sel_p) { if (*p->opt) { - expand_op(p, sel_p); + *p->opt = expand_op(*p->opt, sel_p); } else if (p->val) { /* * Do double expansion, remembering * to free the string from the first * expansion... */ - char *s = *p->opt = expand_key(p->val); - expand_op(p, sel_p); + char *s = expand_op(p->val, TRUE); + *p->opt = expand_op(s, sel_p); XFREE(s); } } @@ -1206,18 +1374,22 @@ free_opts(am_opts *fo) /* - * Expand lookup key + * Expand selectors (variables that cannot be assigned to or overridden) */ char * -expand_key(char *key) +expand_selectors(char *key) { - opt_apply oa; + return expand_op(key, TRUE); +} - oa.opt = &key; - oa.val = 0; - expand_opts(&oa, TRUE); - return key; +/* + * Expand options (i.e. non-selectors, see above for definition) + */ +static inline char * +expand_options(char *key) +{ + return expand_op(key, FALSE); } @@ -1225,10 +1397,14 @@ expand_key(char *key) * Remove trailing /'s from a string * unless the string is a single / (Steven Glassman) * or unless it is two slashes // (Kevin D. Bond) + * or unless amd.conf says not to touch slashes. */ void deslashify(char *s) { + if (!(gopt.flags & CFM_NORMALIZE_SLASHES)) + return; + if (s && *s) { char *sl = s + strlen(s); @@ -1276,24 +1452,27 @@ eval_fs_opts(am_opts *fo, char *opts, char *g_opts, char *path, char *key, char /* * Expand global options */ - fs_static.fs_glob = expand_key(g_opts); + fs_static.fs_glob = expand_selectors(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; + fs_static.fs_local = expand_selectors(opts); - /* - * Expand local options - */ - if (ok && !eval_opts(fs_static.fs_local, key)) - ok = FALSE; + /* break global options into fs_static fields */ + if ((ok = split_opts(fs_static.fs_glob, key))) { + dlog("global split_opts ok"); + /* + * evaluate local selectors + */ + if ((ok = eval_selectors(fs_static.fs_local, key))) { + dlog("local eval_selectors ok"); + /* if the local selectors matched, then do the local overrides */ + ok = split_opts(fs_static.fs_local, key); + if (ok) + dlog("local split_opts ok"); + } + } /* * Normalize remote host name. diff --git a/contrib/amd/amd/readdir.c b/contrib/amd/amd/readdir.c new file mode 100644 index 0000000..d025c91 --- /dev/null +++ b/contrib/amd/amd/readdir.c @@ -0,0 +1,498 @@ +/* + * Copyright (c) 1997-2006 Erez Zadok + * Copyright (c) 1990 Jan-Simon Pendry + * Copyright (c) 1990 Imperial College of Science, Technology & Medicine + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry at Imperial College, London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgment: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * File: am-utils/amd/readdir.c + * + */ + + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ +#include <am_defs.h> +#include <amd.h> + + +/**************************************************************************** + *** MACROS *** + ****************************************************************************/ +#define DOT_DOT_COOKIE (u_int) 1 +#define MAX_CHAIN 2048 + + +/**************************************************************************** + *** FORWARD DEFINITIONS *** + ****************************************************************************/ +static int key_already_in_chain(char *keyname, const nfsentry *chain); +static nfsentry *make_entry_chain(am_node *mp, const nfsentry *current_chain, int fully_browsable); +static int amfs_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, u_int count, int fully_browsable); + + +/**************************************************************************** + *** FUNCTIONS *** + ****************************************************************************/ +/* + * Was: NEW_TOPLVL_READDIR + * Search a chain for an entry with some name. + * -Erez Zadok <ezk@cs.columbia.edu> + */ +static int +key_already_in_chain(char *keyname, const nfsentry *chain) +{ + const nfsentry *tmpchain = chain; + + while (tmpchain) { + if (keyname && tmpchain->ne_name && STREQ(keyname, tmpchain->ne_name)) + return 1; + tmpchain = tmpchain->ne_nextentry; + } + + return 0; +} + + +/* + * Create a chain of entries which are not linked. + * -Erez Zadok <ezk@cs.columbia.edu> + */ +static nfsentry * +make_entry_chain(am_node *mp, const nfsentry *current_chain, int fully_browsable) +{ + static u_int last_cookie = (u_int) 2; /* monotonically increasing */ + static nfsentry chain[MAX_CHAIN]; + static int max_entries = MAX_CHAIN; + char *key; + int num_entries = 0, i; + u_int preflen = 0; + nfsentry *retval = (nfsentry *) NULL; + mntfs *mf; + mnt_map *mmp; + + if (!mp) { + plog(XLOG_DEBUG, "make_entry_chain: mp is (NULL)"); + return retval; + } + mf = mp->am_mnt; + if (!mf) { + plog(XLOG_DEBUG, "make_entry_chain: mp->am_mnt is (NULL)"); + return retval; + } + mmp = (mnt_map *) mf->mf_private; + if (!mmp) { + plog(XLOG_DEBUG, "make_entry_chain: mp->am_mnt->mf_private is (NULL)"); + return retval; + } + + if (mp->am_pref) + preflen = strlen(mp->am_pref); + + /* iterate over keys */ + for (i = 0; i < NKVHASH; i++) { + kv *k; + for (k = mmp->kvhash[i]; k ; k = k->next) { + + /* + * Skip unwanted entries which are either not real entries or + * very difficult to interpret (wildcards...) This test needs + * lots of improvement. Any takers? + */ + key = k->key; + if (!key) + continue; + + /* Skip '/defaults' */ + if (STREQ(key, "/defaults")) + continue; + + /* Skip '*' */ + if (!fully_browsable && strchr(key, '*')) + continue; + + /* + * If the map has a prefix-string then check if the key starts with + * this string, and if it does, skip over this prefix. If it has a + * prefix and it doesn't match the start of the key, skip it. + */ + if (preflen) { + if (preflen > strlen(key)) + continue; + if (!NSTREQ(key, mp->am_pref, preflen)) + continue; + key += preflen; + } + + /* no more '/' are allowed, unless browsable_dirs=full was used */ + if (!fully_browsable && strchr(key, '/')) + continue; + + /* no duplicates allowed */ + if (key_already_in_chain(key, current_chain)) + continue; + + /* fill in a cell and link the entry */ + if (num_entries >= max_entries) { + /* out of space */ + plog(XLOG_DEBUG, "make_entry_chain: no more space in chain"); + if (num_entries > 0) { + chain[num_entries - 1].ne_nextentry = 0; + retval = &chain[0]; + } + return retval; + } + + /* we have space. put entry in next cell */ + ++last_cookie; + chain[num_entries].ne_fileid = (u_int) last_cookie; + *(u_int *) chain[num_entries].ne_cookie = (u_int) last_cookie; + chain[num_entries].ne_name = key; + if (num_entries < max_entries - 1) { /* link to next one */ + chain[num_entries].ne_nextentry = &chain[num_entries + 1]; + } + ++num_entries; + } /* end of "while (k)" */ + } /* end of "for (i ... NKVHASH ..." */ + + /* terminate chain */ + if (num_entries > 0) { + chain[num_entries - 1].ne_nextentry = 0; + retval = &chain[0]; + } + + return retval; +} + + + +/* This one is called only if map is browsable */ +static int +amfs_readdir_browsable(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, u_int count, int fully_browsable) +{ + u_int gen = *(u_int *) cookie; + int chain_length, i; + static nfsentry *te, *te_next; + static int j; + + dp->dl_eof = FALSE; /* assume readdir not done */ + + if (amuDebug(D_READDIR)) + plog(XLOG_DEBUG, "amfs_readdir_browsable gen=%u, count=%d", + gen, count); + + if (gen == 0) { + /* + * In the default instance (which is used to start a search) we return + * "." and "..". + * + * This assumes that the count is big enough to allow both "." and ".." + * to be returned in a single packet. If it isn't (which would be + * fairly unbelievable) then tough. + */ + dlog("amfs_readdir_browsable: default search"); + /* + * Check for enough room. This is extremely approximate but is more + * than enough space. Really need 2 times: + * 4byte fileid + * 4byte cookie + * 4byte name length + * 4byte name + * plus the dirlist structure */ + if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp)))) + return EINVAL; + + /* + * compute # of entries to send in this chain. + * heuristics: 128 bytes per entry. + * This is too much probably, but it seems to work better because + * of the re-entrant nature of nfs_readdir, and esp. on systems + * like OpenBSD 2.2. + */ + chain_length = count / 128; + + /* reset static state counters */ + te = te_next = NULL; + + dp->dl_entries = ep; + + /* construct "." */ + ep[0].ne_fileid = mp->am_gen; + ep[0].ne_name = "."; + ep[0].ne_nextentry = &ep[1]; + *(u_int *) ep[0].ne_cookie = 0; + + /* construct ".." */ + if (mp->am_parent) + ep[1].ne_fileid = mp->am_parent->am_gen; + else + ep[1].ne_fileid = mp->am_gen; + + ep[1].ne_name = ".."; + ep[1].ne_nextentry = 0; + *(u_int *) ep[1].ne_cookie = DOT_DOT_COOKIE; + + /* + * If map is browsable, call a function make_entry_chain() to construct + * a linked list of unmounted keys, and return it. Then link the chain + * to the regular list. Get the chain only once, but return + * chunks of it each time. + */ + te = make_entry_chain(mp, dp->dl_entries, fully_browsable); + if (!te) + return 0; + if (amuDebug(D_READDIR)) { + nfsentry *ne; + for (j = 0, ne = te; ne; ne = ne->ne_nextentry) + plog(XLOG_DEBUG, "gen1 key %4d \"%s\"", j++, ne->ne_name); + } + + /* return only "chain_length" entries */ + te_next = te; + for (i=1; i<chain_length; ++i) { + te_next = te_next->ne_nextentry; + if (!te_next) + break; + } + if (te_next) { + nfsentry *te_saved = te_next->ne_nextentry; + te_next->ne_nextentry = NULL; /* terminate "te" chain */ + te_next = te_saved; /* save rest of "te" for next iteration */ + dp->dl_eof = FALSE; /* tell readdir there's more */ + } else { + dp->dl_eof = TRUE; /* tell readdir that's it */ + } + ep[1].ne_nextentry = te; /* append this chunk of "te" chain */ + if (amuDebug(D_READDIR)) { + nfsentry *ne; + for (j = 0, ne = te; ne; ne = ne->ne_nextentry) + plog(XLOG_DEBUG, "gen2 key %4d \"%s\"", j++, ne->ne_name); + for (j = 0, ne = ep; ne; ne = ne->ne_nextentry) + plog(XLOG_DEBUG, "gen2+ key %4d \"%s\" fi=%d ck=%d", + j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie); + plog(XLOG_DEBUG, "EOF is %d", dp->dl_eof); + } + return 0; + } /* end of "if (gen == 0)" statement */ + + dlog("amfs_readdir_browsable: real child"); + + if (gen == DOT_DOT_COOKIE) { + dlog("amfs_readdir_browsable: End of readdir in %s", mp->am_path); + dp->dl_eof = TRUE; + dp->dl_entries = 0; + return 0; + } + + /* + * If browsable directories, then continue serving readdir() with another + * chunk of entries, starting from where we left off (when gen was equal + * to 0). Once again, assume last chunk served to readdir. + */ + dp->dl_eof = TRUE; + dp->dl_entries = ep; + + te = te_next; /* reset 'te' from last saved te_next */ + if (!te) { /* another indicator of end of readdir */ + dp->dl_entries = 0; + return 0; + } + /* + * compute # of entries to send in this chain. + * heuristics: 128 bytes per entry. + */ + chain_length = count / 128; + + /* return only "chain_length" entries */ + for (i = 1; i < chain_length; ++i) { + te_next = te_next->ne_nextentry; + if (!te_next) + break; + } + if (te_next) { + nfsentry *te_saved = te_next->ne_nextentry; + te_next->ne_nextentry = NULL; /* terminate "te" chain */ + te_next = te_saved; /* save rest of "te" for next iteration */ + dp->dl_eof = FALSE; /* tell readdir there's more */ + } + ep = te; /* send next chunk of "te" chain */ + dp->dl_entries = ep; + if (amuDebug(D_READDIR)) { + nfsentry *ne; + plog(XLOG_DEBUG, "dl_entries=%p, te_next=%p, dl_eof=%d", + dp->dl_entries, te_next, dp->dl_eof); + for (ne = te; ne; ne = ne->ne_nextentry) + plog(XLOG_DEBUG, "gen3 key %4d \"%s\"", j++, ne->ne_name); + } + return 0; +} + + +/* + * This readdir function which call a special version of it that allows + * browsing if browsable_dirs=yes was set on the map. + */ +int +amfs_generic_readdir(am_node *mp, nfscookie cookie, nfsdirlist *dp, nfsentry *ep, u_int count) +{ + u_int gen = *(u_int *) cookie; + am_node *xp; + mntent_t mnt; + + dp->dl_eof = FALSE; /* assume readdir not done */ + + /* check if map is browsable */ + if (mp->am_mnt && mp->am_mnt->mf_mopts) { + mnt.mnt_opts = mp->am_mnt->mf_mopts; + if (amu_hasmntopt(&mnt, "fullybrowsable")) + return amfs_readdir_browsable(mp, cookie, dp, ep, count, TRUE); + if (amu_hasmntopt(&mnt, "browsable")) + return amfs_readdir_browsable(mp, cookie, dp, ep, count, FALSE); + } + + /* when gen is 0, we start reading from the beginning of the directory */ + if (gen == 0) { + /* + * In the default instance (which is used to start a search) we return + * "." and "..". + * + * This assumes that the count is big enough to allow both "." and ".." + * to be returned in a single packet. If it isn't (which would be + * fairly unbelievable) then tough. + */ + dlog("amfs_generic_readdir: default search"); + /* + * Check for enough room. This is extremely approximate but is more + * than enough space. Really need 2 times: + * 4byte fileid + * 4byte cookie + * 4byte name length + * 4byte name + * plus the dirlist structure */ + if (count < (2 * (2 * (sizeof(*ep) + sizeof("..") + 4) + sizeof(*dp)))) + return EINVAL; + + xp = next_nonerror_node(mp->am_child); + dp->dl_entries = ep; + + /* construct "." */ + ep[0].ne_fileid = mp->am_gen; + ep[0].ne_name = "."; + ep[0].ne_nextentry = &ep[1]; + *(u_int *) ep[0].ne_cookie = 0; + + /* construct ".." */ + if (mp->am_parent) + ep[1].ne_fileid = mp->am_parent->am_gen; + else + ep[1].ne_fileid = mp->am_gen; + ep[1].ne_name = ".."; + ep[1].ne_nextentry = 0; + *(u_int *) ep[1].ne_cookie = (xp ? xp->am_gen : DOT_DOT_COOKIE); + + if (!xp) + dp->dl_eof = TRUE; /* by default assume readdir done */ + + if (amuDebug(D_READDIR)) { + nfsentry *ne; + int j; + for (j = 0, ne = ep; ne; ne = ne->ne_nextentry) + plog(XLOG_DEBUG, "gen1 key %4d \"%s\" fi=%d ck=%d", + j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie); + } + return 0; + } + dlog("amfs_generic_readdir: real child"); + + if (gen == DOT_DOT_COOKIE) { + dlog("amfs_generic_readdir: End of readdir in %s", mp->am_path); + dp->dl_eof = TRUE; + dp->dl_entries = 0; + if (amuDebug(D_READDIR)) + plog(XLOG_DEBUG, "end of readdir eof=TRUE, dl_entries=0\n"); + return 0; + } + + /* non-browsable directories code */ + xp = mp->am_child; + while (xp && xp->am_gen != gen) + xp = xp->am_osib; + + if (xp) { + int nbytes = count / 2; /* conservative */ + int todo = MAX_READDIR_ENTRIES; + + dp->dl_entries = ep; + do { + am_node *xp_next = next_nonerror_node(xp->am_osib); + + if (xp_next) { + *(u_int *) ep->ne_cookie = xp_next->am_gen; + } else { + *(u_int *) ep->ne_cookie = DOT_DOT_COOKIE; + dp->dl_eof = TRUE; + } + + ep->ne_fileid = xp->am_gen; + ep->ne_name = xp->am_name; + nbytes -= sizeof(*ep) + 1; + if (xp->am_name) + nbytes -= strlen(xp->am_name); + + xp = xp_next; + + if (nbytes > 0 && !dp->dl_eof && todo > 1) { + ep->ne_nextentry = ep + 1; + ep++; + --todo; + } else { + todo = 0; + } + } while (todo > 0); + + ep->ne_nextentry = 0; + + if (amuDebug(D_READDIR)) { + nfsentry *ne; + int j; + for (j=0,ne=ep; ne; ne=ne->ne_nextentry) + plog(XLOG_DEBUG, "gen2 key %4d \"%s\" fi=%d ck=%d", + j++, ne->ne_name, ne->ne_fileid, *(u_int *)ne->ne_cookie); + } + return 0; + } + return ESTALE; +} diff --git a/contrib/amd/amd/restart.c b/contrib/amd/amd/restart.c index 59902f6..4f71e38 100644 --- a/contrib/amd/amd/restart.c +++ b/contrib/amd/amd/restart.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: restart.c,v 1.3.2.4 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/restart.c * */ @@ -49,14 +48,81 @@ #include <amd.h> +static void +restart_fake_mntfs(mntent_t *me, am_ops *fs_ops) +{ + mntfs *mf; + am_opts mo; + char *cp; + + /* + * Partially fake up an opts structure + */ + memset(&mo, 0, sizeof(mo)); + mo.opt_rhost = 0; + mo.opt_rfs = 0; + cp = strchr(me->mnt_fsname, ':'); + if (cp) { + *cp = '\0'; + mo.opt_rhost = strdup(me->mnt_fsname); + mo.opt_rfs = strdup(cp + 1); + *cp = ':'; + } else if (STREQ(me->mnt_type, MNTTAB_TYPE_NFS)) { + /* + * Hacky workaround for mnttab NFS entries that only list the server + */ + plog(XLOG_WARNING, "NFS server entry assumed to be %s:/", me->mnt_fsname); + mo.opt_rhost = strdup(me->mnt_fsname); + mo.opt_rfs = strdup("/"); + me->mnt_fsname = str3cat(me->mnt_fsname, mo.opt_rhost, ":", "/"); + } + mo.opt_fs = me->mnt_dir; + mo.opt_opts = me->mnt_opts; + + /* + * Make a new mounted filesystem + */ + mf = find_mntfs(fs_ops, &mo, me->mnt_dir, + me->mnt_fsname, "", me->mnt_opts, ""); + if (mf->mf_refc == 1) { + mf->mf_flags |= MFF_RESTART | MFF_MOUNTED; + mf->mf_error = 0; /* Already mounted correctly */ + mf->mf_fo = 0; + /* + * Only timeout non-NFS entries + */ + if (!STREQ(me->mnt_type, MNTTAB_TYPE_NFS)) + mf->mf_flags |= MFF_RSTKEEP; + if (fs_ops->fs_init) { + /* + * Don't care whether this worked since + * it is checked again when the fs is + * inherited. + */ + (void) (*fs_ops->fs_init) (mf); + } + plog(XLOG_INFO, "%s restarted fstype %s on %s, flags 0x%x", + me->mnt_fsname, fs_ops->fs_type, me->mnt_dir, mf->mf_flags); + } else { + /* Something strange happened - two mounts at the same place! */ + free_mntfs(mf); + } + /* + * Clean up mo + */ + if (mo.opt_rhost) + XFREE(mo.opt_rhost); + if (mo.opt_rfs) + XFREE(mo.opt_rfs); +} + + /* * Handle an amd restart. * * Scan through the mount list finding all "interesting" mount points. * Next hack up partial data structures and add the mounted file - * system to the list of known filesystems. This will leave a - * dangling reference to that filesystems, so when the filesystem is - * finally inherited, an extra "free" must be done on it. + * system to the list of known filesystems. * * This module relies on internal details of other components. If * you change something else make *sure* restart() still works. @@ -64,26 +130,19 @@ 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. + * Read the existing mount table. For each entry, find nfs, ufs or auto + * mounts and create a partial am_node to represent it. */ for (mlp = ml = read_mtab("restart", mnttab_file_name); mlp; mlp = mlp->mnext) { mntent_t *me = mlp->mnt; am_ops *fs_ops = 0; - if (STREQ(me->mnt_type, MNTTAB_TYPE_UFS)) { - /* - * UFS entry - */ - fs_ops = &ufs_ops; - } else if (STREQ(me->mnt_type, MNTTAB_TYPE_NFS)) { + + if (STREQ(me->mnt_type, MNTTAB_TYPE_NFS)) { /* * NFS entry, or possibly an Amd entry... * The mnt_fsname for daemon mount points is @@ -92,117 +151,138 @@ restart(void) * host:daemon(pidXXX) */ char *colon = strchr(me->mnt_fsname, ':'); + if (colon && strstr(colon, "(pid")) + continue; + } - 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... - */ + /* Search for the correct filesystem ops */ + fs_ops = ops_search(me->mnt_type); + + /* + * Catch everything else with symlinks to + * avoid recursive mounts. This is debatable... + */ + if (!fs_ops) fs_ops = &amfs_link_ops; + + restart_fake_mntfs(me, fs_ops); + } + + /* + * Free the mount list + */ + free_mntlist(ml); +} + + +/* + * Handle an amd restart for amd's own mount points. + * + * Scan through the mount list finding all daemon mount points + * (determined by the presence of a pid inside the mount info). + * Next hack up partial data structures and add the mounted file + * system to the list of known filesystems. + * + * This module relies on internal details of other components. If + * you change something else make *sure* restart() still works. + */ +void +restart_automounter_nodes(void) +{ + mntlist *ml, *mlp; + /* reasonably sized list of restarted nfs ports */ + u_short old_ports[256]; + + memset((voidp) &old_ports, 0, sizeof(u_short) * 256); + + /* + * Read the existing mount table. For each entry, find nfs, ufs or auto + * mounts and create a partial am_node to represent it. + */ + for (mlp = ml = read_mtab("restart", mnttab_file_name); + mlp; + mlp = mlp->mnext) { + mntent_t *me = mlp->mnt; + am_ops *fs_ops = 0; + char *colon; + long pid; + u_short port; + int err; + + if (!STREQ(me->mnt_type, MNTTAB_TYPE_NFS)) + continue; /* to next mlp */ + /* + * NFS entry, or possibly an Amd entry... + * The mnt_fsname for daemon mount points is + * host:(pidXXX) + * or (seen on Solaris) + * host:daemon(pidXXX) + */ + colon = strchr(me->mnt_fsname, ':'); + if (!colon || !strstr(colon, "(pid")) + continue; + /* if got here, then we matched an existing Amd mount point */ + err = 1; + + plog(XLOG_WARNING, "%s is an existing automount point", me->mnt_dir); + + /* Is the old automounter still alive? */ + if (sscanf(colon, "%*[^(](pid%ld%*[,)]", &pid) != 1) { + plog(XLOG_WARNING, "Can't parse pid in %s", me->mnt_fsname); + goto give_up; + } + if (kill(pid, 0) != -1 || errno != ESRCH) { + plog(XLOG_WARNING, "Automounter (pid: %ld) still alive", pid); + goto give_up; } /* - * If we found something to do + * Do we have a map for this mount point? Who cares, we'll restart + * anyway -- getting ESTALE is way better than hanging. */ - 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, ":", "/"); + /* Can we restart it? Only if it tells us what port it was using... */ + if (sscanf(colon, "%*[^,],port%hu)", &port) != 1) { + plog(XLOG_WARNING, "No port specified for %s", me->mnt_fsname); + goto give_up; + } + + /* Maybe we already own that port... */ + if (port != nfs_port) { + int i; + for (i = 0; i < 256; i++) { + if (old_ports[i] == port || + old_ports[i] == 0) + break; + } + if (i == 256) { + plog(XLOG_WARNING, "Too many open ports (256)"); + goto give_up; } - 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); + if (old_ports[i] == 0) { + int soNFS; + SVCXPRT *nfsxprt; + if (create_nfs_service(&soNFS, &port, &nfsxprt, nfs_program_2) != 0) { + plog(XLOG_WARNING, "Can't bind to port %u", port); + goto give_up; } - 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); + old_ports[i] = nfs_port = port; } - /* - * Clean up mo - */ - if (mo.opt_rhost) - XFREE(mo.opt_rhost); - if (mo.opt_rfs) - XFREE(mo.opt_rfs); } - } + err = 0; - /* - * Free the mount list - */ + give_up: + if (err) { + plog(XLOG_WARNING, "Can't restart %s, leaving it alone", me->mnt_dir); + fs_ops = &amfs_link_ops; + } else { + fs_ops = &amfs_toplvl_ops; + } + + restart_fake_mntfs(me, fs_ops); + } /* end of "for (mlp" */ + + /* free the mount list */ free_mntlist(ml); } diff --git a/contrib/amd/amd/rpc_fwd.c b/contrib/amd/amd/rpc_fwd.c index 5a5439c..b3c8be4 100644 --- a/contrib/amd/amd/rpc_fwd.c +++ b/contrib/amd/amd/rpc_fwd.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1989 Jan-Simon Pendry * Copyright (c) 1989 Imperial College of Science, Technology & Medicine * Copyright (c) 1989 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: rpc_fwd.c,v 1.3.2.5 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/rpc_fwd.c * */ @@ -58,7 +57,7 @@ * is no need to convert to and from network byte ordering. */ -#define XID_ALLOC(struct ) (xid++) +#define XID_ALLOC() (xid++) #define MAX_PACKET_SIZE 8192 /* Maximum UDP packet size */ /* @@ -75,7 +74,7 @@ struct rpc_forward { 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 */ + fwd_fun *rf_fwd; /* Forwarding function */ voidp rf_ptr; struct sockaddr_in rf_sin; }; @@ -94,7 +93,7 @@ static u_int xid; static rpc_forward * fwd_alloc(void) { - time_t now = clocktime(); + time_t now = clocktime(NULL); rpc_forward *p = 0, *p2; /* @@ -117,9 +116,7 @@ fwd_alloc(void) * 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); @@ -228,7 +225,7 @@ fwd_locate(u_int id) * 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) +fwd_packet(int type_id, char *pkt, int len, struct sockaddr_in *fwdto, struct sockaddr_in *replyto, opaque_t cb_arg, fwd_fun cb) { rpc_forward *p; u_int *pkt_int; @@ -247,36 +244,33 @@ fwd_packet(int type_id, voidp pkt, int len, struct sockaddr_in *fwdto, struct so * 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"); + dlog("Sending PORTMAP request %#x", type_id); break; case RPC_XID_MOUNTD: dlog("Sending MOUNTD request %#x", type_id); break; case RPC_XID_NFSPING: - dlog("Sending NFS ping"); + dlog("Sending NFS ping %#x", type_id); + break; + case RPC_XID_WEBNFS: + dlog("Sending WebNFS lookup %#x", type_id); break; default: - dlog("UNKNOWN RPC XID"); + dlog("UNKNOWN RPC XID %#x", type_id); 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 )); + type_id = MK_RPC_XID(type_id, XID_ALLOC()); } p = fwd_alloc(); @@ -290,12 +284,13 @@ fwd_packet(int type_id, voidp pkt, int len, struct sockaddr_in *fwdto, struct so /* * Get the original packet id */ - p->rf_oldid = *pkt_int; + p->rf_oldid = ntohl(*pkt_int); /* * Replace with newly allocated id */ - p->rf_xid = *pkt_int = type_id; + p->rf_xid = type_id; + *pkt_int = htonl(type_id); /* * The sendto may fail if, for example, the route @@ -307,9 +302,9 @@ fwd_packet(int type_id, voidp pkt, int len, struct sockaddr_in *fwdto, struct so { char dq[20]; if (p && fwdto) - dlog("Sending packet id %#x to %s.%d", + dlog("Sending packet id %#x to %s:%d", p->rf_xid, - inet_dquad(dq, fwdto->sin_addr.s_addr), + inet_dquad(dq, sizeof(dq), fwdto->sin_addr.s_addr), ntohs(fwdto->sin_port)); } #endif /* DEBUG */ @@ -349,7 +344,7 @@ out: p->rf_sin = *replyto; else memset((voidp) &p->rf_sin, 0, sizeof(p->rf_sin)); - p->rf_ptr = i; + p->rf_ptr = cb_arg; return error; } @@ -364,6 +359,7 @@ fwd_reply(void) int len; u_int pkt[MAX_PACKET_SIZE / sizeof(u_int) + 1]; u_int *pkt_int; + u_int pkt_xid; int rc; rpc_forward *p; struct sockaddr_in src_addr; @@ -396,6 +392,12 @@ again: rc = ud.udata.len; else { plog(XLOG_ERROR,"fwd_reply failed: t_errno=%d, errno=%d, flags=%d",t_errno,errno, flags); + /* + * Clear error indication, otherwise the error condition persists and + * amd gets into an infinite loop. + */ + if (t_errno == TLOOK) + t_rcvuderr(fwd_sock, NULL); } #else /* not HAVE_TRANSPORT_TYPE_TLI */ rc = recvfrom(fwd_sock, @@ -428,29 +430,29 @@ again: * Find packet reference */ pkt_int = (u_int *) pkt; + pkt_xid = ntohl(*pkt_int); -#ifdef DEBUG - switch (*pkt_int & RPC_XID_MASK) { + switch (pkt_xid & RPC_XID_MASK) { case RPC_XID_PORTMAP: - dlog("Receiving PORTMAP reply"); + dlog("Receiving PORTMAP reply %#x", pkt_xid); break; case RPC_XID_MOUNTD: - dlog("Receiving MOUNTD reply %#x", *pkt_int); + dlog("Receiving MOUNTD reply %#x", pkt_xid); break; case RPC_XID_NFSPING: - dlog("Receiving NFS ping %#x", *pkt_int); + dlog("Receiving NFS ping %#x", pkt_xid); + break; + case RPC_XID_WEBNFS: + dlog("Receiving WebNFS lookup %#x", pkt_xid); break; default: - dlog("UNKNOWN RPC XID"); + dlog("UNKNOWN RPC XID %#x", pkt_xid); break; } -#endif /* DEBUG */ - p = fwd_locate(*pkt_int); + p = fwd_locate(pkt_xid); if (!p) { -#ifdef DEBUG - dlog("Can't forward reply id %#x", *pkt_int); -#endif /* DEBUG */ + dlog("Can't forward reply id %#x", pkt_xid); goto out; } @@ -459,7 +461,7 @@ again: * Put the original message id back * into the packet. */ - *pkt_int = p->rf_oldid; + *pkt_int = htonl(p->rf_oldid); /* * Call forwarding function diff --git a/contrib/amd/amd/sched.c b/contrib/amd/amd/sched.c index c32ef0f..8efe57a 100644 --- a/contrib/amd/amd/sched.c +++ b/contrib/amd/amd/sched.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: sched.c,v 1.4.2.5 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/sched.c * */ @@ -56,12 +55,12 @@ 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 */ + qelem hdr; /* Linked list */ + int pid; /* Process ID of job */ + cb_fun *cb_fun; /* Callback function */ + opaque_t cb_arg; /* Argument for callback */ int w; /* everyone these days uses int, not a "union wait" */ - voidp wchan; /* Wait channel */ + wchan_t wchan; /* Wait channel */ }; /* globals */ @@ -94,12 +93,12 @@ rem_que(qelem *elem) static pjob * -sched_job(cb_fun cf, voidp ca) +sched_job(cb_fun *cf, opaque_t ca) { pjob *p = ALLOC(struct pjob); p->cb_fun = cf; - p->cb_closure = ca; + p->cb_arg = ca; /* * Now place on wait queue @@ -115,7 +114,7 @@ sched_job(cb_fun cf, voidp ca) * cf: Continuation function (ca is its arguments) */ void -run_task(task_fun tf, voidp ta, cb_fun cf, voidp ca) +run_task(task_fun *tf, opaque_t ta, cb_fun *cf, opaque_t ca) { pjob *p = sched_job(cf, ca); #ifdef HAVE_SIGACTION @@ -124,7 +123,7 @@ run_task(task_fun tf, voidp ta, cb_fun cf, voidp ca) int mask; #endif /* not HAVE_SIGACTION */ - p->wchan = (voidp) p; + p->wchan = (wchan_t) p; #ifdef HAVE_SIGACTION sigemptyset(&new); /* initialize signal set we wish to block */ @@ -143,7 +142,7 @@ run_task(task_fun tf, voidp ta, cb_fun cf, voidp ca) return; } - /* child code runs here, parent have returned to caller */ + /* child code runs here, parent has returned to caller */ exit((*tf) (ta)); /* firewall... */ @@ -155,19 +154,17 @@ run_task(task_fun tf, voidp ta, cb_fun cf, voidp ca) * Schedule a task to be run when woken up */ void -sched_task(cb_fun cf, voidp ca, voidp wchan) +sched_task(cb_fun *cf, opaque_t ca, wchan_t wchan) { /* * Allocate a new task */ pjob *p = sched_job(cf, ca); -#ifdef DEBUG - dlog("SLEEP on %#lx", (unsigned long) wchan); -#endif /* DEBUG */ + dlog("SLEEP on %p", wchan); p->wchan = wchan; p->pid = 0; - memset((voidp) &p->w, 0, sizeof(p->w)); + p->w = 0; /* was memset (when ->w was union) */ } @@ -181,7 +178,7 @@ wakeupjob(pjob *p) void -wakeup(voidp wchan) +wakeup(wchan_t wchan) { pjob *p, *p2; @@ -189,7 +186,7 @@ wakeup(voidp wchan) return; /* - * Can't user ITER() here because + * Can't use ITER() here because * wakeupjob() juggles the list. */ for (p = AM_FIRST(pjob, &proc_wait_list); @@ -203,9 +200,20 @@ wakeup(voidp wchan) void -wakeup_task(int rc, int term, voidp cl) +wakeup_task(int rc, int term, wchan_t wchan) { - wakeup(cl); + wakeup(wchan); +} + + +wchan_t +get_mntfs_wchan(mntfs *mf) +{ + if (mf && + mf->mf_ops && + mf->mf_ops->get_wchan) + return mf->mf_ops->get_wchan(mf); + return mf; } @@ -236,9 +244,9 @@ do_task_notify(void) */ if (p->cb_fun) { /* these two trigraphs will ensure compatibility with strict POSIX.1 */ - (*p->cb_fun) (WIFEXITED(p->w) ? WEXITSTATUS(p->w) : 0, - WIFSIGNALED(p->w) ? WTERMSIG(p->w) : 0, - p->cb_closure); + p->cb_fun(WIFEXITED(p->w) ? WEXITSTATUS(p->w) : 0, + WIFSIGNALED(p->w) ? WTERMSIG(p->w) : 0, + p->cb_arg); } XFREE(p); } @@ -261,11 +269,9 @@ sigchld(int sig) 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); @@ -277,18 +283,16 @@ sigchld(int sig) } } /* end of for loop */ -#ifdef DEBUG - if (!p) + if (p == HEAD(pjob, &proc_wait_list)) 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 + * count them all, and NumChildren (and later backoff) will be set * incorrectly. SH/RUNIT 940519. */ - if (--NumChild < 0) - NumChild = 0; + if (--NumChildren < 0) + NumChildren = 0; } /* end of "while wait..." loop */ #ifdef REINSTALL_SIGNAL_HANDLER diff --git a/contrib/amd/amd/srvr_amfs_auto.c b/contrib/amd/amd/srvr_amfs_auto.c index 70c93bf..4742cf6 100644 --- a/contrib/amd/amd/srvr_amfs_auto.c +++ b/contrib/amd/amd/srvr_amfs_auto.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1989 Jan-Simon Pendry * Copyright (c) 1989 Imperial College of Science, Technology & Medicine * Copyright (c) 1989 The Regents of the University of California. @@ -36,9 +36,8 @@ * 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 1.3.2.6 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/srvr_amfs_auto.c * */ @@ -53,9 +52,9 @@ #include <amd.h> /* globals */ -qelem amfs_auto_srvr_list = {&amfs_auto_srvr_list, &amfs_auto_srvr_list}; /* statics */ +static qelem amfs_auto_srvr_list = {&amfs_auto_srvr_list, &amfs_auto_srvr_list}; static fserver *localhost; @@ -63,7 +62,7 @@ static fserver *localhost; * Find an nfs server for the local host */ fserver * -find_amfs_auto_srvr(mntfs *mf) +amfs_generic_find_srvr(mntfs *mf) { fserver *fs = localhost; @@ -73,8 +72,8 @@ find_amfs_auto_srvr(mntfs *mf) fs->fs_host = strdup("localhost"); fs->fs_ip = 0; fs->fs_cid = 0; - fs->fs_pinger = 0; - fs->fs_flags = FSF_VALID; + fs->fs_pinger = AM_PINGER; + fs->fs_flags = FSF_VALID | FSF_PING_UNINIT; fs->fs_type = "local"; fs->fs_private = 0; fs->fs_prfree = 0; @@ -119,9 +118,7 @@ timeout_srvr(voidp v) * 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); @@ -167,11 +164,9 @@ free_srvr(fserver *fs) * removed in AM_TTL seconds if no * other mntfs is referencing it. */ - int ttl = (fs->fs_flags & (FSF_DOWN | FSF_ERROR)) ? 19 : AM_TTL; + int ttl = (FSRV_ERROR(fs) || FSRV_ISDOWN(fs)) ? 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); /* diff --git a/contrib/amd/amd/srvr_nfs.c b/contrib/amd/amd/srvr_nfs.c index 48e0789..0c74a65 100644 --- a/contrib/amd/amd/srvr_nfs.c +++ b/contrib/amd/amd/srvr_nfs.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997-2004 Erez Zadok + * Copyright (c) 1997-2006 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. @@ -36,9 +36,8 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * %W% (Berkeley) %G% * - * $Id: srvr_nfs.c,v 1.7.2.11 2004/01/06 03:15:16 ezk Exp $ + * File: am-utils/amd/srvr_nfs.c * */ @@ -72,8 +71,6 @@ */ #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 */ @@ -88,9 +85,16 @@ typedef struct nfs_private { 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]; +static int global_xid; /* For NFS pings */ +#define XID_ALLOC() (++global_xid) + +#ifdef HAVE_FS_NFS3 +# define NUM_NFS_VERS 2 +#else /* not HAVE_FS_NFS3 */ +# define NUM_NFS_VERS 1 +#endif /* not HAVE_FS_NFS3 */ +static int ping_len[NUM_NFS_VERS]; +static char ping_buf[NUM_NFS_VERS][sizeof(struct rpc_msg) + 32]; #if defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) /* @@ -99,6 +103,8 @@ static char ping_buf[sizeof(struct rpc_msg) + 32]; * Note that Solaris 8 and newer NetBSD systems are switching to UDP first, * so this order may have to be adjusted for Amd in the future once more * vendors make that change. -Erez 11/24/2000 + * + * Or we might simply make this is a platform-specific order. -Ion 09/13/2003 */ static char *protocols[] = { "tcp", "udp", NULL }; #endif /* defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) */ @@ -107,20 +113,21 @@ static char *protocols[] = { "tcp", "udp", NULL }; static void nfs_keepalive(voidp); - /* - * Flush any cached data + * Flush cached data for an fserver (or for all, if fs==NULL) */ void -flush_srvr_nfs_cache(void) +flush_srvr_nfs_cache(fserver *fs) { - fserver *fs = 0; + fserver *fs2 = NULL; - 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; + ITER(fs2, fserver, &nfs_srvr_list) { + if (fs == NULL || fs == fs2) { + nfs_private *np = (nfs_private *) fs2->fs_private; + if (np) { + np->np_mountd_inval = TRUE; + np->np_error = -1; + } } } } @@ -130,7 +137,7 @@ flush_srvr_nfs_cache(void) * Startup the NFS ping for a particular version. */ static void -start_ping(u_long nfs_version) +create_ping_payload(u_long nfs_version) { XDR ping_xdr; struct rpc_msg ping_msg; @@ -140,16 +147,16 @@ start_ping(u_long nfs_version) */ 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", (int) nfs_version); + plog(XLOG_WARNING, "create_ping_payload: nfs_version = 0, changed to 2"); + } else + plog(XLOG_INFO, "create_ping_payload: nfs_version: %d", (int) nfs_version); rpc_msg_init(&ping_msg, NFS_PROGRAM, nfs_version, NFSPROC_NULL); /* * Create an XDR endpoint */ - xdrmem_create(&ping_xdr, ping_buf, sizeof(ping_buf), XDR_ENCODE); + xdrmem_create(&ping_xdr, ping_buf[nfs_version - NFS_VERSION], sizeof(ping_buf[0]), XDR_ENCODE); /* * Create the NFS ping message @@ -161,7 +168,7 @@ start_ping(u_long nfs_version) /* * Find out how long it is */ - ping_len = xdr_getpos(&ping_xdr); + ping_len[nfs_version - NFS_VERSION] = xdr_getpos(&ping_xdr); /* * Destroy the XDR endpoint - we don't need it anymore @@ -183,7 +190,7 @@ got_portmap(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, * Find which fileserver we are talking about */ ITER(fs, fserver, &nfs_srvr_list) - if (fs == fs2) + if (fs == fs2) break; if (fs == fs2) { @@ -192,9 +199,7 @@ got_portmap(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, nfs_private *np = (nfs_private *) fs->fs_private; if (!error && port) { -#ifdef DEBUG dlog("got port (%d) for mountd on %s", (int) port, fs->fs_host); -#endif /* DEBUG */ /* * Grab the port number. Portmap sends back * an u_long in native ordering, so it @@ -205,10 +210,8 @@ got_portmap(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, 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, (int) port); -#endif /* DEBUG */ /* * Almost certainly no mountd running on remote host */ @@ -218,13 +221,9 @@ got_portmap(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, 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 */ } } @@ -258,7 +257,7 @@ call_portmap(fserver *fs, AUTH *auth, u_long prog, u_long vers, u_long prot) memset((voidp) &sin, 0, sizeof(sin)); sin = *fs->fs_ip; sin.sin_port = htons(PMAPPORT); - error = fwd_packet(RPC_XID_PORTMAP, (voidp) iobuf, len, + error = fwd_packet(RPC_XID_PORTMAP, iobuf, len, &sin, &sin, (voidp) fs, got_portmap); } else { error = -len; @@ -274,6 +273,12 @@ recompute_portmap(fserver *fs) int error; u_long mnt_version; + /* + * No portmap calls for pure WebNFS servers. + */ + if (fs->fs_flags & FSF_WEBNFS) + return; + if (nfs_auth) error = 0; else @@ -288,10 +293,11 @@ recompute_portmap(fserver *fs) if (fs->fs_version == 0) plog(XLOG_WARNING, "recompute_portmap: nfs_version = 0 fixed"); - plog(XLOG_INFO, "recompute_portmap: NFS version %d", (int) fs->fs_version); + plog(XLOG_INFO, "recompute_portmap: NFS version %d on %s", + (int) fs->fs_version, fs->fs_host); #ifdef HAVE_FS_NFS3 if (fs->fs_version == NFS_VERSION3) - mnt_version = MOUNTVERS3; + mnt_version = AM_MOUNTVERS3; else #endif /* HAVE_FS_NFS3 */ mnt_version = MOUNTVERS; @@ -301,19 +307,61 @@ recompute_portmap(fserver *fs) } +int +get_mountd_port(fserver *fs, u_short *port, wchan_t wchan) +{ + int error = -1; + if (FSRV_ISDOWN(fs)) + return EWOULDBLOCK; + + if (FSRV_ISUP(fs)) { + nfs_private *np = (nfs_private *) fs->fs_private; + if (np->np_error == 0) { + *port = np->np_mountd; + error = 0; + } else { + error = np->np_error; + } + /* + * Now go get the port mapping again in case it changed. + * Note that it is used even if (np_mountd_inval) + * is True. The flag is used simply as an + * indication that the mountd may be invalid, not + * that it is known to be invalid. + */ + if (np->np_mountd_inval) + recompute_portmap(fs); + else + np->np_mountd_inval = TRUE; + } + if (error < 0 && wchan && !(fs->fs_flags & FSF_WANT)) { + /* + * If a wait channel is supplied, and no + * error has yet occurred, then arrange + * that a wakeup is done on the wait channel, + * whenever a wakeup is done on this fs node. + * Wakeup's are done on the fs node whenever + * it changes state - thus causing control to + * come back here and new, better things to happen. + */ + fs->fs_flags |= FSF_WANT; + sched_task(wakeup_task, wchan, (wchan_t) fs); + } + return error; +} + + /* * This is called when we get a reply to an RPC ping. * The value of id was taken from the nfs_private * structure when the ping was transmitted. */ static void -nfs_pinged(voidp pkt, int len, struct sockaddr_in *sp, struct sockaddr_in *tsp, voidp idv, int done) +nfs_keepalive_callback(voidp pkt, int len, struct sockaddr_in *sp, struct sockaddr_in *tsp, voidp idv, int done) { - int xid = (long) idv; /* for 64-bit archs */ + int xid = (long) idv; /* cast needed for 64-bit archs */ fserver *fs; -#ifdef DEBUG int found_map = 0; -#endif /* DEBUG */ if (!done) return; @@ -336,19 +384,15 @@ nfs_pinged(voidp pkt, int len, struct sockaddr_in *sp, struct sockaddr_in *tsp, } 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"); @@ -365,13 +409,13 @@ nfs_pinged(voidp pkt, int len, struct sockaddr_in *sp, struct sockaddr_in *tsp, /* * Update ttl for this server */ - np->np_ttl = clocktime() + + np->np_ttl = clocktime(NULL) + (MAX_ALLOWED_PINGS - 1) * FAST_NFS_PING + fs->fs_pinger - 1; /* * New RPC xid... */ - np->np_xid = NPXID_ALLOC(struct ); + np->np_xid = XID_ALLOC(); /* * Failed pings is zero... @@ -384,17 +428,53 @@ nfs_pinged(voidp pkt, int len, struct sockaddr_in *sp, struct sockaddr_in *tsp, 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 */ +} + + +static void +check_fs_addr_change(fserver *fs) +{ + struct hostent *hp = NULL; + struct in_addr ia; + char *old_ipaddr, *new_ipaddr; + + hp = gethostbyname(fs->fs_host); + if (!hp || + hp->h_addrtype != AF_INET || + !STREQ((char *) hp->h_name, fs->fs_host) || + memcmp((voidp) &fs->fs_ip->sin_addr, + (voidp) hp->h_addr, + sizeof(fs->fs_ip->sin_addr)) == 0) + return; + /* if got here: downed server changed IP address */ + old_ipaddr = strdup(inet_ntoa(fs->fs_ip->sin_addr)); + memmove((voidp) &ia, (voidp) hp->h_addr, sizeof(struct in_addr)); + new_ipaddr = inet_ntoa(ia); /* ntoa uses static buf */ + plog(XLOG_WARNING, "EZK: down fileserver %s changed ip: %s -> %s", + fs->fs_host, old_ipaddr, new_ipaddr); + XFREE(old_ipaddr); + /* copy new IP addr */ + memmove((voidp) &fs->fs_ip->sin_addr, + (voidp) hp->h_addr, + sizeof(fs->fs_ip->sin_addr)); + /* XXX: do we need to un/set these flags? */ + fs->fs_flags &= ~FSF_DOWN; + fs->fs_flags |= FSF_VALID | FSF_WANT; + map_flush_srvr(fs); /* XXX: a race with flush_srvr_nfs_cache? */ + flush_srvr_nfs_cache(fs); + fs->fs_flags |= FSF_FORCE_UNMOUNT; + +#if 0 + flush_nfs_fhandle_cache(fs); /* done in caller: nfs_keepalive_timeout */ + /* XXX: need to purge nfs_private so that somehow it will get re-initialized? */ +#endif } @@ -402,7 +482,7 @@ nfs_pinged(voidp pkt, int len, struct sockaddr_in *sp, struct sockaddr_in *tsp, * Called when no ping-reply received */ static void -nfs_timed_out(voidp v) +nfs_keepalive_timeout(voidp v) { fserver *fs = v; nfs_private *np = (nfs_private *) fs->fs_private; @@ -423,11 +503,9 @@ nfs_timed_out(voidp v) /* * If ttl has expired then guess that it is dead */ - if (np->np_ttl < clocktime()) { + if (np->np_ttl < clocktime(NULL)) { int oflags = fs->fs_flags; -#ifdef DEBUG dlog("ttl has expired"); -#endif /* DEBUG */ if ((fs->fs_flags & FSF_DOWN) == 0) { /* * Server was up, but is now down. @@ -441,14 +519,13 @@ nfs_timed_out(voidp v) */ flush_nfs_fhandle_cache(fs); np->np_error = -1; + check_fs_addr_change(fs); /* check if IP addr of fserver changed */ } else { /* * Known to be down */ -#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)) @@ -458,17 +535,15 @@ nfs_timed_out(voidp v) */ np->np_ping = 0; } 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 */ } /* * New RPC xid, so any late responses to the previous ping * get ignored... */ - np->np_xid = NPXID_ALLOC(struct ); + np->np_xid = XID_ALLOC(); /* * Run keepalive again @@ -492,19 +567,19 @@ nfs_keepalive(voidp v) * Send an NFS ping to this node */ - if (ping_len == 0) - start_ping(fs->fs_version); + if (ping_len[fs->fs_version - NFS_VERSION] == 0) + create_ping_payload(fs->fs_version); /* * Queue the packet... */ error = fwd_packet(MK_RPC_XID(RPC_XID_NFSPING, np->np_xid), - (voidp) ping_buf, - ping_len, + ping_buf[fs->fs_version - NFS_VERSION], + ping_len[fs->fs_version - NFS_VERSION], fs->fs_ip, (struct sockaddr_in *) 0, - (voidp) ((long) np->np_xid), /* for 64-bit archs */ - nfs_pinged); + (voidp) ((long) np->np_xid), /* cast needed for 64-bit archs */ + nfs_keepalive_callback); /* * See if a hard error occurred @@ -517,7 +592,7 @@ nfs_keepalive(voidp v) np->np_ping = MAX_ALLOWED_PINGS; /* immediately down */ np->np_ttl = (time_t) 0; /* - * This causes an immediate call to nfs_timed_out + * This causes an immediate call to nfs_keepalive_timeout * whenever the server was thought to be up. * See +++ below. */ @@ -525,15 +600,13 @@ nfs_keepalive(voidp v) 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. + * the remote system is known to be down. */ switch (fs->fs_flags & (FSF_DOWN | FSF_VALID)) { case FSF_VALID: /* Up */ @@ -550,73 +623,41 @@ nfs_keepalive(voidp v) 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 occurred, then arrange - * that a wakeup is done on the wait channel, - * whenever a wakeup is done on this fs node. - * Wakeup's are done on the fs node whenever - * it changes state - thus causing control to - * come back here and new, better things to happen. - */ - fs->fs_flags |= FSF_WANT; - sched_task(wakeup_task, wchan, (voidp) fs); - } - return error; + fs->fs_cid = timeout(fstimeo, nfs_keepalive_timeout, (voidp) fs); } static void start_nfs_pings(fserver *fs, int pingval) { - if (fs->fs_flags & FSF_PINGING) { -#ifdef DEBUG - dlog("Already running pings to %s", fs->fs_host); -#endif /* DEBUG */ + if (pingval == 0) /* could be because ping mnt option not found */ + pingval = AM_PINGER; + /* if pings haven't been initalized, then init them for first time */ + if (fs->fs_flags & FSF_PING_UNINIT) { + fs->fs_flags &= ~FSF_PING_UNINIT; + plog(XLOG_INFO, "initializing %s's pinger to %d sec", fs->fs_host, pingval); + goto do_pings; + } + + if ((fs->fs_flags & FSF_PINGING) && fs->fs_pinger == pingval) { + dlog("already running pings to %s", fs->fs_host); return; } + /* if got here, then we need to update the ping value */ + plog(XLOG_INFO, "changing %s's ping value from %d%s to %d%s", + fs->fs_host, + fs->fs_pinger, (fs->fs_pinger < 0 ? " (off)" : ""), + pingval, (pingval < 0 ? " (off)" : "")); + do_pings: + fs->fs_pinger = pingval; + if (fs->fs_cid) untimeout(fs->fs_cid); if (pingval < 0) { - srvrlog(fs, "wired up"); + srvrlog(fs, "wired up (pings disabled)"); fs->fs_flags |= FSF_VALID; fs->fs_flags &= ~FSF_DOWN; } else { @@ -633,17 +674,18 @@ 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; + struct hostent *hp = NULL; + struct sockaddr_in *ip = NULL; 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 */ + u_long best_nfs_version = 0; + char *nfs_proto = NULL; /* no IP protocol either */ + int nfs_port = 0; + int nfs_port_opt = 0; + int fserver_is_down = 0; /* * Get ping interval from mount options. @@ -653,49 +695,60 @@ find_nfs_srvr(mntfs *mf) 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. - */ + if (mf->mf_flags & MFF_NFS_SCALEDOWN) { + /* + * the server granted us a filehandle, but we were unable to mount it. + * therefore, scale down to NFSv2/UDP and try again. + */ + nfs_version = NFS_VERSION; + nfs_proto = "udp"; + plog(XLOG_WARNING, "find_nfs_srvr: NFS mount failed, trying again with NFSv2/UDP"); + mf->mf_flags &= ~MFF_NFS_SCALEDOWN; + } else { + /* + * Get the NFS version from the mount options. This is used + * to decide the highest NFS version to try. + */ #ifdef MNTTAB_OPT_VERS - nfs_version = hasmntval(&mnt, MNTTAB_OPT_VERS); + nfs_version = hasmntval(&mnt, MNTTAB_OPT_VERS); #endif /* MNTTAB_OPT_VERS */ #ifdef MNTTAB_OPT_PROTO - { - char *proto_opt = hasmnteq(&mnt, MNTTAB_OPT_PROTO); - if (proto_opt) { - char **p; - for (p = protocols; *p; p ++) - if (NSTREQ(proto_opt, *p, strlen(*p))) { - nfs_proto = *p; - break; - } - if (*p == NULL) - plog(XLOG_WARNING, "ignoring unknown protocol option for %s:%s", - host, rfsname); + { + char *proto_opt = hasmnteq(&mnt, MNTTAB_OPT_PROTO); + if (proto_opt) { + char **p; + for (p = protocols; *p; p++) + if (NSTREQ(proto_opt, *p, strlen(*p))) { + nfs_proto = *p; + break; + } + if (*p == NULL) + plog(XLOG_WARNING, "ignoring unknown protocol option for %s:%s", + host, mf->mf_fo->opt_rfs); + } } - } #endif /* MNTTAB_OPT_PROTO */ #ifdef HAVE_NFS_NFSV2_H - /* allow overriding if nfsv2 option is specified in mount options */ - if (hasmntopt(&mnt, "nfsv2")) { - nfs_version = (u_long) 2; /* nullify any ``vers=X'' statements */ - nfs_proto = "udp"; /* nullify any ``proto=tcp'' statements */ - plog(XLOG_WARNING, "found compatiblity option \"nfsv2\": set options vers=2,proto=udp for host %s", host); - } + /* allow overriding if nfsv2 option is specified in mount options */ + if (amu_hasmntopt(&mnt, "nfsv2")) { + nfs_version = NFS_VERSION;/* nullify any ``vers=X'' statements */ + nfs_proto = "udp"; /* nullify any ``proto=tcp'' statements */ + plog(XLOG_WARNING, "found compatibility option \"nfsv2\": set options vers=2,proto=udp for host %s", host); + } #endif /* HAVE_NFS_NFSV2_H */ - /* check if we globally overridden the NFS version/protocol */ - if (gopt.nfs_vers) { - nfs_version = gopt.nfs_vers; - plog(XLOG_INFO, "find_nfs_srvr: force NFS version to %d", - (int) nfs_version); - } - if (gopt.nfs_proto) { - nfs_proto = gopt.nfs_proto; - plog(XLOG_INFO, "find_nfs_srvr: force NFS protocol transport to %s", nfs_proto); + /* check if we've globally overridden the NFS version/protocol */ + if (gopt.nfs_vers) { + nfs_version = gopt.nfs_vers; + plog(XLOG_INFO, "find_nfs_srvr: force NFS version to %d", + (int) nfs_version); + } + if (gopt.nfs_proto) { + nfs_proto = gopt.nfs_proto; + plog(XLOG_INFO, "find_nfs_srvr: force NFS protocol transport to %s", nfs_proto); + } } /* @@ -718,72 +771,148 @@ find_nfs_srvr(mntfs *mf) case AF_INET: ip = ALLOC(struct sockaddr_in); memset((voidp) ip, 0, sizeof(*ip)); + /* as per POSIX, sin_len need not be set (used internally by kernel) */ ip->sin_family = AF_INET; memmove((voidp) &ip->sin_addr, (voidp) hp->h_addr, sizeof(ip->sin_addr)); - - ip->sin_port = htons(NFS_PORT); break; default: - ip = 0; - break; + plog(XLOG_USER, "No IP address for host %s", host); + goto no_dns; } } else { plog(XLOG_USER, "Unknown host: %s", host); - ip = 0; + goto no_dns; } /* - * Get the NFS Version, and verify server is up. Probably no - * longer need to start server down below. + * This may not be the best way to do things, but it really doesn't make + * sense to query a file server which is marked as 'down' for any + * version/proto combination. */ - if (ip) { + ITER(fs, fserver, &nfs_srvr_list) { + if (FSRV_ISDOWN(fs) && + STREQ(host, fs->fs_host)) { + plog(XLOG_WARNING, "fileserver %s is already hung - not running NFS proto/version discovery", host); + fs->fs_refc++; + if (ip) + XFREE(ip); + return fs; + } + } + + /* + * Get the NFS Version, and verify server is up. + * If the client only supports NFSv2, hardcode it but still try to + * contact the remote portmapper to see if the service is running. + */ +#ifndef HAVE_FS_NFS3 + nfs_version = NFS_VERSION; + nfs_proto = "udp"; + plog(XLOG_INFO, "The client supports only NFS(2,udp)"); +#endif /* not HAVE_FS_NFS3 */ + + + if (amu_hasmntopt(&mnt, MNTTAB_OPT_PUBLIC)) { + /* + * Use WebNFS to obtain file handles. + */ + mf->mf_flags |= MFF_WEBNFS; + plog(XLOG_INFO, "%s option used, NOT contacting the portmapper on %s", + MNTTAB_OPT_PUBLIC, host); + /* + * Prefer NFSv3/tcp if the client supports it (cf. RFC 2054, 7). + */ + if (!nfs_version) { #ifdef HAVE_FS_NFS3 + nfs_version = NFS_VERSION3; +#else /* not HAVE_FS_NFS3 */ + nfs_version = NFS_VERSION; +#endif /* not HAVE_FS_NFS3 */ + plog(XLOG_INFO, "No NFS version specified, will use NFSv%d", + (int) nfs_version); + } + if (!nfs_proto) { +#if defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) + nfs_proto = "tcp"; +#else /* not defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) */ + nfs_proto = "udp"; +#endif /* not defined(MNTTAB_OPT_PROTO) || defined(HAVE_FS_NFS3) */ + plog(XLOG_INFO, "No NFS protocol transport specified, will use %s", + nfs_proto); + } + } else { /* * Find the best combination of NFS version and protocol. * When given a choice, use the highest available version, * and use TCP over UDP if available. */ - if (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; + if (check_pmap_up(host, ip)) { + if (nfs_proto) { + best_nfs_version = get_nfs_version(host, ip, nfs_version, nfs_proto); + nfs_port = ip->sin_port; + } +#ifdef MNTTAB_OPT_PROTO + else { + u_int proto_nfs_version; + char **p; + + for (p = protocols; *p; p++) { + proto_nfs_version = get_nfs_version(host, ip, nfs_version, *p); + + if (proto_nfs_version > best_nfs_version) { + best_nfs_version = proto_nfs_version; + nfs_proto = *p; + nfs_port = ip->sin_port; + } } } - nfs_version = best_nfs_version; +#endif /* MNTTAB_OPT_PROTO */ + } else { + plog(XLOG_INFO, "portmapper service not running on %s", host); } - if (!nfs_version) { + /* use the portmapper results only nfs_version is not set yet */ + if (!best_nfs_version) { /* * If the NFS server is down or does not support the portmapper call * (such as certain Novell NFS servers) we mark it as version 2 and we - * let the nfs code deal with the case that is down. If when the - * server comes back up, it can support NFS V.3 and/or TCP, it will + * let the nfs code deal with the case when it is down. If/when the + * server comes back up and it can support NFSv3 and/or TCP, it will * use those. */ - nfs_version = NFS_VERSION; - nfs_proto = "udp"; + if (nfs_version == 0) { + nfs_version = NFS_VERSION; + nfs_proto = "udp"; + } + plog(XLOG_INFO, "NFS service not running on %s", host); + fserver_is_down = 1; + } else { + if (nfs_version == 0) + nfs_version = best_nfs_version; + plog(XLOG_INFO, "Using NFS version %d, protocol %s on host %s", + (int) nfs_version, nfs_proto, host); } -#else /* not HAVE_FS_NFS3 */ - nfs_version = NFS_VERSION; -#endif /* not HAVE_FS_NFS3 */ } - if (!nfs_proto) - nfs_proto = "udp"; + /* + * Determine the NFS port. + * + * A valid "port" mount option overrides anything else. + * If the port has been determined from the portmapper, use that. + * Default to NFS_PORT otherwise (cf. RFC 2054, 3). + */ + nfs_port_opt = hasmntval(&mnt, MNTTAB_OPT_PORT); + if (nfs_port_opt > 0) + nfs_port = htons(nfs_port_opt); + if (!nfs_port) + nfs_port = htons(NFS_PORT); - plog(XLOG_INFO, "Using NFS version %d, protocol %s on host %s", - (int) nfs_version, nfs_proto, host); + dlog("find_nfs_srvr: using port %d for nfs on %s", + (int) ntohs(nfs_port), host); + ip->sin_port = nfs_port; +no_dns: /* * Try to find an existing fs server structure for this host. * Note that differing versions or protocols have their own structures. @@ -795,16 +924,46 @@ find_nfs_srvr(mntfs *mf) 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. + * fill in the IP address -- this is only needed + * if there is a chance an IP address will change + * between mounts. + * Mike Mitchell, mcm@unx.sas.com, 09/08/93 + */ + if (hp && fs->fs_ip && + memcmp((voidp) &fs->fs_ip->sin_addr, + (voidp) hp->h_addr, + sizeof(fs->fs_ip->sin_addr)) != 0) { + struct in_addr ia; + char *old_ipaddr, *new_ipaddr; + old_ipaddr = strdup(inet_ntoa(fs->fs_ip->sin_addr)); + memmove((voidp) &ia, (voidp) hp->h_addr, sizeof(struct in_addr)); + new_ipaddr = inet_ntoa(ia); /* ntoa uses static buf */ + plog(XLOG_WARNING, "fileserver %s changed ip: %s -> %s", + fs->fs_host, old_ipaddr, new_ipaddr); + XFREE(old_ipaddr); + flush_nfs_fhandle_cache(fs); + memmove((voidp) &fs->fs_ip->sin_addr, (voidp) hp->h_addr, sizeof(fs->fs_ip->sin_addr)); + } + + /* + * If the new file systems doesn't use WebNFS, the nfs pings may + * try to contact the portmapper. + */ + if (!(mf->mf_flags & MFF_WEBNFS)) + fs->fs_flags &= ~FSF_WEBNFS; + + /* check if pingval needs to be updated/set/reset */ + start_nfs_pings(fs, pingval); + + /* + * Following if statement from Mike Mitchell <mcm@unx.sas.com> + * Initialize the ping data if we aren't pinging now. The np_ttl and + * np_ping fields are especially important. */ if (!(fs->fs_flags & FSF_PINGING)) { np = (nfs_private *) fs->fs_private; np->np_mountd_inval = TRUE; - np->np_xid = NPXID_ALLOC(struct ); + np->np_xid = XID_ALLOC(); np->np_error = -1; np->np_ping = 0; /* @@ -812,18 +971,12 @@ find_nfs_srvr(mntfs *mf) * after MAX_ALLOWED_PINGS of the fast variety * have failed. */ - np->np_ttl = MAX_ALLOWED_PINGS * FAST_NFS_PING + clocktime() - 1; + np->np_ttl = MAX_ALLOWED_PINGS * FAST_NFS_PING + clocktime(NULL) - 1; + start_nfs_pings(fs, pingval); + if (fserver_is_down) + fs->fs_flags |= FSF_VALID | FSF_DOWN; } - /* - * 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); @@ -852,29 +1005,32 @@ find_nfs_srvr(mntfs *mf) mf->mf_flags |= MFF_ERROR; mf->mf_error = ENOENT; } + if (mf->mf_flags & MFF_WEBNFS) + fs->fs_flags |= FSF_WEBNFS; fs->fs_version = nfs_version; fs->fs_proto = nfs_proto; fs->fs_type = MNTTAB_TYPE_NFS; fs->fs_pinger = AM_PINGER; + fs->fs_flags |= FSF_PING_UNINIT; /* pinger hasn't been initialized */ np = ALLOC(struct nfs_private); memset((voidp) np, 0, sizeof(*np)); np->np_mountd_inval = TRUE; - np->np_xid = NPXID_ALLOC(struct ); + np->np_xid = XID_ALLOC(); np->np_error = -1; /* * Initially the server will be deemed dead after * MAX_ALLOWED_PINGS of the fast variety have failed. */ - np->np_ttl = clocktime() + MAX_ALLOWED_PINGS * FAST_NFS_PING - 1; + np->np_ttl = clocktime(NULL) + 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 - */ + if (!FSRV_ERROR(fs)) { + /* start of keepalive timer, first updating pingval */ start_nfs_pings(fs, pingval); + if (fserver_is_down) + fs->fs_flags |= FSF_VALID | FSF_DOWN; } /* |