Ok, here's an updated patch. I did end up using 'indent' to do it. This
is the first time I've used it, but it seems to have done a pretty
decent job.
Some of the lines are still over 80 cols, but most of those are strings
in debug statements that are in heavily nested sections of code -- it
was either go over 80 columns or violate the 8 char tab rule. I went
with the former. Let me know what you think.
-- Jeff
-----------------------------------------------------------------
--- mount_nfs.c.orig 2005-01-10 08:28:29.000000000 -0500
+++ mount_nfs.c 2005-04-08 10:13:29.000000000 -0400
@@ -1,4 +1,4 @@
-#ident "$Id: mount_nfs.c,v 1.21 2005/01/10 13:28:29 raven Exp $"
+#ident "$Id: mount_nfs.c,v 1.12 2004/05/18 12:20:08 raven Exp $"
/* ----------------------------------------------------------------------- *
*
* mount_nfs.c - Module for Linux automountd to mount an NFS filesystem,
@@ -6,6 +6,7 @@
*
* Copyright 1997 Transmeta Corporation - All Rights Reserved
* Copyright 1999-2000 Jeremy Fitzhardinge <[EMAIL PROTECTED]>
+ * Copyright 2005 Jeff Layton/Red Hat <[EMAIL PROTECTED]>
*
* 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
@@ -31,12 +32,50 @@
#include <netinet/in.h>
#include <linux/nfs.h>
#include <linux/nfs2.h>
+#include <linux/nfs3.h>
+#include <stdlib.h>
#define MODULE_MOUNT
#include "automount.h"
#define MODPREFIX "mount(nfs): "
+/*
+ * FIXME: this should really be determined dynamically, though this should be
+ * large enough for any sane use of autofs.
+*/
+#define MAX_REPL_MOUNTS 128
+
+/* Short timeout for RPC pings */
+#define SHORT_SECS 0
+#define SHORT_USEC 100000
+
+/* Long timeout for RPC pings */
+#define LONG_SECS 10
+#define LONG_USEC 0
+
+/* ping status enum */
+enum pingstat
+{
+ NOTPINGED = 0,
+ SHORT_TIMEO = 1,
+ LONG_TIMEO = 2,
+ SUCCESS = 3
+};
+
+/* define a structure for encapsulating an nfs mount */
+struct nfs_mount
+{
+ char *host;
+ char *path;
+ int weight;
+ int local;
+ int bind;
+ int pingstat;
+ double pingtime;
+ struct nfs_mount *next, *prev;
+};
+
int mount_version = AUTOFS_MOUNT_VERSION; /* Required by protocol */
static int udpproto;
@@ -57,16 +96,18 @@
if (port_dis)
port_discard = port_dis->s_port;
else
- port_discard = htons(9); /* 9 is the standard discard
port */
+ port_discard = htons(9); /* 9 is the standard discard
+ port */
- /* Make sure we have the local mount method available */
+ /* Make sure we have the bind mount method available */
if (!mount_bind)
mount_bind = open_mount("bind", MODPREFIX);
return !mount_bind;
}
-int is_local_addr(const char *host, const char *host_addr, int addr_len)
+/* check to see if the server's address is an address on the client itself */
+int is_bind_addr(const char *host, const char *host_addr, int addr_len)
{
struct sockaddr_in src_addr, local_addr;
int src_len = sizeof(src_addr);
@@ -84,7 +125,7 @@
src_addr.sin_port = port_discard;
ret = connect(sock, (struct sockaddr *) &src_addr, src_len);
- if (ret < 0 ) {
+ if (ret < 0) {
error(MODPREFIX "connect failed for %s: %m", host);
close(sock);
return 0;
@@ -102,256 +143,459 @@
ret = memcmp(&src_addr.sin_addr, &local_addr.sin_addr, addr_len);
if (ret)
return 0;
-
+
return 1;
}
+
/*
- * Given a mount string, return (in the same string) the
- * best mount to use based on weight/locality/rpctime
- * - return -1 and what = '\0' on error,
- * 1 and what = local mount path if local bind,
- * else 0 and what = remote mount path
+ * Given a copy of the mount string (p) and a pointer to a nfs_mount struct
+ * (listhead), parse the mount string and populate a linked list with
+ * nfs_mount structs. Return 1 on error and 0 on success.
*/
-int get_best_mount(char *what, const char *original, int longtimeout, int
skiplocal)
+int parse_mount_string(char *p, struct nfs_mount *listhead)
{
- char *p = what;
- char *winner = NULL;
- char *is_replicated = NULL;
- int winner_weight = INT_MAX, local = 0;
- double winner_time = 0;
char *delim;
- int sec = (longtimeout) ? 10 : 0;
- int micros = (longtimeout) ? 0 : 100000;
-
- if (!p) {
- *what = '\0';
- return -1;
- }
+ char *entity[MAX_REPL_MOUNTS];
+ char *currentpath = "";
+ int numwords = 0; /* number of whitespace separated words */
+ struct nfs_mount *currentmount = NULL;
+ struct nfs_mount *lastmount = NULL;
- /*
- * If it's not a replicated server map entry we need
- * to only check for a local mount and return the mount
- * string
- */
- is_replicated = strpbrk(p, "(,");
- if (skiplocal)
- return local;
+ lastmount = listhead;
+ /* break up mountstring into whitespace separated pieces */
while (p && *p) {
- char *next;
- unsigned int ping_stat = 0;
+ p += strspn(p, " \t");
+ delim = strpbrk(p, " \t");
+ if (delim) {
+ *delim = '\0';
+ entity[numwords] = p;
+ p = ++delim;
+ } else {
+ entity[numwords] = p;
+ break;
+ }
+ ++numwords;
+ }
- p += strspn(p, " \t,");
- delim = strpbrk(p, "(, \t:");
- if (!delim)
+ /* now, deal with each whitespace separated chunk in turn */
+ int i;
+ for (i = 0; i <= numwords; ++i) {
+ p = entity[i];
+ debug(MODPREFIX "Working on %s", p);
+
+ /* get the path section out first -- everything to right of the
+ ':' */
+ delim = strpbrk(p, ":");
+ if (delim) {
+ *delim = '\0';
+ currentpath = ++delim;
+ /* if there is no ':', then treat this as a bind mount
+ and move on */
+ } else {
+ currentmount = calloc(1, sizeof(struct nfs_mount));
+ if (!currentmount) {
+ error(MODPREFIX "calloc: %m");
+ return 1;
+ }
+ lastmount->next = currentmount;
+ currentmount->host = NULL;
+ currentmount->bind = 1;
+ currentmount->weight = 0;
+ currentmount->path = p;
+ currentmount->pingstat = NOTPINGED;
+ currentmount->pingtime = 0;
+ currentmount->next = NULL;
+ currentmount->prev = lastmount;
+ lastmount = currentmount;
break;
+ }
- /* Find lowest weight whose server is alive */
- if (*delim == '(') {
- char *weight = delim + 1;
- unsigned int alive;
- *delim = '\0';
+ /* now lets break up the host/weight section */
+ p = entity[i];
+ while (p && *p) {
+ currentmount = calloc(1, sizeof(struct nfs_mount));
+ if (!currentmount) {
+ error(MODPREFIX "calloc: %m");
+ return 1;
+ }
+ lastmount->next = currentmount;
+ currentmount->host = p;
+ currentmount->weight = 0;
+ currentmount->path = currentpath;
+ currentmount->pingstat = NOTPINGED;
+ currentmount->pingtime = 0;
+ currentmount->next = NULL;
+ currentmount->prev = lastmount;
+ currentmount->bind = 0;
+ lastmount = currentmount;
- delim = strchr(weight, ')');
- if (delim) {
- int w;
+ delim = strpbrk(p, ",(");
+ /* if it's a ',' turn it into a \0 */
+ if (delim && *delim == ',') {
*delim = '\0';
- w = atoi(weight);
+ p = ++delim;
- alive = rpc_ping(p, sec, micros);
- if (w < winner_weight && alive) {
- winner_weight = w;
- winner = p;
- }
+ /* if it's a ( then what follows is a weight */
+ } else if (delim && *delim == '(') {
+ *delim = '\0';
+ p = ++delim;
+ delim = strpbrk(p, ")");
+ if (!delim)
+ return 1;
+ *delim = '\0';
+ currentmount->weight = atoi(p);
+ p = ++delim;
+ p += strspn(p, ",");
+
+ /* no more delimiters, so end the loop */
+ } else {
+ break;
}
- delim++;
}
+ }
- if (*delim == ':') {
- *delim = '\0';
- next = strpbrk(delim + 1, " \t");
- } else if (*delim != '\0') {
- *delim = '\0';
- next = delim + 1;
- } else
- break;
+ return 0;
+}
- /* p points to a server, next is our next parse point */
- if (!skiplocal) {
- /* First, check if it's up and if it's localhost */
- struct hostent *he;
- char **haddr;
-
- he = gethostbyname(p);
- if (!he) {
- error(MODPREFIX "host %s: lookup failure", p);
- p = next;
- continue;
- }
+/* remove a member from our doubly linked list */
+void lldrop(struct nfs_mount *ent)
+{
- /* Check each host in round robin list */
- for (haddr = he->h_addr_list; *haddr; haddr++) {
- local = is_local_addr(p, *haddr, he->h_length);
-
- if (local < 0)
- continue;
-
- if (local) {
- winner = p;
- break;
- }
- }
-
- if (local < 0) {
- local = 0;
- p = next;
- continue;
- }
+ if (ent->next) {
+ (ent->next)->prev = ent->prev;
+ }
- if (local)
- break;
- }
+ if (ent->prev) {
+ (ent->prev)->next = ent->next;
+ }
- /*
- * If it's not local and it's a replicated server map entry
- * is it alive
- */
- if (!local && is_replicated && !(ping_stat = rpc_ping(p, sec,
micros))) {
- p = next;
+ free(ent);
+}
+
+/* swap 2 adjacent list entries. Presumes that they are adjacent and that
+ 1 comes before 2. Also presumes both are valid entries. */
+void llswap(struct nfs_mount *ent1, struct nfs_mount *ent2)
+{
+ (ent1->prev)->next = ent2;
+ ent1->next = ent2->next;
+ if (ent2->next) {
+ (ent2->next)->prev = ent1;
+ }
+ ent2->prev = ent1->prev;
+ ent1->prev = ent2;
+ ent2->next = ent1;
+}
+
+/* free an entire (NULL terminated) linked list */
+void llcleanup(struct nfs_mount *currentmount)
+{
+ struct nfs_mount *nextmount;
+ while (currentmount) {
+ nextmount = currentmount->next;
+ free(currentmount);
+ currentmount = nextmount;
+ }
+}
+
+/*
+ * sort a linked list of mounts, based on "bindness", weight, and RPC ping
+ */
+void
+sort_mounts(struct nfs_mount *nfs_mount_head, unsigned int vers,
+ unsigned int proto)
+{
+ struct hostent *he;
+ char **haddr;
+ int bind, status;
+ struct nfs_mount *currentmount = nfs_mount_head->next;
+ struct nfs_mount *nextmount;
+
+ /* check for bind mount first */
+ while (currentmount) {
+ if (currentmount->bind)
+ continue;
+
+ he = gethostbyname(currentmount->host);
+ if (!he) {
+ error(MODPREFIX
+ "host %s: lookup failure, removing from list",
+ currentmount->host);
+ nextmount = currentmount->next;
+ lldrop(currentmount);
+ currentmount = nextmount;
continue;
}
- /* compare RPC times if there are no weighted hosts */
- if (winner_weight == INT_MAX) {
- int status;
- double resp_time;
- unsigned int vers = NFS2_VERSION;
- unsigned int proto = RPC_PING_UDP;
-
- if (ping_stat) {
- vers = ping_stat & 0x00ff;
- proto = ping_stat & 0xff00;
- }
-
- status = rpc_time(p, vers, proto, sec, micros,
&resp_time);
- /* did we time the first winner? */
- if (winner_time == 0) {
- if (status) {
- winner = p;
- winner_time = resp_time;
- } else
- winner_time = 6;
- } else {
- if ((status) && (resp_time < winner_time)) {
- winner = p;
- winner_time = resp_time;
- }
+ /* Check each host in round robin list */
+ for (haddr = he->h_addr_list; *haddr; haddr++) {
+ bind = is_bind_addr(currentmount->host, *haddr,
+ he->h_length);
+ if (bind < 0)
+ continue;
+
+ if (bind) {
+ currentmount->bind = 1;
+ break;
}
}
- p = next;
+ currentmount = currentmount->next;
}
- debug(MODPREFIX "winner = %s local = %d", winner, local);
-
- /*
- * We didn't find a weighted winner or local and it's a replicated
- * server map entry
- */
- if (!local && is_replicated && winner_weight == INT_MAX) {
- /* We had more than one contender and none responded in time */
- if (winner_time != 0 && winner_time > 5) {
- /* We've already tried a longer timeout */
- if (longtimeout) {
- /* SOL: Just pick the first one */
- winner = what;
+ /* bubblesort time! */
+ int changed = 1;
+ debug(MODPREFIX "Starting bubblesort");
+ while (changed) {
+ changed = 0;
+ currentmount = nfs_mount_head->next;
+ while (currentmount->next) {
+ nextmount = currentmount->next;
+ debug(MODPREFIX
+ "currentmount = %s:%s, nextmount = %s:%s",
+ currentmount->host, currentmount->path,
+ nextmount->host, nextmount->path);
+
+ /* bind mount check */
+ if (currentmount->bind < nextmount->bind) {
+ debug(MODPREFIX
+ "bind swap currentmount=%d nextmount=%d",
+ currentmount->bind, nextmount->bind);
+ llswap(currentmount, nextmount);
+ changed = 1;
+ currentmount = nextmount;
+ continue;
+ } else if (currentmount->bind > nextmount->bind) {
+ currentmount = nextmount;
+ continue;
}
- /* Reset string and try again */
- else {
- strcpy(what, original);
- debug(MODPREFIX
- "all hosts rpc timed out for '%s', "
- "retrying with longer timeout",
- original);
+ /* weight check */
+ if (currentmount->weight > nextmount->weight) {
+ debug(MODPREFIX
+ "weight swap currentmount=%d
nextmount=%d",
+ currentmount->weight, nextmount->weight);
+ llswap(currentmount, nextmount);
+ changed = 1;
+ currentmount = nextmount;
+ continue;
+ } else if (currentmount->weight < nextmount->weight) {
+ currentmount = nextmount;
+ continue;
+ }
- return get_best_mount(what, original, 1, 1);
+ /* ping check -- first check if we have a ping time for
+ each */
+ if (currentmount->pingstat == NOTPINGED) {
+ status = rpc_time(currentmount->host,
+ vers, proto,
+ SHORT_SECS, SHORT_USEC,
+ ¤tmount->pingtime);
+ if (!status) {
+ debug(MODPREFIX
+ "short timeout ping failure for
%s",
+ currentmount->host);
+ currentmount->pingstat = SHORT_TIMEO;
+ } else {
+ debug(MODPREFIX
+ "short pingtime for %s == %g",
+ currentmount->host,
+ currentmount->pingtime);
+ currentmount->pingstat = SUCCESS;
+ }
}
- }
- }
- /* No winner found so bail */
- if (!winner) {
- *what = '\0';
- return 0;
- }
+ if (nextmount->pingstat == NOTPINGED) {
+ status = rpc_time(nextmount->host, vers,
+ proto, SHORT_SECS,
+ SHORT_USEC,
+ &nextmount->pingtime);
+ if (!status) {
+ debug(MODPREFIX
+ "short timeout ping failure for
%s",
+ nextmount->host);
+ nextmount->pingstat = SHORT_TIMEO;
+ } else {
+ debug(MODPREFIX
+ "short pingtime for %s == %g",
+ nextmount->host,
+ nextmount->pingtime);
+ nextmount->pingstat = SUCCESS;
+ }
+ }
- /*
- * We now have our winner, copy it to the front of the string,
- * followed by the next :string<delim>
- */
-
- /* if it's local */
- if (!local)
- strcpy(what, winner);
- else
- what[0] = '\0';
+ /* hosts that don't respond get moved to the end of the
+ list, rather than removed */
+ if (currentmount->pingstat == SUCCESS
+ && nextmount->pingstat == SUCCESS) {
+ if (currentmount->pingtime >
+ nextmount->pingtime) {
+ debug(MODPREFIX
+ "pingtime swap: %s == %g, %s ==
%g",
+ currentmount->host,
+ currentmount->pingtime,
+ nextmount->host,
+ nextmount->pingtime);
+ llswap(currentmount, nextmount);
+ changed = 1;
+ }
+ currentmount = nextmount;
+ continue;
+ } else if (nextmount->pingstat == SUCCESS) {
+ debug(MODPREFIX
+ "pingstat swap: %s == %d, %s == %d",
+ currentmount->host,
+ currentmount->pingstat,
+ nextmount->host, nextmount->pingstat);
+ llswap(currentmount, nextmount);
+ changed = 1;
+ currentmount = nextmount;
+ continue;
+ } else if (currentmount->pingstat == SUCCESS) {
+ currentmount = nextmount;
+ continue;
+ }
- /* We know we're only reading from p, so discard const */
- p = (char *) original + (winner - what);
- delim = what + strlen(what);
+ /* we fall thru to here if neither host has a
+ successful ping */
+ if (currentmount->pingstat == SHORT_TIMEO) {
+ status = rpc_time(currentmount->host,
+ vers, proto, LONG_SECS,
+ LONG_USEC,
+ ¤tmount->pingtime);
+ if (!status) {
+ debug(MODPREFIX
+ "long timeout ping failure for
%s",
+ currentmount->host);
+ currentmount->pingstat = LONG_TIMEO;
+ } else {
+ debug(MODPREFIX
+ "long pingtime for %s == %g",
+ currentmount->host,
+ currentmount->pingtime);
+ currentmount->pingstat = SUCCESS;
+ }
+ }
- /* Find the colon (in the original string) */
- while (*p && *p != ':')
- p++;
+ if (nextmount->pingstat == SHORT_TIMEO) {
+ status = rpc_time(nextmount->host, vers,
+ proto, LONG_SECS,
+ LONG_USEC,
+ &nextmount->pingtime);
+ if (!status) {
+ debug(MODPREFIX
+ "long timeout ping failure for
%s",
+ nextmount->host);
+ nextmount->pingstat = LONG_TIMEO;
+ } else {
+ debug(MODPREFIX
+ "long pingtime for %s == %g",
+ nextmount->host,
+ nextmount->pingtime);
+ nextmount->pingstat = SUCCESS;
+ }
+ }
- /* skip : for local paths */
- if (local)
- p++;
+ if (currentmount->pingstat == SUCCESS
+ && nextmount->pingstat == SUCCESS) {
+ if (currentmount->pingtime >
+ nextmount->pingtime) {
+ debug(MODPREFIX
+ "pingtime swap: %s == %g, %s ==
%g",
+ currentmount->host,
+ currentmount->pingtime,
+ nextmount->host,
+ nextmount->pingtime);
+ llswap(currentmount, nextmount);
+ changed = 1;
+ }
+ } else if (nextmount->pingstat == SUCCESS) {
+ debug(MODPREFIX
+ "pingstat swap: %s == %g, %s == %g",
+ currentmount->host,
+ currentmount->pingtime,
+ nextmount->host, nextmount->pingtime);
+ llswap(currentmount, nextmount);
+ changed = 1;
+ }
- /* copy to next space or end of string */
- while (*p && *p != ' ' && *p != '\t')
- *delim++ = *p++;
+ currentmount = nextmount;
- *delim = '\0';
+ }
+ }
+ debug(MODPREFIX "Ending bubblesort");
- return local;
}
-int mount_mount(const char *root, const char *name, int name_len,
- const char *what, const char *fstype, const char *options,
- void *context)
+/* the main routine -- from the info given, pick a filesystem and mount it */
+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, *fullpath;
- char *whatstr;
+ char *fullpath = NULL;
+ char *whatstr = NULL;
+ char *mntstrcopy = NULL;
char *nfsoptions = NULL;
- int local, err;
int nosymlink = 0;
- int ro = 0; /* Set if mount bind should be read-only */
+ int error = 0;
+ int ro = 0;
+ struct nfs_mount *nfs_mount_head = NULL;
+ unsigned int vers = NFS2_VERSION;
+ unsigned int proto = RPC_PING_UDP;
- debug(MODPREFIX "root=%s name=%s what=%s, fstype=%s, options=%s",
+ debug(MODPREFIX " root=%s name=%s what=%s, fstype=%s, options=%s",
root, name, what, fstype, options);
- whatstr = alloca(strlen(what) + 1);
+ /* whatstr -- this is what we pass to spawnl or mount_bind later */
+ whatstr = calloc(strlen(what) + 1, sizeof(char));
if (!whatstr) {
- error(MODPREFIX "alloca: %m");
- return 1;
+ error(MODPREFIX "calloc: %m");
+ error = 1;
+ goto cleanup;
+ }
+
+ /* mount string for parsing, we chop this up in the parse routine */
+ mntstrcopy = calloc(strlen(what) + 1, sizeof(char));
+ if (!mntstrcopy) {
+ error(MODPREFIX "calloc: %m");
+ error = 1;
+ goto cleanup;
}
- strcpy(whatstr, what);
+ strncpy(mntstrcopy, what, strlen(what) + 1);
- /* Extract "nosymlink" pseudo-option which stops local filesystems
- from being symlinked */
+ /* full path of mount point */
+ fullpath = calloc(strlen(root) + name_len + 2, sizeof(char));
+ if (!fullpath) {
+ error(MODPREFIX "calloc: %m");
+ error = 1;
+ goto cleanup;
+ }
+
+ /* declare a struct to be first linked list entry, this won't hold a
+ real entry but should never change */
+ nfs_mount_head = calloc(1, sizeof(struct nfs_mount));
+ if (!nfs_mount_head) {
+ error(MODPREFIX "calloc: %m");
+ error = 1;
+ goto cleanup;
+ }
+
+ /* Extract "nosymlink" pseudo-option which stops local filesystems from
+ being symlinked, and check for tcp and nfsvers= options */
if (options) {
const char *comma;
char *nfsp;
int len = strlen(options) + 1;
- nfsp = nfsoptions = alloca(len + 1);
- if (!nfsoptions)
- return 1;
-
- memset(nfsoptions, '\0', len + 1);
+ /* an nfsoptions string that we'll use later */
+ nfsp = nfsoptions = calloc(len + 1, sizeof(char));
+ if (!nfsoptions) {
+ error(MODPREFIX "calloc: %m");
+ error = 1;
+ goto cleanup;
+ }
for (comma = options; *comma != '\0';) {
const char *cp;
@@ -374,114 +618,176 @@
end--;
#if 0
- debug(MODPREFIX "*comma=%x %c comma=%p %s cp=%p %s "
+ debug(MODPREFIX
+ "*comma=%x %c comma=%p %s cp=%p %s "
"nfsoptions=%p nfsp=%p end=%p used=%d len=%d\n",
*comma, *comma, comma, comma, cp, cp,
nfsoptions, nfsp, nfsoptions + len,
nfsp - nfsoptions, len);
#endif
- if (strncmp("nosymlink", cp, end - cp + 1) == 0)
+ /* if it's nosymlink, set flag and skip copying it to
+ nfsoptions if the flag declares tcp or an nfs
+ version set ping proto and version appropriately.
+ Also look for 'ro' option so we can pass this to
+ mount_bind if need be. */
+ if (strncmp("nosymlink", cp, end - cp + 1) == 0) {
nosymlink = 1;
- else {
- /* Check for options that also make sense
- with bind mounts */
- if (strncmp("ro", cp, end - cp + 1) == 0)
- ro = 1;
- /* and jump over trailing white space */
- memcpy(nfsp, cp, comma - cp + 1);
- nfsp += comma - cp + 1;
+ continue;
+ } else if (strncmp("tcp", cp, end - cp + 1) == 0) {
+ proto = RPC_PING_TCP;
+ } else if (strncmp("nfsvers=3", cp, end - cp + 1) == 0)
{
+ vers = NFS3_VERSION;
+ } else if (strncmp("ro", cp, end - cp + 1) == 0) {
+ ro = 1;
}
- }
- debug(MODPREFIX "nfs options=\"%s\", nosymlink=%d, ro=%d",
- nfsoptions, nosymlink, ro);
- }
-
- local = 0;
-
- colon = strchr(whatstr, ':');
- if (!colon) {
- /* No colon, take this as a bind (local) entry */
- local = 1;
- } else if (!nosymlink) {
- local = get_best_mount(whatstr, what, 0, 0);
- if (!*whatstr) {
- warn(MODPREFIX "no host elected");
- return 1;
+ /* and jump over trailing white space */
+ memcpy(nfsp, cp, comma - cp + 1);
+ nfsp += comma - cp + 1;
}
- debug(MODPREFIX "from %s elected %s", what, whatstr);
- }
- fullpath = alloca(strlen(root) + name_len + 2);
- if (!fullpath) {
- error(MODPREFIX "alloca: %m");
- return 1;
+ debug(MODPREFIX
+ "nfs options=\"%s\", nosymlink=%d, nfsvers=%d, proto=%d",
+ nfsoptions, nosymlink, vers, proto);
}
+ /* get full path of mountpoint */
if (name_len)
sprintf(fullpath, "%s/%s", root, name);
else
sprintf(fullpath, "%s", root);
- if (local) {
- /* Local host -- do a "bind" */
+ /* parse the mount string and get the nfs_mount struct linked list */
+ error = parse_mount_string(mntstrcopy, nfs_mount_head);
+ if (error)
+ goto cleanup;
+
+ /* sort the linked list */
+ sort_mounts(nfs_mount_head, vers, proto);
+
+ /* now try to mount them in turn */
+ struct nfs_mount *currentmount = nfs_mount_head->next;
+ int status = 0;
+ int dir_created = 0;
+ int mounted = is_mounted(_PATH_MOUNTED, fullpath);
+
+ /* log final sorted list for debugging */
+ if (do_debug) {
+ int i = 0;
+ while (currentmount) {
+ debug(MODPREFIX "%d: host=%s, path=%s, weight=%d",
+ i, currentmount->host, currentmount->path,
+ currentmount->weight);
+ currentmount = currentmount->next;
+ ++i;
+ }
+ currentmount = nfs_mount_head->next;
+ }
+
+ /* error out with a debug message if it's already mounted */
+ if (mounted)
+ debug(MODPREFIX "BUG: %s is already mounted!",
+ fullpath) error = 1;
+ while (currentmount && !mounted) {
+ error = 0;
+ debug(MODPREFIX
+ "attempting mount: host=%s path=%s weight=%d",
+ currentmount->host, currentmount->path,
+ currentmount->weight);
+
+ /* see if this qualifies for a bind mount -- currentmount->bind
+ is set or currentmount->host is NULL */
+ if ((currentmount->bind && !nosymlink) || !currentmount->host) {
+ debug(MODPREFIX "%s is local, doing bind", name);
+
+ /* pass the ro flag if it was specified */
+ const char *bind_options = ro ? "ro" : "";
+
+ error = mount_bind->mount_mount(root, name,
+ name_len,
+ currentmount->path,
+ "bind",
+ bind_options,
+ mount_bind->context);
- const char *bind_options = ro ? "ro" : "";
-
- debug(MODPREFIX "%s is local, doing bind", name);
-
- return mount_bind->mount_mount(root, name, name_len,
- whatstr, "bind", bind_options,
- mount_bind->context);
- } else {
- /* Not a local host - do an NFS mount */
- int status, existed = 1;
+ } else {
+ /* otherwise this is an NFS mount */
+ status = mkdir_path(fullpath, 0555);
+ if (status && errno != EEXIST) {
+ error(MODPREFIX
+ "mkdir_path %s failed: %m", fullpath);
+ error = 2;
+ } else if (status) {
+ error = 0;
+ } else {
+ dir_created = 1;
+ }
- debug(MODPREFIX "calling mkdir_path %s", fullpath);
+ /* attempt to mount if there's no error */
+ if (!error) {
+ sprintf(whatstr, "%s:%s",
+ currentmount->host, currentmount->path);
+ if (nfsoptions && *nfsoptions) {
+ debug(MODPREFIX
+ "calling mount -t nfs "
+ SLOPPY " -o %s %s %s",
+ nfsoptions, whatstr, fullpath);
+
+ error = spawnll(LOG_NOTICE,
+ PATH_MOUNT,
+ PATH_MOUNT,
+ "-t", "nfs",
+ SLOPPYOPT "-o",
+ nfsoptions,
+ whatstr,
+ fullpath, NULL);
+ } else {
+ debug(MODPREFIX
+ "calling mount -t nfs %s %s",
+ whatstr, fullpath);
+ error = spawnll(LOG_NOTICE,
+ PATH_MOUNT,
+ PATH_MOUNT,
+ "-t", "nfs",
+ whatstr,
+ fullpath, NULL);
+ }
+ }
+ }
- status = mkdir_path(fullpath, 0555);
- if (status && errno != EEXIST) {
- error(MODPREFIX "mkdir_path %s failed: %m", fullpath);
- return 1;
+ if (error == 2) {
+ debug(MODPREFIX
+ "unable to create mountpoint %s. Not attempting
any further mounts!",
+ fullpath);
+ error = 1;
+ break;
}
- if (!status)
- existed = 0;
+ currentmount = currentmount->next;
+ mounted = is_mounted(_PATH_MOUNTED, fullpath);
+ }
- if (is_mounted(_PATH_MOUNTED, fullpath)) {
- error(MODPREFIX
- "warning: %s is already mounted", fullpath);
- return 0;
+ /* cleanup time -- remove directory if there was an error and we
+ created it */
+ if (error) {
+ debug(MODPREFIX "mount of %s on %s failed!", whatstr, fullpath);
+ if (dir_created || (!ap.ghost && name_len)) {
+ rmdir_path(name);
}
+ } else {
+ debug(MODPREFIX "mounted %s on %s", whatstr, fullpath);
+ }
- if (nfsoptions && *nfsoptions) {
- debug(MODPREFIX "calling mount -t nfs " SLOPPY
- " -o %s %s %s", nfsoptions, whatstr, fullpath);
+ /* clean up any memory we allocated */
+ cleanup:
+ free(whatstr);
+ free(mntstrcopy);
+ free(fullpath);
+ free(nfsoptions);
+ llcleanup(nfs_mount_head);
- err = spawnll(LOG_NOTICE,
- PATH_MOUNT, PATH_MOUNT, "-t",
- "nfs", SLOPPYOPT "-o", nfsoptions,
- whatstr, fullpath, NULL);
- } else {
- debug(MODPREFIX "calling mount -t nfs %s %s",
- whatstr, fullpath);
- err = spawnll(LOG_NOTICE,
- PATH_MOUNT, PATH_MOUNT, "-t",
- "nfs", whatstr, fullpath, NULL);
- }
+ return error;
- if (err) {
- if ((!ap.ghost && name_len) || !existed)
- rmdir_path(name);
-
- error(MODPREFIX "nfs: mount failure %s on %s",
- whatstr, fullpath);
- return 1;
- } else {
- debug(MODPREFIX "mounted %s on %s", whatstr, fullpath);
- return 0;
- }
- }
}
int mount_done(void *context)
_______________________________________________
autofs mailing list
[email protected]
http://linux.kernel.org/mailman/listinfo/autofs