nfs.c: Allow max sa.sun_path for a localdomain socket with the user nfs-server

There is a hard limit for the kernel of 108 characters for a
localdomain socket name.  To avoid problems with the user nfs
server it should maximize the number of characters by using
a relative path on the server side.

Previously the nfs-server used the absolute path name passed to
the sa.sunpath arg for binding the socket and this has caused
problems for both the X server and UST binaries which make
heavy use of named sockets with long names.

Signed-off-by: Jason Wessel <jason.wessel@windriver.com>

Upstream-Status: Submitted http://sourceforge.net/p/unfs3/bugs/5/

---
 nfs.c |   29 +++++++++++++++++++++++++++--
 1 file changed, 27 insertions(+), 2 deletions(-)

--- a/nfs.c
+++ b/nfs.c
@@ -672,6 +672,7 @@ SYMLINK3res *nfsproc3_symlink_3_svc(SYML
 }
 
 #ifndef WIN32
+static char pathbuf_tmp[NFS_MAXPATHLEN + NFS_MAXNAMLEN + 1];
 
 /*
  * create Unix socket
@@ -680,17 +681,41 @@ static int mksocket(const char *path, mo
 {
     int res, sock;
     struct sockaddr_un addr;
+    unsigned int len = strlen(path);
 
     sock = socket(PF_UNIX, SOCK_STREAM, 0);
-    addr.sun_family = AF_UNIX;
-    strcpy(addr.sun_path, path);
     res = sock;
     if (res != -1) {
+	addr.sun_family = AF_UNIX;
+	if (len < sizeof(addr.sun_path) -1) {
+	    strcpy(addr.sun_path, path);
+	} else {
+	    char *ptr;
+	    res = -1;
+	    if (len >= sizeof(path))
+		goto out;
+	    strcpy(pathbuf_tmp, path);
+	    ptr = strrchr(pathbuf_tmp,'/');
+	    if (ptr) {
+		*ptr = '\0';
+		ptr++;
+		if (chdir(pathbuf_tmp))
+		    goto out;
+	    } else {
+		ptr = pathbuf_tmp;
+	    }
+	    if (strlen(ptr) >= sizeof(addr.sun_path))
+		goto out;
+	    strcpy(addr.sun_path, ptr);
+	}
 	umask(~mode);
 	res =
 	    bind(sock, (struct sockaddr *) &addr,
 		 sizeof(addr.sun_family) + strlen(addr.sun_path));
 	umask(0);
+out:
+	if (chdir("/"))
+	    fprintf(stderr, "Internal failure to chdir /\n");
 	close(sock);
     }
     return res;
