#include "xcommon.h"
#include "mount.h"
#include "nls.h"
+#include "nfsrpc.h"
#include "mount_constants.h"
#include "stropts.h"
#include "error.h"
extern int verbose;
extern int sloppy;
+union nfs_sockaddr {
+ struct sockaddr sa;
+ struct sockaddr_in s4;
+ struct sockaddr_in6 s6;
+};
+
struct nfsmount_info {
const char *spec, /* server:/path */
*node, /* mounted-on dir */
*type; /* "nfs" or "nfs4" */
char *hostname; /* server's hostname */
- struct sockaddr_storage address; /* server's address */
+ union nfs_sockaddr address;
socklen_t salen; /* size of server's address */
struct mount_options *options; /* parsed mount options */
socklen_t salen,
struct mount_options *options)
{
- struct sockaddr_storage dummy;
- struct sockaddr *my_addr = (struct sockaddr *)&dummy;
- socklen_t my_len = sizeof(dummy);
+ union nfs_sockaddr address;
+ struct sockaddr *my_addr = &address.sa;
+ socklen_t my_len = sizeof(address);
if (po_contains(options, "clientaddr") == PO_FOUND)
return 1;
}
/*
- * Resolve the 'mounthost=' hostname and append a new option using
- * the resulting address.
+ * Determine whether to append a 'mountaddr=' option. The option is needed if:
+ *
+ * 1. "mounthost=" was specified, or
+ * 2. The address families for proto= and mountproto= are different.
*/
-static int nfs_fix_mounthost_option(struct mount_options *options)
+static int nfs_fix_mounthost_option(struct mount_options *options,
+ const char *nfs_hostname)
{
- struct sockaddr_storage dummy;
- struct sockaddr *sap = (struct sockaddr *)&dummy;
- socklen_t salen = sizeof(dummy);
+ union nfs_sockaddr address;
+ struct sockaddr *sap = &address.sa;
+ socklen_t salen = sizeof(address);
+ sa_family_t nfs_family, mnt_family;
char *mounthost;
+ if (!nfs_nfs_proto_family(options, &nfs_family))
+ return 0;
+ if (!nfs_mount_proto_family(options, &mnt_family))
+ return 0;
+
mounthost = po_get(options, "mounthost");
- if (!mounthost)
- return 1;
+ if (mounthost == NULL) {
+ if (nfs_family == mnt_family)
+ return 1;
+ mounthost = (char *)nfs_hostname;
+ }
- if (!nfs_name_to_address(mounthost, sap, &salen)) {
+ if (!nfs_lookup(mounthost, mnt_family, sap, &salen)) {
nfs_error(_("%s: unable to determine mount server's address"),
progname);
return 0;
return 1;
}
-/*
- * Set up mandatory non-version specific NFS mount options.
- *
- * Returns 1 if successful; otherwise zero.
- */
-static int nfs_validate_options(struct nfsmount_info *mi)
+static int nfs_set_version(struct nfsmount_info *mi)
{
- struct sockaddr *sap = (struct sockaddr *)&mi->address;
-
- if (!nfs_parse_devname(mi->spec, &mi->hostname, NULL))
- return 0;
-
- mi->salen = sizeof(mi->address);
- if (!nfs_name_to_address(mi->hostname, sap, &mi->salen))
- return 0;
-
if (!nfs_nfs_version(mi->options, &mi->version))
return 0;
+
if (strncmp(mi->type, "nfs4", 4) == 0)
mi->version = 4;
else {
mi->version = 3;
}
+ /*
+ * If we still don't know, check for version-specific
+ * mount options.
+ */
+ if (mi->version == 0) {
+ if (po_contains(mi->options, "mounthost") ||
+ po_contains(mi->options, "mountaddr") ||
+ po_contains(mi->options, "mountvers") ||
+ po_contains(mi->options, "mountproto"))
+ mi->version = 3;
+ }
+
/*
* If enabled, see if the default version was
* set in the config file
*/
nfs_default_version(mi);
+
+ return 1;
+}
+
+/*
+ * Set up mandatory non-version specific NFS mount options.
+ *
+ * Returns 1 if successful; otherwise zero.
+ */
+static int nfs_validate_options(struct nfsmount_info *mi)
+{
+ struct sockaddr *sap = &mi->address.sa;
+ sa_family_t family;
+
+ if (!nfs_parse_devname(mi->spec, &mi->hostname, NULL))
+ return 0;
+
+ if (!nfs_nfs_proto_family(mi->options, &family))
+ return 0;
+ mi->salen = sizeof(mi->address);
+ if (!nfs_lookup(mi->hostname, family, sap, &mi->salen))
+ return 0;
+
+ if (!nfs_set_version(mi))
+ return 0;
if (!nfs_append_sloppy_option(mi->options))
return 0;
}
static int nfs_construct_new_options(struct mount_options *options,
+ struct sockaddr *nfs_saddr,
struct pmap *nfs_pmap,
+ struct sockaddr *mnt_saddr,
struct pmap *mnt_pmap)
{
char new_option[64];
+ char *netid;
po_remove_all(options, "nfsprog");
po_remove_all(options, "mountprog");
po_remove_all(options, "proto");
po_remove_all(options, "udp");
po_remove_all(options, "tcp");
- switch (nfs_pmap->pm_prot) {
- case IPPROTO_TCP:
- snprintf(new_option, sizeof(new_option) - 1,
- "proto=tcp");
- if (po_append(options, new_option) == PO_FAILED)
- return 0;
- break;
- case IPPROTO_UDP:
- snprintf(new_option, sizeof(new_option) - 1,
- "proto=udp");
- if (po_append(options, new_option) == PO_FAILED)
- return 0;
- break;
- }
+ netid = nfs_get_netid(nfs_saddr->sa_family, nfs_pmap->pm_prot);
+ if (netid == NULL)
+ return 0;
+ snprintf(new_option, sizeof(new_option) - 1,
+ "proto=%s", netid);
+ free(netid);
+ if (po_append(options, new_option) == PO_FAILED)
+ return 0;
po_remove_all(options, "port");
if (nfs_pmap->pm_port != NFS_PORT) {
return 0;
po_remove_all(options, "mountproto");
- switch (mnt_pmap->pm_prot) {
- case IPPROTO_TCP:
- snprintf(new_option, sizeof(new_option) - 1,
- "mountproto=tcp");
- if (po_append(options, new_option) == PO_FAILED)
- return 0;
- break;
- case IPPROTO_UDP:
- snprintf(new_option, sizeof(new_option) - 1,
- "mountproto=udp");
- if (po_append(options, new_option) == PO_FAILED)
- return 0;
- break;
- }
+ netid = nfs_get_netid(mnt_saddr->sa_family, mnt_pmap->pm_prot);
+ if (netid == NULL)
+ return 0;
+ snprintf(new_option, sizeof(new_option) - 1,
+ "mountproto=%s", netid);
+ free(netid);
+ if (po_append(options, new_option) == PO_FAILED)
+ return 0;
po_remove_all(options, "mountport");
snprintf(new_option, sizeof(new_option) - 1,
static int
nfs_rewrite_pmap_mount_options(struct mount_options *options)
{
- struct sockaddr_storage nfs_address;
- struct sockaddr *nfs_saddr = (struct sockaddr *)&nfs_address;
+ union nfs_sockaddr nfs_address;
+ struct sockaddr *nfs_saddr = &nfs_address.sa;
socklen_t nfs_salen = sizeof(nfs_address);
struct pmap nfs_pmap;
- struct sockaddr_storage mnt_address;
- struct sockaddr *mnt_saddr = (struct sockaddr *)&mnt_address;
+ union nfs_sockaddr mnt_address;
+ struct sockaddr *mnt_saddr = &mnt_address.sa;
socklen_t mnt_salen = sizeof(mnt_address);
struct pmap mnt_pmap;
char *option;
return 0;
}
- if (!nfs_construct_new_options(options, &nfs_pmap, &mnt_pmap)) {
+ if (!nfs_construct_new_options(options, nfs_saddr, &nfs_pmap,
+ mnt_saddr, &mnt_pmap)) {
errno = EINVAL;
return 0;
}
return result;
}
- if (!nfs_fix_mounthost_option(options)) {
+ if (!nfs_fix_mounthost_option(options, mi->hostname)) {
errno = EINVAL;
goto out_fail;
}
*/
static int nfs_try_mount_v4(struct nfsmount_info *mi)
{
- struct sockaddr *sap = (struct sockaddr *)&mi->address;
+ struct sockaddr *sap = &mi->address.sa;
struct mount_options *options = po_dup(mi->options);
int result = 0;
}
if (mi->version == 0) {
+ if (po_contains(options, "mounthost") ||
+ po_contains(options, "mountaddr") ||
+ po_contains(options, "mountvers") ||
+ po_contains(options, "mountproto")) {
+ /*
+ * Since these mountd options are set assume version 3
+ * is wanted so error out with EPROTONOSUPPORT so the
+ * protocol negation starts with v3.
+ */
+ errno = EPROTONOSUPPORT;
+ goto out_fail;
+ }
if (po_append(options, "vers=4") == PO_FAILED) {
errno = EINVAL;
goto out_fail;
errno = EINVAL;
goto out_fail;
}
+
/*
* Update option string to be recorded in /etc/mtab.
*/
if (po_join(options, mi->extra_opts) == PO_FAILED) {
errno = ENOMEM;
- return 0;
+ goto out_fail;
}
result = nfs_sys_mount(mi, options);
/*
* To deal with legacy Linux servers that don't
* automatically export a pseudo root, retry
- * ENOENT errors using version 3
+ * ENOENT errors using version 3. And for
+ * Linux servers prior to 2.6.25, retry EPERM
*/
- if (errno != ENOENT)
+ if (errno != ENOENT && errno != EPERM)
break;
}
}