]> git.decadent.org.uk Git - nfs-utils.git/blobdiff - utils/mount/network.c
umount.nfs command: Add an AF_INET6-capable version of nfs_call_unmount()
[nfs-utils.git] / utils / mount / network.c
index 6a9a41a9f358b839cfbc89fdcb65dab351ac88f7..92f75b4a5e03d74c2ba2d00bb5de849f8734c9ac 100644 (file)
 #include "nfs_mount.h"
 #include "mount_constants.h"
 #include "nfsrpc.h"
+#include "parse_opt.h"
 #include "network.h"
 
-/*
- * Earlier versions of glibc's /usr/include/netdb.h exclude these
- * definitions because it was thought they were not part of a stable
- * POSIX standard.  However, they are defined by RFC 2553 and 3493
- * and in POSIX 1003.1-2001, so these definitions were added in later
- * versions of netdb.h.
- */
-#ifndef AI_V4MAPPED
-#define AI_V4MAPPED     0x0008  /* IPv4-mapped addresses are acceptable.  */
-#endif /* AI_V4MAPPED */
-#ifndef AI_ALL
-#define AI_ALL          0x0010  /* Return both IPv4 and IPv6 addresses.  */
-#endif /* AI_ALL */
-#ifndef AI_ADDRCONFIG
-#define AI_ADDRCONFIG   0x0020  /* Use configuration of this host to choose \
-                                  returned address type.  */
-#endif /* AI_ADDRCONFIG */
-
 #define PMAP_TIMEOUT   (10)
 #define CONNECT_TIMEOUT        (20)
 #define MOUNT_TIMEOUT  (30)
@@ -85,6 +68,33 @@ static const char *nfs_ns_pgmtbl[] = {
        NULL,
 };
 
+static const char *nfs_mnt_pgmtbl[] = {
+       "mount",
+       "mountd",
+       NULL,
+};
+
+static const char *nfs_nfs_pgmtbl[] = {
+       "nfs",
+       "nfsprog",
+       NULL,
+};
+
+static const char *nfs_transport_opttbl[] = {
+       "udp",
+       "tcp",
+       "proto",
+       NULL,
+};
+
+static const char *nfs_version_opttbl[] = {
+       "v2",
+       "v3",
+       "vers",
+       "nfsvers",
+       NULL,
+};
+
 static const unsigned long nfs_to_mnt[] = {
        0,
        0,
@@ -175,9 +185,11 @@ static void nfs_set_port(struct sockaddr *sap, const unsigned short port)
        }
 }
 
+#ifdef HAVE_DECL_AI_ADDRCONFIG
 /**
  * nfs_name_to_address - resolve hostname to an IPv4 or IPv6 socket address
  * @hostname: pointer to C string containing DNS hostname to resolve
+ * @af_hint: hint to restrict resolution to one address family
  * @sap: pointer to buffer to fill with socket address
  * @len: IN: size of buffer to fill; OUT: size of socket address
  *
@@ -228,11 +240,66 @@ int nfs_name_to_address(const char *hostname,
        freeaddrinfo(gai_results);
        return ret;
 }
+#else  /* HAVE_DECL_AI_ADDRCONFIG */
+/**
+ * nfs_name_to_address - resolve hostname to an IPv4 socket address
+ * @hostname: pointer to C string containing DNS hostname to resolve
+ * @af_hint: hint to restrict resolution to one address family
+ * @sap: pointer to buffer to fill with socket address
+ * @len: IN: size of buffer to fill; OUT: size of socket address
+ *
+ * Returns 1 and places a socket address at @sap if successful;
+ * otherwise zero.
+ *
+ * Some older getaddrinfo(3) implementations don't support
+ * AI_ADDRCONFIG or AI_V4MAPPED properly.  For those cases, a DNS
+ * resolver based on the traditional gethostbyname(3) is provided.
+ */
+int nfs_name_to_address(const char *hostname,
+                       const sa_family_t af_hint,
+                       struct sockaddr *sap, socklen_t *salen)
+{
+       struct sockaddr_in *sin = (struct sockaddr_in *)sap;
+       socklen_t len = *salen;
+       struct hostent *hp;
+
+       *salen = 0;
+
+       if (af_hint != AF_INET) {
+               nfs_error(_("%s: address family not supported by DNS resolver\n"),
+                               progname, hostname);
+               return 0;
+       }
+
+       sin->sin_family = AF_INET;
+       if (inet_aton(hostname, &sin->sin_addr)) {
+               *salen = sizeof(*sin);
+               return 1;
+       }
+
+       hp = gethostbyname(hostname);
+       if (hp == NULL) {
+               nfs_error(_("%s: DNS resolution failed for %s: %s"),
+                               progname, hostname, hstrerror(h_errno));
+               return 0;
+       }
+
+       if (hp->h_length > len) {
+               nfs_error(_("%s: DNS resolution results too long for buffer\n"),
+                               progname);
+               return 0;
+       }
+
+       memcpy(&sin->sin_addr, hp->h_addr, hp->h_length);
+       *salen = hp->h_length;
+       return 1;
+}
+#endif /* HAVE_DECL_AI_ADDRCONFIG */
 
 /**
  * nfs_gethostbyname - resolve a hostname to an IPv4 address
  * @hostname: pointer to a C string containing a DNS hostname
- * @saddr: returns an IPv4 address 
+ * @sin: returns an IPv4 address 
  *
  * Returns 1 if successful, otherwise zero.
  */
