From 4f15a6b80b67c306ec3ea8bda4cafd2b9b16bd49 Mon Sep 17 00:00:00 2001
From: Ian Kent <ikent@redhat.com>
Date: Thu, 23 Feb 2012 15:58:40 +0800
Subject: [PATCH 2/3] autofs-5.0.6 - add function to check mount.nfs version

Add a function to check if the mount.nfs version is greater than or
equal to a given version.
---
 CHANGELOG           |    1 +
 configure           |   62 ++++++++++++++++++++++
 configure.in        |    1 +
 include/config.h.in |    6 +++
 include/mounts.h    |    7 +++
 lib/mounts.c        |  147 +++++++++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 224 insertions(+)

diff --git a/CHANGELOG b/CHANGELOG
index 7e5783f..359c293 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -28,6 +28,7 @@
 - fix init script usage message.
 - ignore duplicate exports in auto.net.
 - add kernel verion check function.
+- add function to check mount.nfs version.
 
 28/06/2011 autofs-5.0.6
 -----------------------
diff --git a/configure b/configure
index f78b322..ec394e7 100755
--- a/configure
+++ b/configure
@@ -645,6 +645,8 @@ HAVE_E2FSCK
 E2FSCK
 HAVE_UMOUNT
 UMOUNT
+HAVE_MOUNT_NFS
+MOUNT_NFS
 HAVE_MOUNT
 MOUNT
 DMALLOCLIB
@@ -3248,6 +3250,66 @@ else
   HAVE_MOUNT=0
 fi
 
+for ac_prog in mount.nfs
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+$as_echo_n "checking for $ac_word... " >&6; }
+if ${ac_cv_path_MOUNT_NFS+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  case $MOUNT_NFS in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_MOUNT_NFS="$MOUNT_NFS" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $searchpath
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+    for ac_exec_ext in '' $ac_executable_extensions; do
+  if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then
+    ac_cv_path_MOUNT_NFS="$as_dir/$ac_word$ac_exec_ext"
+    $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+  done
+IFS=$as_save_IFS
+
+  ;;
+esac
+fi
+MOUNT_NFS=$ac_cv_path_MOUNT_NFS
+if test -n "$MOUNT_NFS"; then
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MOUNT_NFS" >&5
+$as_echo "$MOUNT_NFS" >&6; }
+else
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
+$as_echo "no" >&6; }
+fi
+
+
+  test -n "$MOUNT_NFS" && break
+done
+test -n "$MOUNT_NFS" || MOUNT_NFS="/sbin/mount.nfs "
+
+if test -n "$MOUNT_NFS"; then
+
+$as_echo "#define HAVE_MOUNT_NFS 1" >>confdefs.h
+
+
+cat >>confdefs.h <<_ACEOF
+#define PATH_MOUNT_NFS "$MOUNT_NFS"
+_ACEOF
+
+  HAVE_MOUNT_NFS=1
+else
+  HAVE_MOUNT_NFS=0
+fi
+
 for ac_prog in umount
 do
   # Extract the first word of "$ac_prog", so it can be a program name with args.
diff --git a/configure.in b/configure.in
index c017829..5477508 100644
--- a/configure.in
+++ b/configure.in
@@ -137,6 +137,7 @@ AC_SUBST(DMALLOCLIB)
 # Programs needed for various system functions or modules
 #
 AF_PATH_INCLUDE(MOUNT, mount, /bin/mount, $searchpath)
+AF_PATH_INCLUDE(MOUNT_NFS, mount.nfs, /sbin/mount.nfs , $searchpath)
 AF_PATH_INCLUDE(UMOUNT, umount, /bin/umount, $searchpath)
 AF_PATH_INCLUDE(E2FSCK, fsck.ext2 e2fsck, , $searchpath)
 AF_PATH_INCLUDE(E3FSCK, fsck.ext3 e3fsck, , $searchpath)
diff --git a/include/config.h.in b/include/config.h.in
index fb4aadb..f79d579 100644
--- a/include/config.h.in
+++ b/include/config.h.in
@@ -45,6 +45,9 @@
 /* define if you have MOUNT */
 #undef HAVE_MOUNT
 
+/* define if you have MOUNT_NFS */
+#undef HAVE_MOUNT_NFS
+
 /* define if the mount command supports the -s option */
 #undef HAVE_SLOPPY_MOUNT
 
@@ -111,6 +114,9 @@
 /* define if you have MOUNT */
 #undef PATH_MOUNT
 
+/* define if you have MOUNT_NFS */
+#undef PATH_MOUNT_NFS
+
 /* define if you have RANLIB */
 #undef PATH_RANLIB
 
diff --git a/include/mounts.h b/include/mounts.h
index 3947d83..6a6ebab 100644
--- a/include/mounts.h
+++ b/include/mounts.h
@@ -89,6 +89,13 @@ static inline unsigned int linux_version_code(void)
         return KERNEL_VERSION(p, q, r);
 }
 
+struct nfs_mount_vers {
+	unsigned int major;
+	unsigned int minor;
+	unsigned int fix;
+};
+int check_nfs_mount_version(struct nfs_mount_vers *, struct nfs_mount_vers *);
+
 unsigned int query_kproto_ver(void);
 unsigned int get_kver_major(void);
 unsigned int get_kver_minor(void);
