Blame modules/mount_nfs.c

Packit 8480eb
/* ----------------------------------------------------------------------- *
Packit 8480eb
 *   
Packit 8480eb
 * mount_nfs.c - Module for Linux automountd to mount an NFS filesystem,
Packit 8480eb
 *               with fallback to symlinking if the path is local
Packit 8480eb
 *
Packit 8480eb
 *   Copyright 1997 Transmeta Corporation - All Rights Reserved
Packit 8480eb
 *   Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org>
Packit 8480eb
 *
Packit 8480eb
 *   This program is free software; you can redistribute it and/or modify
Packit 8480eb
 *   it under the terms of the GNU General Public License as published by
Packit 8480eb
 *   the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
Packit 8480eb
 *   USA; either version 2 of the License, or (at your option) any later
Packit 8480eb
 *   version; incorporated herein by reference.
Packit 8480eb
 *
Packit 8480eb
 * ----------------------------------------------------------------------- */
Packit 8480eb
Packit 8480eb
#include <stdio.h>
Packit 8480eb
#include <malloc.h>
Packit 8480eb
#include <netdb.h>
Packit 8480eb
#include <stdlib.h>
Packit 8480eb
#include <string.h>
Packit 8480eb
#include <sys/param.h>
Packit 8480eb
#include <sys/socket.h>
Packit 8480eb
#include <sys/types.h>
Packit 8480eb
#include <sys/stat.h>
Packit 8480eb
#include <sys/vfs.h>
Packit 8480eb
#include <netinet/in.h>
Packit 8480eb
#include <linux/nfs.h>
Packit 8480eb
#include <linux/nfs2.h>
Packit 8480eb
#include <ctype.h>
Packit 8480eb
Packit 8480eb
#define MODULE_MOUNT
Packit 8480eb
#include "automount.h"
Packit 8480eb
#include "replicated.h"
Packit 8480eb
Packit 8480eb
#define MODPREFIX "mount(nfs): "
Packit 8480eb
Packit 8480eb
int mount_version = AUTOFS_MOUNT_VERSION;	/* Required by protocol */
Packit 8480eb
Packit 8480eb
static struct mount_mod *mount_bind = NULL;
Packit 8480eb
static int init_ctr = 0;
Packit 8480eb
Packit 8480eb
int mount_init(void **context)
Packit 8480eb
{
Packit 8480eb
	/* Make sure we have the local mount method available */
Packit 8480eb
	if (!mount_bind) {
Packit 8480eb
		if ((mount_bind = open_mount("bind", MODPREFIX)))
Packit 8480eb
			init_ctr++;
Packit 8480eb
	} else
Packit 8480eb
		init_ctr++;
Packit 8480eb
Packit 8480eb
	seed_random();
Packit 8480eb
Packit 8480eb
	return !mount_bind;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
int mount_reinit(void **context)
Packit 8480eb
{
Packit 8480eb
	return 0;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
int mount_mount(struct autofs_point *ap, const char *root, const char *name, int name_len,
Packit 8480eb
		const char *what, const char *fstype, const char *options,
Packit 8480eb
		void *context)
Packit 8480eb
{
Packit 8480eb
	char fullpath[PATH_MAX];
Packit 8480eb
	char buf[MAX_ERR_BUF];
Packit 8480eb
	struct host *this, *hosts = NULL;
Packit 8480eb
	unsigned int mount_default_proto, vers;
Packit 8480eb
	char *nfsoptions = NULL;
Packit 8480eb
	unsigned int flags = ap->flags &
Packit 8480eb
			(MOUNT_FLAG_RANDOM_SELECT | MOUNT_FLAG_USE_WEIGHT_ONLY);
Packit 8480eb
	int symlink = (*name != '/' && (ap->flags & MOUNT_FLAG_SYMLINK));
Packit 8480eb
	int nobind = ap->flags & MOUNT_FLAG_NOBIND;
Packit 8480eb
	int len, status, err, existed = 1;
Packit 8480eb
	int nosymlink = 0;
Packit 8480eb
	int port = -1;
Packit 8480eb
	int ro = 0;            /* Set if mount bind should be read-only */
Packit 8480eb
	int rdma = 0;
Packit Bot 3cd872
	void (*mountlog)(unsigned int, const char*, ...) = &log_debug;
Packit 8480eb
Packit 8480eb
	if (ap->flags & MOUNT_FLAG_REMOUNT)
Packit 8480eb
		return 0;
Packit 8480eb
Packit Bot 3cd872
	if (defaults_get_mount_verbose())
Packit Bot 3cd872
		mountlog = &log_info;
Packit Bot 3cd872
Packit Bot 3cd872
	mountlog(ap->logopt,
Packit Bot 3cd872
		 MODPREFIX "root=%s name=%s what=%s, fstype=%s, options=%s",
Packit Bot 3cd872
		 root, name, what, fstype, options);
Packit 8480eb
Packit 8480eb
	mount_default_proto = defaults_get_mount_nfs_default_proto();
Packit Bot 36e2db
	vers = NFS_VERS_DEFAULT | NFS_PROTO_DEFAULT;
Packit 8480eb
	if (strcmp(fstype, "nfs4") == 0)
Packit Bot 36e2db
		vers = NFS4_VERS_DEFAULT | TCP_SUPPORTED;
Packit 8480eb
	else if (mount_default_proto == 4)
Packit Bot 36e2db
		vers = vers | NFS4_VERS_DEFAULT;
Packit 8480eb
Packit 8480eb
	/* Extract "nosymlink" pseudo-option which stops local filesystems
Packit 8480eb
	 * from being symlinked.
Packit 8480eb
	 *
Packit 8480eb
	 * "nosymlink" is not used anymore. It is left for compatibility
Packit 8480eb
	 * only (so we don't choke on it).
Packit 8480eb
	 */
Packit 8480eb
	if (options) {
Packit 8480eb
		const char *comma;
Packit 8480eb
		char *nfsp;
Packit 8480eb
		int o_len = strlen(options) + 1;
Packit 8480eb
Packit 8480eb
		nfsp = nfsoptions = alloca(o_len + 1);
Packit 8480eb
		if (!nfsoptions)
Packit 8480eb
			return 1;
Packit 8480eb
Packit 8480eb
		memset(nfsoptions, '\0', o_len + 1);
Packit 8480eb
Packit 8480eb
		for (comma = options; *comma != '\0';) {
Packit 8480eb
			const char *cp;
Packit 8480eb
			const char *end;
Packit 8480eb
Packit 8480eb
			while (*comma == ',')
Packit 8480eb
				comma++;
Packit 8480eb
Packit 8480eb
			/* Skip leading white space */
Packit 8480eb
			while (*comma == ' ' || *comma == '\t')
Packit 8480eb
				comma++;
Packit 8480eb
Packit 8480eb
			cp = comma;
Packit 8480eb
			while (*comma != '\0' && *comma != ',')
Packit 8480eb
				comma++;
Packit 8480eb
Packit 8480eb
			/* Skip trailing white space */
Packit 8480eb
			end = comma - 1;
Packit 8480eb
			while (*comma == ' ' || *comma == '\t')
Packit 8480eb
				end--;
Packit 8480eb
Packit 8480eb
			o_len = end - cp + 1;
Packit 8480eb
Packit 8480eb
			if (_strncmp("proto=rdma", cp, o_len) == 0 ||
Packit 8480eb
				   _strncmp("rdma", cp, o_len) == 0)
Packit 8480eb
				rdma = 1;
Packit 8480eb
Packit 8480eb
			if (_strncmp("nosymlink", cp, o_len) == 0) {
Packit 8480eb
				warn(ap->logopt, MODPREFIX
Packit 8480eb
				     "the \"nosymlink\" option is depricated "
Packit 8480eb
				     "and will soon be removed, "
Packit 8480eb
				     "use the \"nobind\" option instead");
Packit 8480eb
				nosymlink = 1;
Packit 8480eb
			} else if (_strncmp("symlink", cp, o_len) == 0) {
Packit 8480eb
				if (*name != '/')
Packit 8480eb
					symlink = 1;
Packit 8480eb
			} else if (_strncmp("nobind", cp, o_len) == 0) {
Packit 8480eb
				nobind = 1;
Packit 8480eb
			} else if (_strncmp("no-use-weight-only", cp, o_len) == 0) {
Packit 8480eb
				flags &= ~MOUNT_FLAG_USE_WEIGHT_ONLY;
Packit 8480eb
			} else if (_strncmp("use-weight-only", cp, o_len) == 0) {
Packit 8480eb
				flags |= MOUNT_FLAG_USE_WEIGHT_ONLY;
Packit 8480eb
			} else {
Packit 8480eb
				/* Is any version of NFSv4 in the options */
Packit 8480eb
				if (_strncmp("vers=4", cp, 6) == 0 ||
Packit 8480eb
				    _strncmp("nfsvers=4", cp, 9) == 0)
Packit 8480eb
					vers = NFS4_VERS_MASK | TCP_SUPPORTED;
Packit 8480eb
				else if (_strncmp("vers=3", cp, o_len) == 0 ||
Packit 8480eb
					 _strncmp("nfsvers=3", cp, o_len) == 0) {
Packit 8480eb
					vers &= ~(NFS4_VERS_MASK | NFS_VERS_MASK);
Packit 8480eb
					vers |= NFS3_REQUESTED;
Packit 8480eb
				} else if (_strncmp("vers=2", cp, o_len) == 0 ||
Packit 8480eb
					 _strncmp("nfsvers=2", cp, o_len) == 0) {
Packit 8480eb
					vers &= ~(NFS4_VERS_MASK | NFS_VERS_MASK);
Packit 8480eb
					vers |= NFS2_REQUESTED;
Packit 8480eb
				} else if (strstr(cp, "port=") == cp &&
Packit 8480eb
					 o_len - 5 < 25) {
Packit 8480eb
					char optport[25];
Packit 8480eb
Packit 8480eb
					strncpy(optport, cp + 5, o_len - 5);
Packit 8480eb
					optport[o_len - 5] = '\0';
Packit 8480eb
					port = atoi(optport);
Packit 8480eb
					if (port < 0)
Packit 8480eb
						port = 0;
Packit 8480eb
				} else if (_strncmp("proto=udp", cp, o_len) == 0 ||
Packit 8480eb
					   _strncmp("udp", cp, o_len) == 0) {
Packit 8480eb
					vers &= ~TCP_SUPPORTED;
Packit 8480eb
				} else if (_strncmp("proto=udp6", cp, o_len) == 0 ||
Packit 8480eb
					   _strncmp("udp6", cp, o_len) == 0) {
Packit 8480eb
					vers &= ~TCP_SUPPORTED;
Packit 8480eb
					vers |= UDP6_REQUESTED;
Packit 8480eb
				} else if (_strncmp("proto=tcp", cp, o_len) == 0 ||
Packit 8480eb
					   _strncmp("tcp", cp, o_len) == 0) {
Packit 8480eb
					vers &= ~UDP_SUPPORTED;
Packit 8480eb
				} else if (_strncmp("proto=tcp6", cp, o_len) == 0 ||
Packit 8480eb
					   _strncmp("tcp6", cp, o_len) == 0) {
Packit 8480eb
					vers &= ~UDP_SUPPORTED;
Packit 8480eb
					vers |= TCP6_REQUESTED;
Packit 8480eb
				}
Packit 8480eb
				/* Check for options that also make sense
Packit 8480eb
				   with bind mounts */
Packit 8480eb
				else if (_strncmp("ro", cp, o_len) == 0)
Packit 8480eb
					ro = 1;
Packit 8480eb
				else if (_strncmp("rw", cp, o_len) == 0)
Packit 8480eb
					ro = 0;
Packit 8480eb
				/* and jump over trailing white space */
Packit 8480eb
				memcpy(nfsp, cp, comma - cp + 1);
Packit 8480eb
				nfsp += comma - cp + 1;
Packit 8480eb
			}
Packit 8480eb
		}
Packit 8480eb
Packit 8480eb
		/* In case both tcp and udp options were given */
Packit 8480eb
		if ((vers & NFS_PROTO_MASK) == 0)
Packit 8480eb
			vers |= NFS_PROTO_MASK;
Packit 8480eb
Packit Bot 3cd872
		mountlog(ap->logopt, MODPREFIX
Packit Bot 3cd872
			 "nfs options=\"%s\", nobind=%d, nosymlink=%d, ro=%d",
Packit Bot 3cd872
			 nfsoptions, nobind, nosymlink, ro);
Packit Service 718b04
	}
Packit Service 718b04
Packit Bot 67b1d0
	/* Construct mount point directory */
Packit Bot e82254
	len = mount_fullpath(fullpath, PATH_MAX, root, 0, name);
Packit Bot 67b1d0
	if (!len) {
Packit Bot 67b1d0
		error(ap->logopt,
Packit Bot 67b1d0
		      MODPREFIX "mount point path too long");
Packit Bot 67b1d0
		return 1;
Packit Bot 67b1d0
	}
Packit Bot 67b1d0
Packit 8480eb
	if (!parse_location(ap->logopt, &hosts, what, flags)) {
Packit 8480eb
		info(ap->logopt, MODPREFIX "no hosts available");
Packit 8480eb
		return 1;
Packit 8480eb
	}
Packit 8480eb
	/*
Packit 8480eb
	 * We can't probe protocol rdma so leave it to mount.nfs(8)
Packit 8480eb
	 * and and suffer the delay if a server isn't available.
Packit 8480eb
	 */
Packit 8480eb
	if (rdma)
Packit 8480eb
		goto dont_probe;
Packit 8480eb
Packit 8480eb
	/*
Packit 8480eb
	 * If this is a singleton mount, and NFSv4 only hasn't been asked
Packit 8480eb
	 * for, and the default NFS protocol is set to v4 in the autofs
Packit 8480eb
	 * configuration only probe NFSv4 and let mount.nfs(8) do fallback
Packit 8480eb
	 * to NFSv3 (if it can). If the NFSv4 probe fails then probe as
Packit 8480eb
	 * normal.
Packit 8480eb
	 *
Packit 8480eb
	 * Note: some NFS servers consider it a protocol violation to use
Packit 8480eb
	 * NFSv4 over UDP so if it has been requested in the mount options
Packit 8480eb
	 * we can't use this at all.
Packit 8480eb
	 */
Packit 8480eb
	if ((hosts && !hosts->next) &&
Packit 8480eb
	    mount_default_proto == 4 &&
Packit 8480eb
	    (vers & NFS_VERS_MASK) != 0 &&
Packit 8480eb
	    (vers & NFS4_VERS_MASK) != 0 &&
Packit 8480eb
	    !(vers & UDP6_REQUESTED)) {
Packit 8480eb
		unsigned int v4_probe_ok = 0;
Packit Bot 2a326b
		struct host *tmp = new_host(hosts->name, 0,
Packit 8480eb
					    hosts->addr, hosts->addr_len,
Packit 8480eb
					    hosts->proximity,
Packit 8480eb
					    hosts->weight, hosts->options);
Packit 8480eb
		if (tmp) {
Packit 8480eb
			tmp->rr = hosts->rr;
Packit 8480eb
			prune_host_list(ap->logopt, &tmp,
Packit 8480eb
					NFS4_VERS_MASK|TCP_SUPPORTED, port);
Packit 8480eb
			/* If probe succeeds just try the mount with host in hosts */
Packit 8480eb
			if (tmp) {
Packit 8480eb
				v4_probe_ok = 1;
Packit 8480eb
				free_host_list(&tmp);
Packit 8480eb
			}
Packit 8480eb
		}
Packit 8480eb
		if (!v4_probe_ok)
Packit 8480eb
			prune_host_list(ap->logopt, &hosts, vers, port);
Packit 8480eb
	} else {
Packit 8480eb
		prune_host_list(ap->logopt, &hosts, vers, port);
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
dont_probe:
Packit 8480eb
	if (!hosts) {
Packit 8480eb
		info(ap->logopt, MODPREFIX "no hosts available");
Packit 8480eb
		return 1;
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	debug(ap->logopt, MODPREFIX "calling mkdir_path %s", fullpath);
Packit 8480eb
Packit Bot bc4df2
	status = mkdir_path(fullpath, mp_mode);
Packit 8480eb
	if (status && errno != EEXIST) {
Packit 8480eb
		char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
Packit 8480eb
		error(ap->logopt,
Packit 8480eb
		      MODPREFIX "mkdir_path %s failed: %s", fullpath, estr);
Packit 8480eb
		free_host_list(&hosts);
Packit 8480eb
		return 1;
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
	if (!status)
Packit 8480eb
		existed = 0;
Packit 8480eb
Packit 8480eb
	this = hosts;
Packit 8480eb
	while (this) {
Packit 8480eb
		char *loc;
Packit 8480eb
Packit 8480eb
		/* Port option specified, don't try to bind */
Packit 8480eb
		if (!(nosymlink || nobind) &&
Packit 8480eb
		    this->proximity == PROXIMITY_LOCAL) {
Packit 8480eb
			/* Local host -- do a "bind" */
Packit 8480eb
			const char *bind_options;
Packit 8480eb
Packit 8480eb
			debug(ap->logopt,
Packit 8480eb
			      MODPREFIX "%s is local, attempt bind mount",
Packit 8480eb
			      name);
Packit 8480eb
Packit 8480eb
			bind_options = symlink ? "symlink" : ro ? "ro" : "";
Packit 8480eb
Packit 8480eb
			err = mount_bind->mount_mount(ap, root, name, name_len,
Packit 8480eb
					       this->path, "bind", bind_options,
Packit 8480eb
					       mount_bind->context);
Packit 8480eb
Packit 8480eb
			/* Success - we're done */
Packit 8480eb
			if (!err) {
Packit 8480eb
				free_host_list(&hosts);
Packit 8480eb
				return 0;
Packit 8480eb
			}
Packit 8480eb
Packit 8480eb
			/* Failed to update mtab, don't try any more */
Packit 8480eb
			if (err == MNT_FORCE_FAIL)
Packit 8480eb
				goto forced_fail;
Packit 8480eb
Packit 8480eb
			/* No hostname, can't be NFS */
Packit 8480eb
			if (!this->name) {
Packit 8480eb
				this = this->next;
Packit 8480eb
				continue;
Packit 8480eb
			}
Packit 8480eb
		}
Packit 8480eb
Packit 8480eb
		/* Not a local host - do an NFS mount */
Packit 8480eb
Packit 8480eb
		if (this->rr && this->addr &&
Packit 8480eb
		    !defaults_use_hostname_for_mounts()) {
Packit 8480eb
			socklen_t len = INET6_ADDRSTRLEN;
Packit 8480eb
			char n_buf[len + 1];
Packit 8480eb
			const char *n_addr;
Packit Bot c3b261
Packit 8480eb
			n_addr = get_addr_string(this->addr, n_buf, len);
Packit Bot c3b261
			if (!n_addr) {
Packit Bot c3b261
				char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
Packit Bot c3b261
				error(ap->logopt, "get_addr_string: %s", estr);
Packit Bot c3b261
				goto forced_fail;
Packit Bot c3b261
			}
Packit 8480eb
			loc = malloc(strlen(n_addr) + strlen(this->path) + 4);
Packit 8480eb
			if (!loc) {
Packit 8480eb
				char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
Packit 8480eb
				error(ap->logopt, "malloc: %s", estr);
Packit 8480eb
				goto forced_fail;
Packit 8480eb
			}
Packit 8480eb
			if (this->addr->sa_family == AF_INET6) {
Packit 8480eb
				strcpy(loc, "[");
Packit 8480eb
				strcat(loc, n_addr);
Packit 8480eb
				strcat(loc, "]");
Packit 8480eb
			} else
Packit 8480eb
				strcpy(loc, n_addr);
Packit 8480eb
		} else {
Packit 8480eb
			char *host = this->name ? this->name : "localhost";
Packit 8480eb
			loc = malloc(strlen(host) + strlen(this->path) + 2);
Packit 8480eb
			if (!loc) {
Packit 8480eb
				char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
Packit 8480eb
				error(ap->logopt, "malloc: %s", estr);
Packit 8480eb
				goto forced_fail;
Packit 8480eb
			}
Packit 8480eb
			strcpy(loc, host);
Packit 8480eb
		}
Packit 8480eb
		strcat(loc, ":");
Packit 8480eb
		strcat(loc, this->path);
Packit 8480eb
Packit 8480eb
		/* If this is a fallback from a bind mount failure
Packit 8480eb
		 * check if the local NFS server is available to try
Packit 8480eb
		 * and prevent lengthy mount failure waits.
Packit 8480eb
		 */
Packit 8480eb
		if (this->proximity == PROXIMITY_LOCAL) {
Packit 8480eb
			char *host = this->name ? this->name : "localhost";
Packit Bot 3eb012
			int ret = 1;
Packit Bot 3eb012
Packit Bot 3eb012
			/* If we're using RDMA, rpc_ping will fail when
Packit Bot 3eb012
			 * nfs-server is local. Therefore, don't probe
Packit Bot 3eb012
			 * when we're using RDMA.
Packit Bot 3eb012
			 */
Packit Bot 3eb012
			if(!rdma)
Packit Bot 3eb012
				ret = rpc_ping(host, port, vers, 2, 0, RPC_CLOSE_DEFAULT);
Packit 8480eb
			if (ret <= 0)
Packit 8480eb
				goto next;
Packit 8480eb
		}
Packit 8480eb
Packit 8480eb
		if (nfsoptions && *nfsoptions) {
Packit Bot 3cd872
			mountlog(ap->logopt,
Packit Bot 3cd872
				 MODPREFIX "calling mount -t %s " SLOPPY
Packit Bot 3cd872
				 "-o %s %s %s", fstype, nfsoptions, loc,
Packit Bot 3cd872
				 fullpath);
Packit 8480eb
Packit 8480eb
			err = spawn_mount(ap->logopt,
Packit 8480eb
					  "-t", fstype, SLOPPYOPT "-o",
Packit 8480eb
					  nfsoptions, loc, fullpath, NULL);
Packit 8480eb
		} else {
Packit Bot 3cd872
			mountlog(ap->logopt,
Packit Bot 3cd872
				 MODPREFIX "calling mount -t %s %s %s",
Packit Bot 3cd872
				 fstype, loc, fullpath);
Packit 8480eb
			err = spawn_mount(ap->logopt,
Packit 8480eb
					  "-t", fstype, loc, fullpath, NULL);
Packit 8480eb
		}
Packit 8480eb
Packit 8480eb
		if (!err) {
Packit 8480eb
			debug(ap->logopt, MODPREFIX "mounted %s on %s", loc, fullpath);
Packit 8480eb
			free(loc);
Packit 8480eb
			free_host_list(&hosts);
Packit 8480eb
			return 0;
Packit 8480eb
		}
Packit 8480eb
next:
Packit 8480eb
		free(loc);
Packit 8480eb
		this = this->next;
Packit 8480eb
	}
Packit 8480eb
Packit 8480eb
forced_fail:
Packit 8480eb
	free_host_list(&hosts);
Packit 8480eb
Packit 8480eb
	/* If we get here we've failed to complete the mount */
Packit 8480eb
Packit 8480eb
	info(ap->logopt, MODPREFIX "nfs: mount failure %s on %s", what, fullpath);
Packit 8480eb
Packit 8480eb
	if (ap->type != LKP_INDIRECT)
Packit 8480eb
		return 1;
Packit 8480eb
Packit 8480eb
	if ((!(ap->flags & MOUNT_FLAG_GHOST) && name_len) || !existed)
Packit 8480eb
		rmdir_path(ap, fullpath, ap->dev);
Packit 8480eb
Packit 8480eb
	return 1;
Packit 8480eb
}
Packit 8480eb
Packit 8480eb
int mount_done(void *context)
Packit 8480eb
{
Packit 8480eb
	int rv = 0;
Packit 8480eb
Packit 8480eb
	if (--init_ctr == 0) {
Packit 8480eb
		rv = close_mount(mount_bind);
Packit 8480eb
		mount_bind = NULL;
Packit 8480eb
	}
Packit 8480eb
	return rv;
Packit 8480eb
}