summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2004-01-15 01:34:49 (GMT)
committerH. Peter Anvin <hpa@zytor.com>2004-01-15 01:34:49 (GMT)
commit02f901f6769a427d1c1d60a80fb12070f5d3fd00 (patch)
treea628ed7061575ff50c02f576b7ca7fa42785c61e
parent3a345e8d816b93b042b44f84639bbee5e7e3f42a (diff)
downloadautofs3-02f901f6769a427d1c1d60a80fb12070f5d3fd00.zip
autofs3-02f901f6769a427d1c1d60a80fb12070f5d3fd00.tar.gz
autofs3-02f901f6769a427d1c1d60a80fb12070f5d3fd00.tar.bz2
autofs3-02f901f6769a427d1c1d60a80fb12070f5d3fd00.tar.xz
Add mount_default module (missing from previous checkin)
-rw-r--r--modules/mount_default.c185
1 files changed, 185 insertions, 0 deletions
diff --git a/modules/mount_default.c b/modules/mount_default.c
new file mode 100644
index 0000000..66b0d0a
--- /dev/null
+++ b/modules/mount_default.c
@@ -0,0 +1,185 @@
+#ident "$Id$"
+/* ----------------------------------------------------------------------- *
+ *
+ * mount_default.c - Module for Linux automountd to mount an NFS filesystem,
+ * with fallback to symlinking if the path is local
+ *
+ * Copyright 1997-2004 Transmeta Corporation - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
+ * USA; either version 2 of the License, or (at your option) any later
+ * version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include <alloca.h>
+#include <stdio.h>
+#include <malloc.h>
+#include <errno.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <string.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+
+#define MODULE_MOUNT
+#include "automount.h"
+
+#define MODPREFIX "mount(default): "
+int mount_version = AUTOFS_MOUNT_VERSION; /* Required by protocol */
+
+static int udpproto;
+static short port_discard;
+
+static struct mount_mod *mount_bind = NULL;
+static struct mount_mod *mount_nfs = NULL;
+
+int mount_init(void **context)
+{
+ struct protoent *udp;
+ struct servent *port_dis;
+
+ /* These are context independent */
+ udp = getprotobyname("udp");
+ udpproto = udp ? udp->p_proto : 0;
+ port_dis = getservbyname("discard","udp");
+ if ( port_dis )
+ port_discard = port_dis->s_port;
+ else
+ port_discard = htons(9); /* 9 is the standard discard port */
+
+ /* Make sure we have the real mount methods available */
+ if ( !mount_bind )
+ mount_bind = open_mount("bind", MODPREFIX);
+ if ( !mount_nfs )
+ mount_bind = open_mount("nfs", MODPREFIX);
+
+ return !mount_bind || !mount_nfs;
+}
+
+int mount_mount(const char *root, const char *name, int name_len,
+ const char *what, const char *fstype,
+ const char *options, void *context)
+{
+ char *colon, *localname, **haddr, *fullpath;
+ char *whatstr, *hostname, *comma, *paren;
+ struct hostent *he;
+ struct sockaddr_in saddr, laddr;
+ int sock, local;
+ size_t len;
+
+ whatstr = alloca(strlen(what)+1);
+ if ( !whatstr ) {
+ syslog(LOG_NOTICE, MODPREFIX "alloca: %m");
+ return 1;
+ }
+ strcpy(whatstr, what);
+
+ colon = strchr(whatstr, ':');
+ if ( !colon ) {
+ /* No colon, take this as a bind (local) entry */
+ local = 1;
+ localname = whatstr;
+ } else {
+ *colon = '\0';
+
+ /* The host part may actually be a comma-separated list of hosts with
+ parenthesized weights. We want to check each host, ignoring any
+ weights, until we either find the localhost or reach the end of the
+ list. */
+ local = 0;
+ localname = colon+1;
+ hostname = whatstr;
+ do {
+ comma = strchr(hostname, ',');
+ if ( comma )
+ *comma = '\0';
+
+ paren = strchr(hostname, '(');
+ if ( paren )
+ *paren = '\0';
+
+ if ( !(he = gethostbyname(hostname)) ) {
+ syslog(LOG_NOTICE, MODPREFIX "entry %s: host %s: lookup failure",
+ name, hostname);
+ return 1; /* No such host */
+ }
+
+ /* Probe to see if we are the local host. Open a UDP socket and see
+ if the local address is the same as the remote one */
+
+ for ( haddr = he->h_addr_list ; *haddr ; haddr++ ) {
+ sock = socket(AF_INET, SOCK_DGRAM, udpproto);
+ if ( sock < 0 ) {
+ syslog(LOG_ERR, MODPREFIX "socket: %m");
+ return 1;
+ }
+ saddr.sin_family = AF_INET;
+ memcpy(&saddr.sin_addr, *haddr, he->h_length);
+ saddr.sin_port = port_discard;
+
+ len = sizeof(laddr);
+
+ if ( connect(sock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0 )
+ continue; /* Assume it wasn't local */
+
+ if ( getsockname(sock, (struct sockaddr *) &laddr, &len) < 0 ) {
+ syslog(LOG_ERR, MODPREFIX "getsockname failed for %s: %m", name);
+ close(sock);
+ return 1;
+ }
+ close(sock);
+
+ if ( !memcmp(&saddr.sin_addr,&laddr.sin_addr,he->h_length) ) {
+ local = 1;
+ break;
+ }
+ }
+
+ if ( paren )
+ *paren = '(';
+
+ if ( comma ) {
+ *comma = ',';
+ hostname = comma + 1;
+ } else {
+ hostname += strlen(hostname);
+ }
+ } while (*hostname && !local);
+ }
+
+ fullpath = alloca(strlen(root)+name_len+2);
+ if ( !fullpath ) {
+ syslog(LOG_ERR, MODPREFIX "alloca: %m");
+ return 1;
+ }
+ sprintf(fullpath, "%s/%s", root, name);
+
+ if ( local ) {
+ /* Local host -- do a "bind" */
+
+ syslog(LOG_DEBUG, MODPREFIX "%s is local, doing bind", name);
+ return mount_bind->mount_mount(root, name, name_len, localname,
+ "bind", NULL, mount_bind->context);
+ } else {
+ /* Not a local host - do an NFS mount */
+
+ syslog(LOG_DEBUG, MODPREFIX "%s is remote, mounting nfs", name);
+ return mount_nfs->mount_mount(root, name, name_len, what,
+ "nfs", options, mount_nfs->context);
+ }
+}
+
+int mount_done(void *context)
+{
+ return
+ mount_bind->mount_done(mount_bind->context) |
+ mount_nfs->mount_done(mount_bind->context);
+}