+ if (!nfs_probe_mntport(sap, salen, pmap))
+ return 0;
+ clnt = mnt_openclnt(mnt_server, &msock);
+ if (!clnt)
+ return 0;
+ res = clnt_call(clnt, MOUNTPROC_UMNT,
+ (xdrproc_t)xdr_dirpath, (caddr_t)argp,
+ (xdrproc_t)xdr_void, NULL,
+ TIMEOUT);
+ mnt_closeclnt(clnt, msock);
+
+ if (res == RPC_SUCCESS)
+ return 1;
+ return 0;
+}
+
+/**
+ * mnt_openclnt - get a handle for a remote mountd service
+ * @mnt_server: address and pmap arguments of mountd service
+ * @msock: returns a file descriptor of the underlying transport socket
+ *
+ * Returns an active handle for the remote's mountd service
+ */
+CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock)
+{
+ struct sockaddr_in *mnt_saddr = &mnt_server->saddr;
+ struct pmap *mnt_pmap = &mnt_server->pmap;
+ CLIENT *clnt = NULL;
+
+ mnt_saddr->sin_port = htons((u_short)mnt_pmap->pm_port);
+ *msock = get_socket(mnt_saddr, mnt_pmap->pm_prot, MOUNT_TIMEOUT,
+ TRUE, FALSE);
+ if (*msock == RPC_ANYSOCK) {
+ if (rpc_createerr.cf_error.re_errno == EADDRINUSE)
+ /*
+ * Probably in-use by a TIME_WAIT connection,
+ * It is worth waiting a while and trying again.
+ */
+ rpc_createerr.cf_stat = RPC_TIMEDOUT;
+ return NULL;
+ }
+
+ switch (mnt_pmap->pm_prot) {
+ case IPPROTO_UDP:
+ clnt = clntudp_bufcreate(mnt_saddr,
+ mnt_pmap->pm_prog, mnt_pmap->pm_vers,
+ RETRY_TIMEOUT, msock,
+ MNT_SENDBUFSIZE, MNT_RECVBUFSIZE);
+ break;
+ case IPPROTO_TCP:
+ clnt = clnttcp_create(mnt_saddr,
+ mnt_pmap->pm_prog, mnt_pmap->pm_vers,
+ msock,
+ MNT_SENDBUFSIZE, MNT_RECVBUFSIZE);
+ break;
+ }
+ if (clnt) {
+ /* try to mount hostname:dirname */
+ clnt->cl_auth = nfs_authsys_create();
+ if (clnt->cl_auth)
+ return clnt;
+ CLNT_DESTROY(clnt);
+ }
+ return NULL;
+}
+
+/**
+ * mnt_closeclnt - terminate a handle for a remote mountd service
+ * @clnt: pointer to an active handle for a remote mountd service
+ * @msock: file descriptor of the underlying transport socket
+ *
+ */
+void mnt_closeclnt(CLIENT *clnt, int msock)
+{
+ auth_destroy(clnt->cl_auth);
+ clnt_destroy(clnt);
+ close(msock);
+}
+
+/**
+ * clnt_ping - send an RPC ping to the remote RPC service endpoint
+ * @saddr: server's address
+ * @prog: target RPC program number
+ * @vers: target RPC version number
+ * @prot: target RPC protocol
+ * @caddr: filled in with our network address
+ *
+ * Sigh... GETPORT queries don't actually check the version number.
+ * In order to make sure that the server actually supports the service
+ * we're requesting, we open an RPC client, and fire off a NULL
+ * RPC call.
+ *
+ * caddr is the network address that the server will use to call us back.
+ * On multi-homed clients, this address depends on which NIC we use to
+ * route requests to the server.
+ *
+ * Returns one if successful, otherwise zero.
+ */
+int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog,
+ const unsigned long vers, const unsigned int prot,
+ struct sockaddr_in *caddr)
+{
+ CLIENT *clnt = NULL;
+ int sock, status;
+ static char clnt_res;
+ struct sockaddr dissolve;
+
+ rpc_createerr.cf_stat = status = 0;
+ sock = get_socket(saddr, prot, CONNECT_TIMEOUT, FALSE, TRUE);
+ if (sock == RPC_ANYSOCK) {
+ if (rpc_createerr.cf_error.re_errno == ETIMEDOUT) {
+ /*
+ * TCP timeout. Bubble up the error to see
+ * how it should be handled.
+ */
+ rpc_createerr.cf_stat = RPC_TIMEDOUT;
+ }
+ return 0;
+ }
+
+ if (caddr) {
+ /* Get the address of our end of this connection */
+ socklen_t len = sizeof(*caddr);
+ if (getsockname(sock, caddr, &len) != 0)
+ caddr->sin_family = 0;
+ }
+
+ switch(prot) {
+ case IPPROTO_UDP:
+ /* The socket is connected (so we could getsockname successfully),
+ * but some servers on multi-homed hosts reply from
+ * the wrong address, so if we stay connected, we lose the reply.
+ */
+ dissolve.sa_family = AF_UNSPEC;
+ connect(sock, &dissolve, sizeof(dissolve));
+
+ clnt = clntudp_bufcreate(saddr, prog, vers,
+ RETRY_TIMEOUT, &sock,
+ RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
+ break;
+ case IPPROTO_TCP:
+ clnt = clnttcp_create(saddr, prog, vers, &sock,
+ RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
+ break;
+ }
+ if (!clnt) {
+ close(sock);
+ return 0;
+ }
+ memset(&clnt_res, 0, sizeof(clnt_res));
+ status = clnt_call(clnt, NULLPROC,
+ (xdrproc_t)xdr_void, (caddr_t)NULL,
+ (xdrproc_t)xdr_void, (caddr_t)&clnt_res,
+ TIMEOUT);
+ if (status) {
+ clnt_geterr(clnt, &rpc_createerr.cf_error);
+ rpc_createerr.cf_stat = status;
+ }
+ clnt_destroy(clnt);
+ close(sock);
+
+ if (status == RPC_SUCCESS)
+ return 1;
+ else
+ return 0;
+}
+
+/*
+ * Try a getsockname() on a connected datagram socket.
+ *
+ * Returns 1 and fills in @buf if successful; otherwise, zero.
+ *
+ * A connected datagram socket prevents leaving a socket in TIME_WAIT.
+ * This conserves the ephemeral port number space, helping reduce failed
+ * socket binds during mount storms.
+ */
+static int nfs_ca_sockname(const struct sockaddr *sap, const socklen_t salen,
+ struct sockaddr *buf, socklen_t *buflen)
+{
+ struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = htonl(INADDR_ANY),
+ };
+ struct sockaddr_in6 sin6 = {
+ .sin6_family = AF_INET6,
+ .sin6_addr = IN6ADDR_ANY_INIT,
+ };
+ int sock;
+
+ sock = socket(sap->sa_family, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock < 0)
+ return 0;
+
+ switch (sap->sa_family) {
+ case AF_INET:
+ if (bind(sock, SAFE_SOCKADDR(&sin), sizeof(sin)) < 0) {
+ close(sock);