--- /dev/null
+/*
+ * parse_dev.c -- parse device name into hostname and export path
+ *
+ * Copyright (C) 2008 Oracle. 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; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 021110-1307, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "xcommon.h"
+#include "nls.h"
+#include "parse_dev.h"
+
+#ifndef NFS_MAXHOSTNAME
+#define NFS_MAXHOSTNAME (255)
+#endif
+
+#ifndef NFS_MAXPATHNAME
+#define NFS_MAXPATHNAME (1024)
+#endif
+
+extern char *progname;
+extern int verbose;
+
+static int nfs_pdn_no_devname_err(void)
+{
+ nfs_error(_("%s: no device name was provided"), progname);
+ return 0;
+}
+
+static int nfs_pdn_hostname_too_long_err(void)
+{
+ nfs_error(_("%s: server hostname is too long"), progname);
+ return 0;
+}
+
+static int nfs_pdn_pathname_too_long_err(void)
+{
+ nfs_error(_("%s: export pathname is too long"), progname);
+ return 0;
+}
+
+static int nfs_pdn_bad_format_err(void)
+{
+ nfs_error(_("%s: remote share not in 'host:dir' format"), progname);
+ return 0;
+}
+
+static int nfs_pdn_nomem_err(void)
+{
+ nfs_error(_("%s: no memory available to parse devname"), progname);
+ return 0;
+}
+
+static int nfs_pdn_missing_brace_err(void)
+{
+ nfs_error(_("%s: closing bracket missing from server address"),
+ progname);
+ return 0;
+}
+
+/*
+ * Standard hostname:path format
+ */
+static int nfs_parse_simple_hostname(const char *dev,
+ char **hostname, char **pathname)
+{
+ size_t host_len, path_len;
+ char *colon, *comma;
+
+ /* Must have a colon */
+ colon = strchr(dev, ':');
+ if (colon == NULL)
+ return nfs_pdn_bad_format_err();
+ *colon = '\0';
+ host_len = colon - dev;
+
+ if (host_len > NFS_MAXHOSTNAME)
+ return nfs_pdn_hostname_too_long_err();
+
+ /* If there's a comma before the colon, take only the
+ * first name in list */
+ comma = strchr(dev, ',');
+ if (comma != NULL) {
+ *comma = '\0';
+ host_len = comma - dev;
+ nfs_error(_("%s: warning: multiple hostnames not supported"),
+ progname);
+ } else
+
+ colon++;
+ path_len = strlen(colon);
+ if (path_len > NFS_MAXPATHNAME)
+ return nfs_pdn_pathname_too_long_err();
+
+ if (hostname) {
+ *hostname = strndup(dev, host_len);
+ if (*hostname == NULL)
+ return nfs_pdn_nomem_err();
+ }
+ if (pathname) {
+ *pathname = strndup(colon, path_len);
+ if (*pathname == NULL) {
+ free(*hostname);
+ return nfs_pdn_nomem_err();
+ }
+ }
+ return 1;
+}
+
+/*
+ * To handle raw IPv6 addresses (which contain colons), the
+ * server's address is enclosed in square brackets. Return
+ * what's between the brackets.
+ *
+ * There could be anything in between the brackets, but we'll
+ * let DNS resolution sort it out later.
+ */
+static int nfs_parse_square_bracket(const char *dev,
+ char **hostname, char **pathname)
+{
+ size_t host_len, path_len;
+ char *cbrace;
+
+ dev++;
+
+ /* Must have a closing square bracket */
+ cbrace = strchr(dev, ']');
+ if (cbrace == NULL)
+ return nfs_pdn_missing_brace_err();
+ *cbrace = '\0';
+ host_len = cbrace - dev;
+
+ /* Must have a colon just after the closing bracket */
+ cbrace++;
+ if (*cbrace != ':')
+ return nfs_pdn_bad_format_err();
+
+ if (host_len > NFS_MAXHOSTNAME)
+ return nfs_pdn_hostname_too_long_err();
+
+ cbrace++;
+ path_len = strlen(cbrace);
+ if (path_len > NFS_MAXPATHNAME)
+ return nfs_pdn_pathname_too_long_err();
+
+ if (hostname) {
+ *hostname = strndup(dev, host_len);
+ if (*hostname == NULL)
+ return nfs_pdn_nomem_err();
+ }
+ if (pathname) {
+ *pathname = strndup(cbrace, path_len);
+ if (*pathname == NULL) {
+ free(*hostname);
+ return nfs_pdn_nomem_err();
+ }
+ }
+ return 1;
+}
+
+/*
+ * RFC 2224 says an NFS client must grok "public file handles" to
+ * support NFS URLs. Linux doesn't do that yet. Print a somewhat
+ * helpful error message in this case instead of pressing forward
+ * with the mount request and failing with a cryptic error message
+ * later.
+ */
+static int nfs_parse_nfs_url(const char *dev,
+ char **hostname, char **pathname)
+{
+ nfs_error(_("%s: NFS URLs are not supported"), progname);
+ return 0;
+}
+
+/**
+ * nfs_parse_devname - Determine the server's hostname by looking at "devname".
+ * @devname: pointer to mounted device name (first argument of mount command)
+ * @hostname: OUT: pointer to server's hostname
+ * @pathname: OUT: pointer to export path on server
+ *
+ * Returns 1 if succesful, or zero if some error occurred. On success,
+ * @hostname and @pathname point to dynamically allocated buffers containing
+ * the hostname of the server and the export pathname (both '\0'-terminated).
+ *
+ * @hostname or @pathname may be NULL if caller doesn't want a copy of those
+ * parts of @devname.
+ *
+ * Note that this will not work if @devname is a wide-character string.
+ */
+int nfs_parse_devname(const char *devname,
+ char **hostname, char **pathname)
+{
+ char *dev;
+ int result;
+
+ if (devname == NULL)
+ return nfs_pdn_no_devname_err();
+
+ /* Parser is destructive, so operate on a copy of the device name. */
+ dev = strdup(devname);
+ if (dev == NULL)
+ return nfs_pdn_nomem_err();
+ if (*dev == '[')
+ result = nfs_parse_square_bracket(dev, hostname, pathname);
+ else if (strncmp(dev, "nfs://", 6) == 0)
+ result = nfs_parse_nfs_url(dev, hostname, pathname);
+ else
+ result = nfs_parse_simple_hostname(dev, hostname, pathname);
+
+ free(dev);
+ return result;
+}
#include "network.h"
#include "parse_opt.h"
#include "version.h"
+#include "parse_dev.h"
#ifdef HAVE_RPCSVC_NFS_PROT_H
#include <rpcsvc/nfs_prot.h>
sa_family_t family; /* supported address family */
};
-static int nfs_parse_devname(struct nfsmount_info *mi)
-{
- int ret = 0;
- char *dev, *pathname, *s;
-
- dev = xstrdup(mi->spec);
-
- if (!(pathname = strchr(dev, ':'))) {
- nfs_error(_("%s: remote share not in 'host:dir' format"),
- progname);
- goto out;
- }
- *pathname = '\0';
- pathname++;
-
- /*
- * We don't need a copy of the pathname, but let's
- * sanity check it anyway.
- */
- if (strlen(pathname) > NFS_MAXPATHNAME) {
- nfs_error(_("%s: export pathname is too long"),
- progname);
- goto out;
- }
-
- /*
- * Ignore all but first hostname in replicated mounts
- * until they can be fully supported. (mack@sgi.com)
- */
- if ((s = strchr(dev, ','))) {
- *s = '\0';
- nfs_error(_("%s: warning: multiple hostnames not supported"),
- progname);
- nfs_error(_("%s: ignoring hostnames that follow the first one"),
- progname);
- }
- mi->hostname = xstrdup(dev);
- if (strlen(mi->hostname) > NFS_MAXHOSTNAME) {
- nfs_error(_("%s: server hostname is too long"),
- progname);
- free(mi->hostname);
- mi->hostname = NULL;
- goto out;
- }
-
- ret = 1;
-
-out:
- free(dev);
- return ret;
-}
-
static int fill_ipv4_sockaddr(const char *hostname, struct sockaddr_in *addr)
{
struct hostent *hp;
*/
static int nfs_validate_options(struct nfsmount_info *mi)
{
- struct sockaddr_in saddr;
+ struct sockaddr_storage dummy;
+ struct sockaddr *sap = (struct sockaddr *)&dummy;
+ socklen_t salen = sizeof(dummy);
- if (!fill_ipv4_sockaddr(mi->hostname, &saddr))
+ if (!nfs_parse_devname(mi->spec, &mi->hostname, NULL))
+ return 0;
+
+ if (!nfs_name_to_address(mi->hostname, mi->family, sap, &salen))
return 0;
if (strncmp(mi->type, "nfs4", 4) == 0) {
- if (!nfs_append_clientaddr_option((struct sockaddr *)&saddr,
- sizeof(saddr), mi->options))
+ if (!nfs_append_clientaddr_option(sap, salen, mi->options))
return 0;
} else {
if (!nfs_fix_mounthost_option(mi->family, mi->options))
if (!nfs_append_sloppy_option(mi->options))
return 0;
- return nfs_append_addr_option((struct sockaddr *)&saddr,
- sizeof(saddr), mi->options);
+ return nfs_append_addr_option(sap, salen, mi->options);
}
/*
};
int retval = EX_FAIL;
- if (!nfs_parse_devname(&mi))
- return retval;
-
mi.options = po_split(*extra_opts);
if (mi.options) {
retval = nfsmount_start(&mi);