@@ -770,6 +837,59 @@ int start_statd(void)
        return 0;
 }
 
+/**
+ * nfs_advise_umount - ask the server to remove a share from it's rmtab
+ * @sap: pointer to IP address of server to call
+ * @salen: length of server address
+ * @pmap: partially filled-in mountd RPC service tuple
+ * @argp: directory path of share to "unmount"
+ *
+ * Returns one if the unmount call succeeded; zero if the unmount
+ * failed for any reason;  rpccreateerr.cf_stat is set to reflect
+ * the nature of the error.
+ *
+ * We use a fast timeout since this call is advisory only.
+ */
+int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen,
+                     const struct pmap *pmap, const dirpath *argp)
+{
+       struct sockaddr_storage address;
+       struct sockaddr *saddr = (struct sockaddr *)&address;
+       struct pmap mnt_pmap = *pmap;
+       struct timeval timeout = {
+               .tv_sec         = MOUNT_TIMEOUT >> 3,
+       };
+       CLIENT *client;
+       enum clnt_stat res = 0;
+
+       if (nfs_probe_mntport(sap, salen, &mnt_pmap) == 0)
+               return 0;
+
+       memcpy(saddr, sap, salen);
+       nfs_set_port(saddr, mnt_pmap.pm_port);
+
+       client = nfs_get_rpcclient(saddr, salen, mnt_pmap.pm_prot,
+                                       mnt_pmap.pm_prog, mnt_pmap.pm_vers,
+                                       &timeout);
+       if (client == NULL)
+               return 0;
+
+       client->cl_auth = authunix_create_default();
+
+       res = CLNT_CALL(client, MOUNTPROC_UMNT,
+                       (xdrproc_t)xdr_dirpath, (caddr_t)argp,
+                       (xdrproc_t)xdr_void, NULL,
+                       timeout);
+
+       auth_destroy(client->cl_auth);
+       CLNT_DESTROY(client);
+
+       if (res != RPC_SUCCESS)
+               return 0;
+
+       return 1;
+}
+
 /**
  * nfs_call_umount - ask the server to remove a share from it's rmtab
  * @mnt_server: address of RPC MNT program server
@@ -1072,3 +1192,188 @@ out_failed:
        return 0;
 
 }
+
+/*
+ * "nfsprog" is only supported by the legacy mount command.  The
+ * kernel mount client does not support this option.
+ *
+ * Returns the value set by the nfsprog= option, the value of
+ * the RPC NFS program specified in /etc/rpc, or a baked-in
+ * default program number, if all fails.
+ */
+static rpcprog_t nfs_nfs_program(struct mount_options *options)
+{
+       long tmp;
+
+       if (po_get_numeric(options, "nfsprog", &tmp) == PO_FOUND)
+               if (tmp >= 0)
+                       return tmp;
+       return nfs_getrpcbyname(NFSPROG, nfs_nfs_pgmtbl);
+}
+
+
+/*
+ * Returns the RPC version number specified by the given mount
+ * options for the NFS service, or zero if all fails.
+ */
+static rpcvers_t nfs_nfs_version(struct mount_options *options)
+{
+       long tmp;
+
+       switch (po_rightmost(options, nfs_version_opttbl)) {
+       case 0: /* v2 */
+               return 2;
+       case 1: /* v3 */
+               return 3;
+       case 2: /* vers */
+               if (po_get_numeric(options, "vers", &tmp) == PO_FOUND)
+                       if (tmp >= 2 && tmp <= 3)
+                               return tmp;
+               break;
+       case 3: /* nfsvers */
+               if (po_get_numeric(options, "nfsvers", &tmp) == PO_FOUND)
+                       if (tmp >= 2 && tmp <= 3)
+                               return tmp;
+               break;
+       }
+
+       return 0;
+}
+
+/*
+ * Returns the NFS transport protocol specified by the given mount options
+ *
+ * Returns the IPPROTO_ value specified by the given mount options, or
+ * IPPROTO_UDP if all fails.
+ */
+static unsigned short nfs_nfs_protocol(struct mount_options *options)
+{
+       char *option;
+
+       switch (po_rightmost(options, nfs_transport_opttbl)) {
+       case 1: /* tcp */
+               return IPPROTO_TCP;
+       case 2: /* proto */
+               option = po_get(options, "proto");
+               if (option) {
+                       if (strcmp(option, "tcp") == 0)
+                               return IPPROTO_TCP;
+                       if (strcmp(option, "udp") == 0)
+                               return IPPROTO_UDP;
+               }
+       }
+
+       return IPPROTO_UDP;
+}
+
+/*
+ * Returns the NFS server's port number specified by the given
+ * mount options, or zero if all fails.  Zero results in a portmap
+ * query to discover the server's mountd service port.
+ *
+ * port=0 will guarantee an rpcbind request precedes the first
+ * NFS RPC so the client can determine the server's port number.
+ */
+static unsigned short nfs_nfs_port(struct mount_options *options)
+{
+       long tmp;
+
+       if (po_get_numeric(options, "port", &tmp) == PO_FOUND)
+               if (tmp >= 0 && tmp <= 65535)
+                       return tmp;
+       return 0;
+}
+
+/*
+ * "mountprog" is only supported by the legacy mount command.  The
+ * kernel mount client does not support this option.
+ *
+ * Returns the value set by the mountprog= option, the value of
+ * the RPC mount program specified in /etc/rpc, or a baked-in
+ * default program number, if all fails.
+ */
+static rpcprog_t nfs_mount_program(struct mount_options *options)
+{
+       long tmp;
+
+       if (po_get_numeric(options, "mountprog", &tmp) == PO_FOUND)
+               if (tmp >= 0)
+                       return tmp;
+       return nfs_getrpcbyname(MOUNTPROG, nfs_mnt_pgmtbl);
+}
+
+/*
+ * Returns the RPC version number specified by the given mount options,
+ * or the version "3" if all fails.
+ */
+static rpcvers_t nfs_mount_version(struct mount_options *options)
+{
+       long tmp;
+
+       if (po_get_numeric(options, "mountvers", &tmp) == PO_FOUND)
+               if (tmp >= 1 && tmp <= 4)
+                       return tmp;
+
+       return nfsvers_to_mnt(nfs_nfs_version(options));
+}
+
+/*
+ * Returns the transport protocol to use for the mount service
+ *
+ * Returns the IPPROTO_ value specified by the mountproto option, or
+ * if that doesn't exist, the IPPROTO_ value specified for NFS
+ * itself.
+ */
+static unsigned short nfs_mount_protocol(struct mount_options *options)
+{
+       char *option;
+
+       option = po_get(options, "mountproto");
+       if (option) {
+               if (strcmp(option, "tcp") == 0)
+                       return IPPROTO_TCP;
+               if (strcmp(option, "udp") == 0)
+                       return IPPROTO_UDP;
+       }
+
+       return nfs_nfs_version(options);
+}
+
+/*
+ * Returns the mountd server's port number specified by the given
+ * mount options, or zero if all fails.  Zero results in a portmap
+ * query to discover the server's mountd service port.
+ *
+ * port=0 will guarantee an rpcbind request precedes the mount
+ * RPC so the client can determine the server's port number.
+ */
+static unsigned short nfs_mount_port(struct mount_options *options)
+{
+       long tmp;
+
+       if (po_get_numeric(options, "mountport", &tmp) == PO_FOUND)
+               if (tmp >= 0 && tmp <= 65535)
+                       return tmp;
+       return 0;
+}
+
+/**
+ * nfs_options2pmap - set up pmap structs based on mount options
+ * @options: pointer to mount options
+ * @nfs_pmap: OUT: pointer to pmap arguments for NFS server
+ * @mnt_pmap: OUT: pointer to pmap arguments for mountd server
+ *
+ */
+void nfs_options2pmap(struct mount_options *options,
+                     struct pmap *nfs_pmap, struct pmap *mnt_pmap)
+{
+       nfs_pmap->pm_prog = nfs_nfs_program(options);
+       nfs_pmap->pm_vers = nfs_nfs_version(options);
+       nfs_pmap->pm_prot = nfs_nfs_protocol(options);
+       nfs_pmap->pm_port = nfs_nfs_port(options);
+
+       mnt_pmap->pm_prog = nfs_mount_program(options);
+       mnt_pmap->pm_vers = nfs_mount_version(options);
+       mnt_pmap->pm_prot = nfs_mount_protocol(options);
+       mnt_pmap->pm_port = nfs_mount_port(options);
+}