diff --git a/lib/mounts.c b/lib/mounts.c
index f26579e..efe0438 100644
--- a/lib/mounts.c
+++ b/lib/mounts.c
@@ -19,6 +19,8 @@
 #include <sys/stat.h>
 #include <sys/ioctl.h>
 #include <sys/mount.h>
+#include <sys/wait.h>
+#include <ctype.h>
 #include <stdio.h>
 #include <dirent.h>
 #include <sys/vfs.h>
@@ -30,6 +32,8 @@
 #define MAX_OPTIONS_LEN		80
 #define MAX_MNT_NAME_LEN	30
 
+#define EBUFSIZ 1024
+
 const unsigned int t_indirect = AUTOFS_TYPE_INDIRECT;
 const unsigned int t_direct = AUTOFS_TYPE_DIRECT;
 const unsigned int t_offset = AUTOFS_TYPE_OFFSET;
@@ -131,6 +135,149 @@ unsigned int get_kver_minor(void)
 	return kver.minor;
 }
 
+#ifdef HAVE_MOUNT_NFS
+static int extract_version(char *start, struct nfs_mount_vers *vers)
+{
+	char *s_ver = strchr(start, ' ');
+	while (*s_ver && !isdigit(*s_ver)) {
+		s_ver++;
+		if (!*s_ver)
+			return 0;
+		break;
+	}
+	vers->major = atoi(strtok(s_ver, "."));
+	vers->minor = (unsigned int) atoi(strtok(NULL, "."));
+	vers->fix = (unsigned int) atoi(strtok(NULL, "."));
+	return 1;
+}
+
+int check_nfs_mount_version(struct nfs_mount_vers *vers,
+			    struct nfs_mount_vers *check)
+{
+	pid_t f;
+	int ret, status, pipefd[2];
+	char errbuf[EBUFSIZ + 1], *p, *sp;
+	int errp, errn;
+	sigset_t allsigs, tmpsig, oldsig;
+	char *s_ver;
+	int cancel_state;
+
+	if (pipe(pipefd))
+		return -1;
+
+	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state);
+
+	sigfillset(&allsigs);
+	pthread_sigmask(SIG_BLOCK, &allsigs, &oldsig);
+
+	f = fork();
+	if (f == 0) {
+		reset_signals();
+		close(pipefd[0]);
+		dup2(pipefd[1], STDOUT_FILENO);
+		dup2(pipefd[1], STDERR_FILENO);
+		close(pipefd[1]);
+
+		execl(PATH_MOUNT_NFS, PATH_MOUNT_NFS, "-V", (char *) NULL);
+		_exit(255);	/* execv() failed */
+	}
+
+	ret = 0;
+
+	tmpsig = oldsig;
+
+	sigaddset(&tmpsig, SIGCHLD);
+	pthread_sigmask(SIG_SETMASK, &tmpsig, NULL);
+
+	close(pipefd[1]);
+
+	if (f < 0) {
+		close(pipefd[0]);
+		pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
+		pthread_setcancelstate(cancel_state, NULL);
+		return -1;
+	}
+
+	errp = 0;
+	do {
+		while (1) {
+			errn = read(pipefd[0], errbuf + errp, EBUFSIZ - errp);
+			if (errn == -1 && errno == EINTR)
+				continue;
+			break;
+		}
+
+		if (errn > 0) {
+			errp += errn;
+
+			sp = errbuf;
+			while (errp && (p = memchr(sp, '\n', errp))) {
+				*p++ = '\0';
+				errp -= (p - sp);
+				sp = p;
+			}
+
+			if (errp && sp != errbuf)
+				memmove(errbuf, sp, errp);
+
+			if (errp >= EBUFSIZ) {
+				/* Line too long, split */
+				errbuf[errp] = '\0';
+				if ((s_ver = strstr(errbuf, "nfs-utils"))) {
+					if (extract_version(s_ver, vers))
+						ret = 1;
+				}
+				errp = 0;
+			}
+
+			if ((s_ver = strstr(errbuf, "nfs-utils"))) {
+				if (extract_version(s_ver, vers))
+					ret = 1;
+			}
+		}
+	} while (errn > 0);
+
+	close(pipefd[0]);
+
+	if (errp > 0) {
+		/* End of file without \n */
+		errbuf[errp] = '\0';
+		if ((s_ver = strstr(errbuf, "nfs-utils"))) {
+			if (extract_version(s_ver, vers))
+				ret = 1;
+		}
+	}
+
+	if (ret) {
+		if (vers->major == check->major &&
+		    vers->minor == check->minor &&
+		    vers->fix == check->fix)
+			;
+		else {
+			if (vers->major < check->major)
+				ret = 0;
+			else if (vers->minor < check->minor)
+				ret = 0;
+			else if (vers->fix < check->fix)
+				ret = 0;
+		}
+	}
+
+	if (waitpid(f, &status, 0) != f) ;
+
+	pthread_sigmask(SIG_SETMASK, &oldsig, NULL);
+	pthread_setcancelstate(cancel_state, NULL);
+
+	return ret;
+}
+#else
+int check_nfs_mount_version(struct nfs_mount_vers *vers,
+			    struct nfs_mount_vers *check)
+{
+	return 0;
+}
+#endif
+
 /*
  * Make common autofs mount options string
  */
-- 
1.7.10.4

