Blob Blame History Raw
diff --git a/configure.ac b/configure.ac
index 276dec3..4b698dd 100644
--- a/configure.ac
+++ b/configure.ac
@@ -157,7 +157,7 @@ AC_ARG_WITH(rpcgen,
 	    RPCGEN_PATH=$rpcgen_path
 	fi
 	AC_SUBST(RPCGEN_PATH)
-	AM_CONDITIONAL(CONFIG_RPCGEN, [test "$RPCGEN_PATH" = ""])
+	AM_CONDITIONAL(CONFIG_RPCGEN, [test "$RPCGEN_PATH" = "internal"])
 AC_ARG_ENABLE(uuid,
 	[AC_HELP_STRING([--disable-uuid], 
 		[Exclude uuid support to avoid buggy libblkid. @<:@default=no@:>@])],
diff --git a/support/export/client.c b/support/export/client.c
index 2346f99..baf59c8 100644
--- a/support/export/client.c
+++ b/support/export/client.c
@@ -482,8 +482,9 @@ add_name(char *old, const char *add)
 		else
 			cp = cp + strlen(cp);
 	}
-	if (old) {
-		strncpy(new, old, cp-old);
+	len = cp-old;
+	if (old && len > 0) {
+		strncpy(new, old, len);
 		new[cp-old] = 0;
 	} else {
 		new[0] = 0;
diff --git a/support/include/conffile.h b/support/include/conffile.h
index bc2d61f..a3340f9 100644
--- a/support/include/conffile.h
+++ b/support/include/conffile.h
@@ -67,6 +67,7 @@ extern int      conf_match_num(const char *, const char *, int);
 extern int      conf_remove(int, const char *, const char *);
 extern int      conf_remove_section(int, const char *);
 extern void     conf_report(FILE *);
+extern int      conf_write(const char *, const char *, const char *, const char *, const char *);
 
 /*
  * Convert letter from upper case to lower case
diff --git a/support/include/exportfs.h b/support/include/exportfs.h
index 8af47a8..4e0d9d1 100644
--- a/support/include/exportfs.h
+++ b/support/include/exportfs.h
@@ -97,7 +97,7 @@ typedef struct mexport {
 	struct mclient *	m_client;
 	struct exportent	m_export;
 	int			m_exported;	/* known to knfsd. */
-	int			m_xtabent  : 1,	/* xtab entry exists */
+	unsigned int		m_xtabent  : 1,	/* xtab entry exists */
 				m_mayexport: 1,	/* derived from xtabbed */
 				m_changed  : 1, /* options (may) have changed */
 				m_warned   : 1; /* warned about multiple exports
diff --git a/support/junction/path.c b/support/junction/path.c
index 68a1d13..e74e4c4 100644
--- a/support/junction/path.c
+++ b/support/junction/path.c
@@ -326,8 +326,10 @@ nsdb_posix_to_path_array(const char *pathname, char ***path_array)
 			break;
 		next = strchrnul(component, '/');
 		length = (size_t)(next - component);
-		if (length > 255)
+		if (length > 255) {
+			nsdb_free_string_array(result);
 			return FEDFS_ERR_SVRFAULT;
+		}
 
 		result[i] = strndup(component, length);
 		if (result[i] == NULL) {
diff --git a/support/misc/file.c b/support/misc/file.c
index 63597df..4065376 100644
--- a/support/misc/file.c
+++ b/support/misc/file.c
@@ -96,7 +96,7 @@ generic_setup_basedir(const char *progname, const char *parentdir, char *base,
 	}
 
 	/* Ensure we have a clean directory pathname */
-	strncpy(buf, parentdir, sizeof(buf));
+	strncpy(buf, parentdir, sizeof(buf)-1);
 	path = dirname(buf);
 	if (*path == '.') {
 		(void)fprintf(stderr, "%s: Unusable directory %s",
diff --git a/support/nfs/conffile.c b/support/nfs/conffile.c
index ee94031..3845b94 100644
--- a/support/nfs/conffile.c
+++ b/support/nfs/conffile.c
@@ -255,14 +255,14 @@ conf_parse_line(int trans, char *line, const char *filename, int lineno, char **
 	char *inc_section = NULL, *inc_subsection = NULL;
 	char *relpath, *subconf;
 
+	/* Strip off any leading blanks */
+	while (isspace(*line))
+		line++;
+
 	/* Ignore blank lines */
 	if (*line == '\0')
 		return;
 
-	/* Strip off any leading blanks */
-	while (isblank(*line))
-		line++;
-
 	/* Lines starting with '#' or ';' are comments.  */
 	if (*line == '#' || *line == ';')
 		return;
@@ -495,7 +495,7 @@ conf_readfile(const char *path)
 {
 	struct stat sb;
 	if (!path) {
-		xlog_err("conf_readfile: no path given");
+		xlog(L_ERROR, "conf_readfile: no path given");
 		return NULL;
 	}
 
@@ -640,7 +640,7 @@ conf_get_num(const char *section, const char *tag, int def)
 /*
  * Return the Boolean value denoted by TAG in section SECTION, or DEF
  * if that tags does not exist.
- * FALSE is returned for case-insensitve comparisons with 0, f, false, n, no, off
+ * FALSE is returned for case-insensitive comparisons with 0, f, false, n, no, off
  * TRUE is returned for 1, t, true, y, yes, on
  * A failure to match one of these results in DEF
  */
@@ -727,6 +727,8 @@ retry:
 			continue;
 		if (arg && (cb->arg == NULL || strcasecmp(arg, cb->arg) != 0))
 			continue;
+		if (!arg && cb->arg)
+			continue;
 		if (strcasecmp(tag, cb->tag) != 0)
 			continue;
 		if (cb->value[0] == '$') {
@@ -1321,3 +1323,624 @@ cleanup:
 	}
 	return;
 }
+
+/* struct and queue for buffing output lines */
+TAILQ_HEAD(tailhead, outbuffer);
+
+struct outbuffer {
+	TAILQ_ENTRY(outbuffer) link;
+	char *text;
+};
+
+static struct outbuffer *
+make_outbuffer(char *line)
+{
+	struct outbuffer *new;
+
+	if (line == NULL)
+		return NULL;
+
+	new = calloc(1, sizeof(struct outbuffer));
+	if (new == NULL) {
+		xlog(L_ERROR, "malloc error creating outbuffer");
+		return NULL;
+	}
+	new->text = line;
+	return new;
+}
+
+/* compose a properly escaped tag=value line */
+static char *
+make_tagline(const char *tag, const char *value)
+{
+	char *line;
+	int ret;
+
+	if (!value)
+		return NULL;
+
+	if (should_escape(value))
+		ret = asprintf(&line, "%s = \"%s\"\n", tag, value);
+	else
+		ret = asprintf(&line, "%s = %s\n", tag, value);
+
+	if (ret == -1) {
+		xlog(L_ERROR, "malloc error composing a tag line");
+		return NULL;
+	}
+	return line;
+}
+
+/* compose a section header line */
+static char *
+make_section(const char *section, const char *arg)
+{
+	char *line;
+	int ret;
+
+	if (arg)
+		ret = asprintf(&line, "[%s \"%s\"]\n", section, arg);
+	else
+		ret = asprintf(&line, "[%s]\n", section);
+
+	if (ret == -1) {
+		xlog(L_ERROR, "malloc error composing section header");
+		return NULL;
+	}
+	return line;
+}
+
+/* does the supplied line contain the named section header */
+static bool
+is_section(const char *line, const char *section, const char *arg)
+{
+	char *end;
+	char *name;
+	char *sub;
+	bool found = false;
+
+	/* skip leading white space */
+	while (*line == '[' || isspace(*line))
+		line++;
+
+	name = strdup(line);
+	if (name == NULL) {
+		xlog_warn("conf_write: malloc failed ");
+		return false;
+	}
+
+	/* find the end */
+	end = strchr(name, ']');
+
+	/* malformed line */
+	if (end == NULL) {
+		xlog_warn("conf_write: warning: malformed section name");
+		goto cleanup;
+	}
+
+	while (*end && ( *end == ']' || isblank(*end)))
+		*(end--) = '\0';
+
+	/* is there a subsection name (aka arg) */
+	sub = strchr(name, '"');
+	if (sub) {
+		end = sub - 1;
+		*(sub++) = '\0';
+
+		/* trim whitespace between section name and arg */
+		while (end > name && isblank(*end))
+			*(end--) = '\0';
+
+		/* trim off closing quote */
+		end = strchr(sub, '"');
+		if (end == NULL) {
+			xlog_warn("conf_write: warning: malformed sub-section name");
+			goto cleanup;
+		}
+		*end = '\0';
+	}
+
+	/* ready to compare */
+	if (strcasecmp(section, name)!=0)
+		goto cleanup;
+
+	if (arg != NULL) {
+		if (sub == NULL || strcasecmp(arg, sub)!=0)
+			goto cleanup;
+	} else {
+		if (sub != NULL)
+			goto cleanup;
+	}
+
+	found = true;
+
+cleanup:
+	free(name);
+	return found;
+}
+
+/* check that line contains the specified tag assignment */
+static bool
+is_tag(const char *line, const char *tagname)
+{
+	char *end;
+	char *name;
+	bool found = false;
+
+	/* quick check, is this even an assignment line */
+	end = strchr(line, '=');
+	if (end == NULL)
+		return false;
+
+	/* skip leading white space before tag name */
+	while (isblank(*line))
+		line++;
+
+	name = strdup(line);
+	if (name == NULL) {
+		xlog_warn("conf_write: malloc failed");
+		return false;
+	}
+
+	/* trim any newline characters */
+	end = strchr(name, '\n');
+	if (end)
+		*end = '\0';
+	end = strchr(name, '\r');
+	if (end)
+		*end = '\0';
+
+	/* find the assignment equals sign */
+	end = strchr(name, '=');
+
+	/* malformed line, i swear the equals was there earlier */
+	if (end == NULL) {
+		xlog_warn("conf_write: warning: malformed tag name");
+		goto cleanup;
+	}
+
+	/* trim trailing whitespace after tag name */
+	do {
+		*(end--) = '\0';
+	}while (end > name && *end && isblank(*end));
+
+	/* quoted string, take contents of quotes only */
+	if (*name == '"') {
+		char * new = strdup(name+1);
+		end = strchr(new, '"');
+		if (end != NULL) {
+			*end = 0;
+			free(name);
+			name = new;
+		} else {
+			free(new);
+		}
+	}
+
+	/* now compare */
+	if (strcasecmp(tagname, name) == 0)
+		found = true;
+
+cleanup:
+	free(name);
+	return found;
+}
+
+/* is this an empty line ? */
+static bool
+is_empty(const char *line)
+{
+	const char *p = line;
+
+	if (line == NULL)
+		return true;
+	if (*line == '\0')
+		return true;
+
+	while (*p != '\0' && isspace(*p))
+		p++;
+
+	if (*p == '\0')
+		return true;
+
+	return false;
+}
+
+/* is this line just a comment ? */
+static bool
+is_comment(const char *line)
+{
+	if (line == NULL)
+		return false;
+
+	while (isblank(*line))
+		line++;
+
+	if (*line == '#')
+		return true;
+
+	return false;
+}
+
+/* delete a buffer queue whilst optionally outputting to file */
+static int
+flush_outqueue(struct tailhead *queue, FILE *fout)
+{
+	int ret = 0;
+	while (queue->tqh_first != NULL) {
+		struct outbuffer *ob = queue->tqh_first;
+		TAILQ_REMOVE(queue, ob, link);
+		if (ob->text) {
+			if (fout) {
+				ret = fprintf(fout, "%s", ob->text);
+				if (ret == -1) {
+					xlog(L_ERROR, "Error writing to config file: %s",
+						 strerror(errno));
+					fout = NULL;
+				}
+			}
+			free(ob->text);
+		}
+		free(ob);
+	}
+	if (ret == -1)
+		return 1;
+	return 0;
+}
+
+/* read one line of text from a file, growing the buffer as necessary */
+static int
+read_line(char **buff, int *buffsize, FILE *in)
+{
+	char *readp;
+	int used = 0;
+	bool again = false;
+
+	/* make sure we have a buffer to read into */
+	if (*buff == NULL) {
+		*buffsize = 4096;
+		*buff = calloc(1, *buffsize);
+		if (*buff == NULL) {
+			xlog(L_ERROR, "malloc error for read buffer");
+			return -1;
+		}
+	}
+
+	readp = *buff;
+
+	do {
+		int len;
+
+		/* read in a chunk */
+		if (fgets(readp, *buffsize-used, in)==NULL)
+			return -1;
+
+		len = strlen(*buff);
+		if (len == 0)
+			return -1;
+
+		/* was this the end of a line, or partial read */
+		readp = *buff + len - 1;
+
+		if (*readp != '\n' && *readp !='\r') {
+			/* no nl/cr must be partial read, go again */
+			readp++;
+			again = true;
+		} else {
+			/* that was a normal end of line */
+			again = false;
+		}
+
+		/* do we need more space */
+		if (again && *buffsize - len < 1024) {
+			int offset = readp - *buff;
+			char *newbuff;
+			*buffsize += 4096;
+			newbuff = realloc(*buff, *buffsize);
+			if (newbuff == NULL) {
+				xlog(L_ERROR, "malloc error reading line");
+				return -1;
+			}
+			*buff = newbuff;
+			readp = newbuff + offset;
+		}
+	} while(again);
+	return 0;
+}
+
+/* append a line to the given location in the queue */
+static int
+append_line(struct tailhead *queue, struct outbuffer *entry, char *line)
+{
+	int ret = 0;
+	char *end;
+	bool splitmode = false;
+	char *start = line;
+
+	if (line == NULL)
+		return -1;
+
+	/* if there are \n's in the middle of the string
+	 * then we need to split it into folded lines */
+	do {
+		char *thisline;
+		struct outbuffer *qbuff;
+
+		end = strchr(start, '\n');
+		if (end && *(end+1) != '\0') {
+			*end = '\0';
+
+			ret = asprintf(&thisline, "%s\\\n", start);
+			if (ret == -1) {
+				xlog(L_ERROR, "malloc error composing output");
+				return -1;
+			}
+			splitmode = true;
+			start = end+1;
+		} else {
+			end = NULL;
+			if (splitmode) {
+				thisline = strdup(start);
+				if (thisline == NULL)
+					return -1;
+			} else {
+				thisline = start;
+			}
+		}
+
+		qbuff = make_outbuffer(thisline);
+		if (qbuff == NULL)
+			return -1;
+
+		if (entry) {
+			TAILQ_INSERT_AFTER(queue, entry, qbuff, link);
+			entry = TAILQ_NEXT(entry, link);
+		} else {
+			TAILQ_INSERT_TAIL(queue, qbuff, link);
+		}
+	}while (end != NULL);
+
+	/* we malloced copies of this, so free the original */
+	if (splitmode)
+		free(line);
+
+	return 0;
+}
+
+/* is this a "folded" line, i.e. ends in backslash */
+static bool
+is_folded(const char *line)
+{
+	const char *end;
+	if (line == NULL)
+		return false;
+
+	end = line + strlen(line);
+	while (end > line) {
+		end--;
+		if (*end != '\n' && *end != '\r')
+			break;
+	}
+
+	if (*end == '\\')
+		return true;
+
+	return false;
+}
+
+/***
+ * Write a value to an nfs.conf style filename
+ *
+ * create the file if it doesnt already exist
+ * if value==NULL removes the setting (if present)
+ */
+int
+conf_write(const char *filename, const char *section, const char *arg,
+	   const char *tag, const char *value)
+{
+	int fdout = -1;
+	char *outpath = NULL;
+	FILE *outfile = NULL;
+	FILE *infile = NULL;
+	int ret = 1;
+	struct tailhead outqueue;
+	char * buff = NULL;
+	int buffsize = 0;
+
+	TAILQ_INIT(&outqueue);
+
+	if (!filename) {
+		xlog_warn("conf_write: no filename supplied");
+		return ret;
+	}
+
+	if (!section || !tag) {
+		xlog_warn("conf_write: section or tag name missing");
+		return ret;
+	}
+
+	if (asprintf(&outpath, "%s.XXXXXX", filename) == -1) {
+		xlog(L_ERROR, "conf_write: error composing temp filename");
+		return ret;
+	}
+
+	fdout = mkstemp(outpath);
+	if (fdout < 0) {
+		xlog(L_ERROR, "conf_write: open temp file %s failed: %s",
+			 outpath, strerror(errno));
+		goto cleanup;
+	}
+
+	outfile = fdopen(fdout, "w");
+	if (!outfile) {
+		xlog(L_ERROR, "conf_write: fdopen temp file failed: %s",
+			 strerror(errno));
+		goto cleanup;
+	}
+
+	infile = fopen(filename, "r");
+	if (!infile) {
+		if (!value) {
+			xlog_warn("conf_write: config file \"%s\" not found, nothing to do", filename);
+			ret = 0;
+			goto cleanup;
+		}
+
+		xlog_warn("conf_write: config file \"%s\" not found, creating.", filename);
+		if (append_line(&outqueue, NULL, make_section(section, arg)))
+			goto cleanup;
+
+		if (append_line(&outqueue, NULL, make_tagline(tag, value)))
+			goto cleanup;
+
+		if (flush_outqueue(&outqueue, outfile))
+			goto cleanup;
+	} else {
+		bool found = false;
+		int err = 0;
+
+		buffsize = 4096;
+		buff = calloc(1, buffsize);
+		if (buff == NULL) {
+			xlog(L_ERROR, "malloc error for read buffer");
+			goto cleanup;
+		}
+
+		buff[0] = '\0';
+		do {
+			struct outbuffer *where = NULL;
+
+			/* read in one section worth of lines */
+			do {
+				if (*buff != '\0') {
+					if (append_line(&outqueue, NULL, strdup(buff)))
+						goto cleanup;
+				}
+
+				err = read_line(&buff, &buffsize, infile);
+			} while (err == 0 && buff[0] != '[');
+
+			/* find the section header */
+			where = TAILQ_FIRST(&outqueue);
+			while (where != NULL) {
+				if (where->text != NULL && where->text[0] == '[')
+					break;
+				where = TAILQ_NEXT(where, link);
+			}
+
+			/* this is the section we care about */
+			if (where != NULL && is_section(where->text, section, arg)) {
+				/* is there an existing assignment */
+				while ((where = TAILQ_NEXT(where, link)) != NULL) {
+					if (is_tag(where->text, tag)) {
+						found = true;
+						break;
+					}
+				}
+
+				if (found) {
+					struct outbuffer *prev = TAILQ_PREV(where, tailhead, link);
+					bool again = false;
+
+					/* remove current tag */
+					do {
+						struct outbuffer *next = TAILQ_NEXT(where, link);
+						TAILQ_REMOVE(&outqueue, where, link);
+						if (is_folded(where->text))
+							again = true;
+						else
+							again = false;
+						free(where->text);
+						free(where);
+						where = next;
+					} while(again && where != NULL);
+
+					/* insert new tag */
+					if (value) {
+						if (append_line(&outqueue, prev, make_tagline(tag, value)))
+							goto cleanup;
+					}
+				} else
+				/* no existing assignment found and we need to add one */
+				if (value) {
+					/* rewind past blank lines and comments */
+					struct outbuffer *tail = TAILQ_LAST(&outqueue, tailhead);
+
+					/* comments immediately before a section usually relate
+					 * to the section below them */
+					while (tail != NULL && is_comment(tail->text))
+						tail = TAILQ_PREV(tail, tailhead, link);
+
+					/* there is usually blank line(s) between sections */
+					while (tail != NULL && is_empty(tail->text))
+						tail = TAILQ_PREV(tail, tailhead, link);
+
+					/* now add the tag here */
+					if (append_line(&outqueue, tail, make_tagline(tag, value)))
+						goto cleanup;
+
+					found = true;
+				}
+			}
+
+			/* EOF and correct section not found, so add one */
+			if (err && !found && value) {
+				/* did the last section end in a blank line */
+				struct outbuffer *tail = TAILQ_LAST(&outqueue, tailhead);
+				if (tail && !is_empty(tail->text)) {
+					/* no, so add one for clarity */
+					if (append_line(&outqueue, NULL, strdup("\n")))
+						goto cleanup;
+				}
+
+				/* add the new section header */
+				if (append_line(&outqueue, NULL, make_section(section, arg)))
+					goto cleanup;
+
+				/* now add the tag */
+				if (append_line(&outqueue, NULL, make_tagline(tag, value)))
+					goto cleanup;
+			}
+
+			/* we are done with this section, write it out */
+			if (flush_outqueue(&outqueue, outfile))
+				goto cleanup;
+		} while(err == 0);
+	}
+
+	if (infile) {
+		fclose(infile);
+		infile = NULL;
+	}
+
+	fdout = -1;
+	if (fclose(outfile)) {
+		xlog(L_ERROR, "Error writing config file: %s", strerror(errno));
+		goto cleanup;
+	}
+
+	/* now swap the old file for the new one */
+	if (rename(outpath, filename)) {
+		xlog(L_ERROR, "Error updating config file: %s: %s\n", filename, strerror(errno));
+		ret = 1;
+	} else {
+		ret = 0;
+		free(outpath);
+		outpath = NULL;
+	}
+
+cleanup:
+	flush_outqueue(&outqueue, NULL);
+
+	if (buff)
+		free(buff);
+	if (infile)
+		fclose(infile);
+	if (fdout != -1)
+		close(fdout);
+	if (outpath) {
+		unlink(outpath);
+		free(outpath);
+	}
+	return ret;
+}
diff --git a/support/nfsidmap/nfsidmap_common.c b/support/nfsidmap/nfsidmap_common.c
index 5242c7e..f89b82e 100644
--- a/support/nfsidmap/nfsidmap_common.c
+++ b/support/nfsidmap/nfsidmap_common.c
@@ -57,8 +57,10 @@ struct conf_list *get_local_realms(void)
 			return NULL;
 
 		node->field = calloc(1, NFS4_MAX_DOMAIN_LEN);
-		if (node->field == NULL)
+		if (node->field == NULL) {
+			free(node);
 			return NULL;
+		}
 
 		nfs4_get_default_domain(NULL, node->field, NFS4_MAX_DOMAIN_LEN);
 		toupper_str(node->field);
diff --git a/support/nfsidmap/umich_ldap.c b/support/nfsidmap/umich_ldap.c
index 0e31b1c..b661110 100644
--- a/support/nfsidmap/umich_ldap.c
+++ b/support/nfsidmap/umich_ldap.c
@@ -1125,9 +1125,9 @@ umichldap_init(void)
 
 	/* Verify required information is supplied */
 	if (server_in == NULL || strlen(server_in) == 0)
-		strncat(missing_msg, "LDAP_server ", sizeof(missing_msg));
+		strncat(missing_msg, "LDAP_server ", sizeof(missing_msg)-1);
 	if (ldap_info.base == NULL || strlen(ldap_info.base) == 0)
-		strncat(missing_msg, "LDAP_base ", sizeof(missing_msg));
+		strncat(missing_msg, "LDAP_base ", sizeof(missing_msg)-1);
 	if (strlen(missing_msg) != 0) {
 		IDMAP_LOG(0, ("umichldap_init: Missing required information: "
 			  "%s", missing_msg));
diff --git a/systemd/rpc-pipefs-generator.c b/systemd/rpc-pipefs-generator.c
index 6e1d69c..0b5da11 100644
--- a/systemd/rpc-pipefs-generator.c
+++ b/systemd/rpc-pipefs-generator.c
@@ -35,7 +35,10 @@ static int generate_mount_unit(const char *pipefs_path, const char *pipefs_unit,
 	sprintf(path, "%s/%s", dirname, pipefs_unit);
 	f = fopen(path, "w");
 	if (!f)
+	{
+		free(path);
 		return 1;
+	}
 
 	fprintf(f, "# Automatically generated by rpc-pipefs-generator\n\n[Unit]\n");
 	fprintf(f, "Description=RPC Pipe File System\n");
@@ -48,6 +51,7 @@ static int generate_mount_unit(const char *pipefs_path, const char *pipefs_unit,
 	fprintf(f, "Type=rpc_pipefs\n");
 
 	fclose(f);
+	free(path);
 	return 0;
 }
 
@@ -76,12 +80,16 @@ int generate_target(char *pipefs_path, const char *dirname)
 	strcat(path, filebase);
 	f = fopen(path, "w");
 	if (!f)
+	{
+		free(path);
 		return 1;
+	}
 
 	fprintf(f, "# Automatically generated by rpc-pipefs-generator\n\n[Unit]\n");
 	fprintf(f, "Requires=%s\n", pipefs_unit);
 	fprintf(f, "After=%s\n", pipefs_unit);
 	fclose(f);
+	free(path);
 
 	return 0;
 }
diff --git a/tools/nfsconf/nfsconf.man b/tools/nfsconf/nfsconf.man
index 5b5e914..1ae8543 100644
--- a/tools/nfsconf/nfsconf.man
+++ b/tools/nfsconf/nfsconf.man
@@ -28,6 +28,25 @@ nfsconf \- Query various NFS configuration settings
 .IR subsection ]
 .IR section
 .IR tag
+.P
+.B nfsconf \-\-set
+.RB [ \-v | \-\-verbose ]
+.RB [ \-f | \-\-file
+.IR infile.conf ]
+.RB [ \-a | \-\-arg
+.IR subsection ]
+.IR section
+.IR tag
+.IR value
+.P
+.B nfsconf \-\-unset
+.RB [ \-v | \-\-verbose ]
+.RB [ \-f | \-\-file
+.IR infile.conf ]
+.RB [ \-a | \-\-arg
+.IR subsection ]
+.IR section
+.IR tag
 .SH DESCRIPTION
 The
 .B nfsconf
@@ -41,6 +60,10 @@ Output an alphabetically sorted dump of the current configuration in conf file f
 Test if a specific tag has a value set.
 .IP "\fB\-g, \-\-get\fP"
 Output the current value of the specified tag.
+.IP "\fB\-s, \-\-set\fP"
+Update or Add a tag and value to the config file, creating the file if necessary.
+.IP "\fB\-u, \-\-unset\fP"
+Remove the specified tag and its value from the config file.
 .SH OPTIONS
 .SS Options valid in all modes
 .TP
@@ -56,7 +79,7 @@ Select a different config file to operate upon, default is
 Select a specific sub-section
 .SH EXIT STATUS
 .SS \fB\-\-isset\fR mode
-In this mode the command will return success (0) if the selected tag has a value, any other exit code indicates the value is not set, or some other error has occured.
+In this mode the command will return success (0) if the selected tag has a value, any other exit code indicates the value is not set, or some other error has occurred.
 .SS all other modes
 Success is indicated by an exit status of zero, any other status indicates an error. Error messages are output on stderr, and increasing verbosity will give more detailed explanations if any are available.
 .SH EXAMPLES
@@ -64,11 +87,14 @@ Success is indicated by an exit status of zero, any other status indicates an er
 .B nfsconf -v --dump --file /tmp/testconf.conf  sorted.conf
 Check a new config file for syntax errors and output a sorted version for ease of comparison with existing settings.
 .TP
-.B if ! nfsconf --isset gssd preferred-realm ; then echo 'No prefered realm configured for gss'; fi
+.B if ! nfsconf --isset gssd preferred-realm ; then echo 'No preferred realm configured for gss'; fi
 The tool allows for easy testing of configuration values from shell scripts, here we test if a specific value has been set.
 .TP
 .B nfsconf --file /etc/nfsmount.conf --get --arg /home MountPoint background
 Show default value for \fIbackground\fR option for NFS mounts of the \fI/home\fR path.
+.TP
+.B nfsconf --file /etc/nfs.conf --set nfsd debug 1
+Enable debugging in nfsd
 .SH FILES
 .TP
 .B /etc/nfs.conf
diff --git a/tools/nfsconf/nfsconfcli.c b/tools/nfsconf/nfsconfcli.c
index 034ec51..f98d0d1 100644
--- a/tools/nfsconf/nfsconfcli.c
+++ b/tools/nfsconf/nfsconfcli.c
@@ -12,7 +12,9 @@ typedef enum {
 	MODE_NONE,
 	MODE_GET,
 	MODE_ISSET,
-	MODE_DUMP
+	MODE_DUMP,
+	MODE_SET,
+	MODE_UNSET
 } confmode_t;
 
 static void usage(const char *name)
@@ -29,11 +31,14 @@ static void usage(const char *name)
 	fprintf(stderr, "      Output one specific config value\n");
 	fprintf(stderr, "  --isset [--arg subsection] {section} {tag}\n");
 	fprintf(stderr, "      Return code indicates if config value is present\n");
+	fprintf(stderr, "  --set [--arg subsection] {section} {tag} {value}\n");
+	fprintf(stderr, "      Set and Write a config value\n");
+	fprintf(stderr, "  --unset [--arg subsection] {section} {tag}\n");
+	fprintf(stderr, "      Remove an existing config value\n");
 }
 
 int main(int argc, char **argv)
 {
-	const char * val;
 	char * confpath = NFS_CONFFILE;
 	char * arg = NULL;
 	int verbose=0;
@@ -47,6 +52,8 @@ int main(int argc, char **argv)
 		int index = 0;
 		struct option long_options[] = {
 			{"get",		no_argument, 0, 'g' },
+			{"set",		no_argument, 0, 's' },
+			{"unset",	no_argument, 0, 'u' },
 			{"arg",	  required_argument, 0, 'a' },
 			{"isset", 	no_argument, 0, 'i' },
 			{"dump",  optional_argument, 0, 'd' },
@@ -55,14 +62,14 @@ int main(int argc, char **argv)
 			{NULL,			  0, 0, 0 }
 		};
 
-		c = getopt_long(argc, argv, "ga:id::f:v", long_options, &index);
+		c = getopt_long(argc, argv, "gsua:id::f:v", long_options, &index);
 		if (c == -1) break;
 
 		switch (c) {
 			case 0:
 				break;
 			case 'f':
-				/* user specified souce path for config */
+				/* user specified source path for config */
 				confpath = optarg;
 				break;
 			case 'a':
@@ -75,6 +82,12 @@ int main(int argc, char **argv)
 			case 'g':
 				mode = MODE_GET;
 				break;
+			case 's':
+				mode = MODE_SET;
+				break;
+			case 'u':
+				mode = MODE_UNSET;
+				break;
 			case 'i':
 				mode = MODE_ISSET;
 				break;
@@ -97,7 +110,7 @@ int main(int argc, char **argv)
 		xlog_config(D_ALL, 1);
 	xlog_stderr(1);
 	xlog_syslog(0);
-	xlog_open("nfsconftool");
+	xlog_open("nfsconf");
 
 	if (mode == MODE_NONE) {
 		fprintf(stderr, "Error: No MODE selected.\n");
@@ -105,14 +118,18 @@ int main(int argc, char **argv)
 		return 1;
 	}
 
-	if (conf_init_file(confpath)) {
-		/* config file was missing or had an error, warn about it */
-		if (verbose || mode != MODE_ISSET)
-			fprintf(stderr, "Error loading config file %s\n",
-				confpath);
-		/* this isnt fatal for --isset */
-		if (mode != MODE_ISSET)
-			return 1;
+	if (mode != MODE_SET && mode != MODE_UNSET) {
+		if (conf_init_file(confpath)) {
+			/* config file was missing or had an error, warn about it */
+			if (verbose || mode != MODE_ISSET) {
+				fprintf(stderr, "Error loading config file %s\n",
+					confpath);
+			}
+
+			/* this isnt fatal for --isset */
+			if (mode != MODE_ISSET)
+				return 1;
+		}
 	}
 
 	/* --dump mode, output the current configuration */
@@ -144,6 +161,7 @@ int main(int argc, char **argv)
 	if (mode == MODE_GET || mode == MODE_ISSET) {
 		char * section = NULL;
 		char * tag = NULL;
+		const char * val;
 
 		/* test they supplied section and tag names */
 		if (optind+1 >= argc) {
@@ -169,8 +187,43 @@ int main(int argc, char **argv)
 				fprintf(stderr, "Tag '%s' not found\n", tag);
 			ret = 1;
 		}
+	} else
+	if (mode == MODE_SET || mode == MODE_UNSET) {
+		char * section = NULL;
+		char * tag = NULL;
+		char * val = NULL;
+		int need = 2;
+
+		if (mode == MODE_UNSET)
+			need = 1;
+
+		/* test they supplied section and tag names */
+		if (optind+need >= argc) {
+			fprintf(stderr, "Error: insufficient arguments for mode\n");
+			usage(argv[0]);
+			ret = 2;
+			goto cleanup;
+		}
+
+		/* now we have a section and tag name */
+		section = argv[optind++];
+		tag = argv[optind++];
+		if (mode == MODE_SET)
+			val = argv[optind++];
+
+		/* setting an empty string is same as unsetting */
+		if (val!=NULL && *val == '\0') {
+			mode = MODE_UNSET;
+			val = NULL;
+		}
+
+		if (conf_write(confpath, section, arg, tag, val)) {
+			if (verbose)
+				fprintf(stderr, "Error writing config\n");
+			ret = 1;
+		}
 	} else {
-		fprintf(stderr, "Mode not yet implimented.\n");
+		fprintf(stderr, "Mode not yet implemented.\n");
 		ret = 2;
 	}
 
diff --git a/utils/exportfs/exports.man b/utils/exportfs/exports.man
index 4f95f3a..e3a16f6 100644
--- a/utils/exportfs/exports.man
+++ b/utils/exportfs/exports.man
@@ -131,10 +131,12 @@ this way are ro, rw, no_root_squash, root_squash, and all_squash.
 understands the following export options:
 .TP
 .IR secure
-This option requires that requests originate on an Internet port less
-than IPPORT_RESERVED (1024). This option is on by default. To turn it
-off, specify
+This option requires that requests not using gss originate on an
+Internet port less than IPPORT_RESERVED (1024). This option is on by default.
+To turn it off, specify
 .IR insecure .
+(NOTE: older kernels (before upstream kernel version 4.17) enforced this
+requirement on gss requests as well.)
 .TP
 .IR rw
 Allow both read and write requests on this NFS volume. The
diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c
index 2c14e5f..7b21ee2 100644
--- a/utils/gssd/gssd.c
+++ b/utils/gssd/gssd.c
@@ -279,16 +279,16 @@ gssd_read_service_info(int dirfd, struct clnt_info *clp)
 	 * (commit bf19aacecbeebccb2c3d150a8bd9416b7dba81fe)
 	 */
 	numfields = fscanf(info,
-			   "RPC server: %ms\n"
-			   "service: %ms (%d) version %d\n"
-			   "address: %ms\n"
-			   "protocol: %ms\n"
-			   "port: %ms\n",
-			   &server,
-			   &service, &program, &version,
-			   &address,
-			   &protoname,
-			   &port);
+			   "RPC server: %s\n"
+			   "service: %s (%d) version %d\n"
+			   "address: %s\n"
+			   "protocol: %s\n"
+			   "port: %s\n",
+			   (char *)&server,
+			   (char *)&service, &program, &version,
+			   (char *)&address,
+			   (char *)&protoname,
+			   (char *)&port);
 
 
 	switch (numfields) {
diff --git a/utils/gssd/gssd_proc.c b/utils/gssd/gssd_proc.c
index ce73777..8767e26 100644
--- a/utils/gssd/gssd_proc.c
+++ b/utils/gssd/gssd_proc.c
@@ -520,8 +520,9 @@ out:
 }
 
 static AUTH *
-krb5_use_machine_creds(struct clnt_info *clp, uid_t uid, char *tgtname,
-		    char *service, CLIENT **rpc_clnt)
+krb5_use_machine_creds(struct clnt_info *clp, uid_t uid,
+		       char *srchost, char *tgtname, char *service,
+		       CLIENT **rpc_clnt)
 {
 	AUTH	*auth = NULL;
 	char	**credlist = NULL;
@@ -534,7 +535,7 @@ krb5_use_machine_creds(struct clnt_info *clp, uid_t uid, char *tgtname,
 
 	do {
 		gssd_refresh_krb5_machine_credential(clp->servername, NULL,
-						service);
+						     service, srchost);
 	/*
 	 * Get a list of credential cache names and try each
 	 * of them until one works or we've tried them all
@@ -594,8 +595,8 @@ out:
  * context on behalf of the kernel
  */
 static void
-process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname,
-		    char *service)
+process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *srchost,
+		    char *tgtname, char *service)
 {
 	CLIENT			*rpc_clnt = NULL;
 	AUTH			*auth = NULL;
@@ -643,7 +644,7 @@ process_krb5_upcall(struct clnt_info *clp, uid_t uid, int fd, char *tgtname,
 	if (auth == NULL) {
 		if (uid == 0 && (root_uses_machine_creds == 1 ||
 				service != NULL)) {
-			auth =	krb5_use_machine_creds(clp, uid, tgtname,
+			auth =	krb5_use_machine_creds(clp, uid, srchost, tgtname,
 							service, &rpc_clnt);
 			if (auth == NULL)
 				goto out_return_error;
@@ -714,7 +715,7 @@ handle_krb5_upcall(struct clnt_upcall_info *info)
 
 	printerr(2, "\n%s: uid %d (%s)\n", __func__, info->uid, clp->relpath);
 
-	process_krb5_upcall(clp, info->uid, clp->krb5_fd, NULL, NULL);
+	process_krb5_upcall(clp, info->uid, clp->krb5_fd, NULL, NULL, NULL);
 	free(info);
 }
 
@@ -728,11 +729,12 @@ handle_gssd_upcall(struct clnt_upcall_info *info)
 	char			*uidstr = NULL;
 	char			*target = NULL;
 	char			*service = NULL;
+	char			*srchost = NULL;
 	char			*enctypes = NULL;
 	char			*upcall_str;
 	char			*pbuf = info->lbuf;
 
-	printerr(2, "\n%s: '%s' (%s)\n", __func__, info->lbuf, clp->relpath);
+	printerr(2, "%s: '%s' (%s)\n", __func__, info->lbuf, clp->relpath);
 
 	upcall_str = strdup(info->lbuf);
 	if (upcall_str == NULL) {
@@ -751,6 +753,8 @@ handle_gssd_upcall(struct clnt_upcall_info *info)
 			target = p + strlen("target=");
 		else if (!strncmp(p, "service=", strlen("service=")))
 			service = p + strlen("service=");
+		else if (!strncmp(p, "srchost=", strlen("srchost=")))
+			srchost = p + strlen("srchost=");
 	}
 
 	if (!mech || strlen(mech) < 1) {
@@ -802,7 +806,7 @@ handle_gssd_upcall(struct clnt_upcall_info *info)
 	}
 
 	if (strcmp(mech, "krb5") == 0 && clp->servername)
-		process_krb5_upcall(clp, uid, clp->gssd_fd, target, service);
+		process_krb5_upcall(clp, uid, clp->gssd_fd, srchost, target, service);
 	else {
 		if (clp->servername)
 			printerr(0, "WARNING: handle_gssd_upcall: "
@@ -815,4 +819,3 @@ out_nomem:
 	free(info);
 	return;
 }
-
diff --git a/utils/gssd/krb5_util.c b/utils/gssd/krb5_util.c
index b342b06..eba1aac 100644
--- a/utils/gssd/krb5_util.c
+++ b/utils/gssd/krb5_util.c
@@ -757,7 +757,8 @@ gssd_search_krb5_keytab(krb5_context context, krb5_keytab kt,
  * the server hostname.
  */
 static int
-find_keytab_entry(krb5_context context, krb5_keytab kt, const char *tgtname,
+find_keytab_entry(krb5_context context, krb5_keytab kt,
+		  const char *srchost, const char *tgtname,
 		  krb5_keytab_entry *kte, const char **svcnames)
 {
 	krb5_error_code code;
@@ -781,7 +782,9 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *tgtname,
 		goto out;
 
 	/* Get full local hostname */
-	if (gethostname(myhostname, sizeof(myhostname)) == -1) {
+	if (srchost) {
+		strcpy(myhostname, srchost);
+	} else if (gethostname(myhostname, sizeof(myhostname)) == -1) {
 		retval = errno;
 		k5err = gssd_k5_err_msg(context, retval);
 		printerr(1, "%s while getting local hostname\n", k5err);
@@ -807,10 +810,12 @@ find_keytab_entry(krb5_context context, krb5_keytab kt, const char *tgtname,
 	        myhostad[i+1] = 0;
 	}
 
-	retval = get_full_hostname(myhostname, myhostname, sizeof(myhostname));
-	if (retval) {
-		/* Don't use myhostname */
-		myhostname[0] = 0;
+	if (!srchost) {
+		retval = get_full_hostname(myhostname, myhostname, sizeof(myhostname));
+		if (retval) {
+			/* Don't use myhostname */
+			myhostname[0] = 0;
+		}
 	}
 
 	code = krb5_get_default_realm(context, &default_realm);
@@ -1140,7 +1145,7 @@ gssd_get_krb5_machine_cred_list(char ***list)
 		if (ple->ccname) {
 			/* Make sure cred is up-to-date before returning it */
 			retval = gssd_refresh_krb5_machine_credential(NULL, ple,
-				NULL);
+								      NULL, NULL);
 			if (retval)
 				continue;
 			if (i + 1 > listsize) {
@@ -1231,7 +1236,7 @@ gssd_destroy_krb5_machine_creds(void)
 int
 gssd_refresh_krb5_machine_credential(char *hostname,
 				     struct gssd_k5_kt_princ *ple, 
-					 char *service)
+				     char *service, char *srchost)
 {
 	krb5_error_code code = 0;
 	krb5_context context;
@@ -1240,6 +1245,9 @@ gssd_refresh_krb5_machine_credential(char *hostname,
 	char *k5err = NULL;
 	const char *svcnames[] = { "$", "root", "nfs", "host", NULL };
 
+	printerr(2, "%s: hostname=%s ple=%p service=%s srchost=%s\n",
+		__func__, hostname, ple, service, srchost);
+
 	/*
 	 * If a specific service name was specified, use it.
 	 * Otherwise, use the default list.
@@ -1270,7 +1278,8 @@ gssd_refresh_krb5_machine_credential(char *hostname,
 	if (ple == NULL) {
 		krb5_keytab_entry kte;
 
-		code = find_keytab_entry(context, kt, hostname, &kte, svcnames);
+		code = find_keytab_entry(context, kt, srchost, hostname,
+					 &kte, svcnames);
 		if (code) {
 			printerr(0, "ERROR: %s: no usable keytab entry found "
 				 "in keytab %s for connection with host %s\n",
diff --git a/utils/gssd/krb5_util.h b/utils/gssd/krb5_util.h
index e3bbb07..b000b44 100644
--- a/utils/gssd/krb5_util.h
+++ b/utils/gssd/krb5_util.h
@@ -30,7 +30,7 @@ void gssd_free_krb5_machine_cred_list(char **list);
 void gssd_destroy_krb5_machine_creds(void);
 int  gssd_refresh_krb5_machine_credential(char *hostname,
 					  struct gssd_k5_kt_princ *ple, 
-					  char *service);
+					  char *service, char *srchost);
 char *gssd_k5_err_msg(krb5_context context, krb5_error_code code);
 void gssd_k5_get_default_realm(char **def_realm);
 
diff --git a/utils/idmapd/idmapd.c b/utils/idmapd/idmapd.c
index b87c4dd..4811e0f 100644
--- a/utils/idmapd/idmapd.c
+++ b/utils/idmapd/idmapd.c
@@ -310,7 +310,7 @@ main(int argc, char **argv)
 	if (!serverstart && !clientstart)
 		errx(1, "it is illegal to specify both -C and -S");
 
-	strncat(pipefsdir, "/nfs", sizeof(pipefsdir));
+	strncat(pipefsdir, "/nfs", sizeof(pipefsdir)-1);
 
 	daemon_init(fg);
 
@@ -923,7 +923,8 @@ static int
 getfield(char **bpp, char *fld, size_t fldsz)
 {
 	char *bp;
-	int val, n;
+	unsigned int val; 
+	int n;
 
 	while ((bp = strsep(bpp, " ")) != NULL && bp[0] == '\0')
 		;
diff --git a/utils/mount/network.c b/utils/mount/network.c
index e490399..356f663 100644
--- a/utils/mount/network.c
+++ b/utils/mount/network.c
@@ -50,6 +50,8 @@
 #include <rpc/rpc.h>
 #include <rpc/pmap_prot.h>
 #include <rpc/pmap_clnt.h>
+#include <net/if.h>
+#include <ifaddrs.h>
 
 #include "sockaddr.h"
 #include "xcommon.h"
@@ -1759,3 +1761,48 @@ int nfs_umount_do_umnt(struct mount_options *options,
 
 	return EX_SUCCESS;
 }
+
+int nfs_is_inaddr_any(struct sockaddr *nfs_saddr)
+{
+	switch (nfs_saddr->sa_family) {
+	case AF_INET: {
+		if (((struct sockaddr_in *)nfs_saddr)->sin_addr.s_addr ==
+				INADDR_ANY)
+			return 1;
+		break;
+	}
+	case AF_INET6:
+		if (!memcmp(&((struct sockaddr_in6 *)nfs_saddr)->sin6_addr,
+				&in6addr_any, sizeof(in6addr_any)))
+			return 1;
+		break;
+	}
+	return 0;
+}
+
+int nfs_addr_matches_localips(struct sockaddr *nfs_saddr)
+{
+	struct ifaddrs *myaddrs, *ifa;
+	int found = 0;
+
+	/* acquire exiting network interfaces */
+	if (getifaddrs(&myaddrs) != 0)
+		return 0;
+
+	/* interate over the available interfaces and check if we
+	 * we find a match to the supplied clientaddr value
+	 */
+	for (ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next) {
+		if (ifa->ifa_addr == NULL)
+			continue;
+		if (!(ifa->ifa_flags & IFF_UP))
+			continue;
+		if (!memcmp(ifa->ifa_addr, nfs_saddr,
+				sizeof(struct sockaddr))) {
+			found = 1;
+			break;
+		}
+	}
+	freeifaddrs(myaddrs);
+	return found;
+}
diff --git a/utils/mount/network.h b/utils/mount/network.h
index ecaac33..0fc98ac 100644
--- a/utils/mount/network.h
+++ b/utils/mount/network.h
@@ -54,6 +54,8 @@ int nfs_callback_address(const struct sockaddr *, const socklen_t,
 int clnt_ping(struct sockaddr_in *, const unsigned long,
 		const unsigned long, const unsigned int,
 		struct sockaddr_in *);
+int nfs_is_inaddr_any(struct sockaddr *);
+int nfs_addr_matches_localips(struct sockaddr *);
 
 struct mount_options;
 
diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man
index bd1508c..9ee9bd9 100644
--- a/utils/mount/nfs.man
+++ b/utils/mount/nfs.man
@@ -835,6 +835,9 @@ to perform NFS version 4.0 callback requests against
 files on this mount point. If  the  server is unable to
 establish callback connections to clients, performance
 may degrade, or accesses to files may temporarily hang.
+Can specify a value of IPv4_ANY (0.0.0.0) or equivalent
+IPv6 any address which will signal to the NFS server that
+this NFS client does not want delegations.
 .IP
 If this option is not specified, the
 .BR mount (8)
diff --git a/utils/mount/nfs4mount.c b/utils/mount/nfs4mount.c
index 89629ed..3e4f1e2 100644
--- a/utils/mount/nfs4mount.c
+++ b/utils/mount/nfs4mount.c
@@ -218,7 +218,7 @@ int nfs4mount(const char *spec, const char *node, int flags,
 		goto fail;
 	}
 	if (running_bg)
-		strncpy(new_opts, old_opts, sizeof(new_opts));
+		strncpy(new_opts, old_opts, sizeof(new_opts)-1);
 	else
 		snprintf(new_opts, sizeof(new_opts), "%s%saddr=%s",
 			 old_opts, *old_opts ? "," : "", s);
diff --git a/utils/mount/nfsmount.c b/utils/mount/nfsmount.c
index ae4a3da..952a755 100644
--- a/utils/mount/nfsmount.c
+++ b/utils/mount/nfsmount.c
@@ -828,7 +828,7 @@ noauth_flavors:
 
 	data.fd = fsock;
 	memcpy((char *) &data.addr, (char *) nfs_saddr, sizeof(data.addr));
-	strncpy(data.hostname, hostname, sizeof(data.hostname));
+	strncpy(data.hostname, hostname, sizeof(data.hostname)-1);
 
  out_ok:
 	/* Ensure we have enough padding for the following strcat()s */
diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c
index d1b0708..4d2e37e 100644
--- a/utils/mount/stropts.c
+++ b/utils/mount/stropts.c
@@ -229,7 +229,8 @@ static int nfs_append_addr_option(const struct sockaddr *sap,
 
 /*
  * Called to discover our address and append an appropriate 'clientaddr='
- * option to the options string.
+ * option to the options string. If the supplied 'clientaddr=' value does
+ * not match either IPV4/IPv6 any or a local address, then fail the mount.
  *
  * Returns 1 if 'clientaddr=' option created successfully or if
  * 'clientaddr=' option is already present; otherwise zero.
@@ -242,8 +243,27 @@ static int nfs_append_clientaddr_option(const struct sockaddr *sap,
 	struct sockaddr *my_addr = &address.sa;
 	socklen_t my_len = sizeof(address);
 
-	if (po_contains(options, "clientaddr") == PO_FOUND)
+	if (po_contains(options, "clientaddr") == PO_FOUND) {
+		char *addr = po_get(options, "clientaddr");
+		union nfs_sockaddr nfs_address;
+		struct sockaddr *nfs_saddr = &nfs_address.sa;
+		socklen_t nfs_salen = sizeof(nfs_address);
+
+		/* translate the input for clientaddr to nfs_sockaddr */
+		if (!nfs_string_to_sockaddr(addr, nfs_saddr, &nfs_salen))
+			return 0;
+
+		/* check for IPV4_ANY and IPV6_ANY */
+		if (nfs_is_inaddr_any(nfs_saddr))
+			return 1;
+
+		/* check if ip matches local network addresses */
+		if (!nfs_addr_matches_localips(nfs_saddr))
+			nfs_error(_("%s: [warning] supplied clientaddr=%s "
+				"does not match any existing network "
+				"addresses"), progname, addr);
 		return 1;
+	}
 
 	nfs_callback_address(sap, salen, my_addr, &my_len);
 
diff --git a/utils/mountd/mountd.c b/utils/mountd/mountd.c
index 4c68702..086c39b 100644
--- a/utils/mountd/mountd.c
+++ b/utils/mountd/mountd.c
@@ -702,7 +702,7 @@ main(int argc, char **argv)
 	else
 		NFSCTL_TCPUNSET(_rpcprotobits);
 	for (vers = 2; vers <= 4; vers++) {
-		char tag[10];
+		char tag[20];
 		sprintf(tag, "vers%d", vers);
 		if (conf_get_bool("nfsd", tag, NFSCTL_VERISSET(nfs_version, vers)))
 			NFSCTL_VERSET(nfs_version, vers);
diff --git a/utils/mountd/v4root.c b/utils/mountd/v4root.c
index f978f4c..d735dbf 100644
--- a/utils/mountd/v4root.c
+++ b/utils/mountd/v4root.c
@@ -92,7 +92,7 @@ v4root_create(char *path, nfs_export *export)
 
 	dupexportent(&eep, &pseudo_root.m_export);
 	eep.e_hostname = curexp->e_hostname;
-	strncpy(eep.e_path, path, sizeof(eep.e_path));
+	strncpy(eep.e_path, path, sizeof(eep.e_path)-1);
 	if (strcmp(path, "/") != 0)
 		eep.e_flags &= ~NFSEXP_FSID;
 	set_pseudofs_security(&eep, curexp->e_flags);
diff --git a/utils/nfsd/nfsd.c b/utils/nfsd/nfsd.c
index 2303a5d..f048631 100644
--- a/utils/nfsd/nfsd.c
+++ b/utils/nfsd/nfsd.c
@@ -98,7 +98,7 @@ main(int argc, char **argv)
 	else
 		NFSCTL_TCPUNSET(protobits);
 	for (i = 2; i <= 4; i++) {
-		char tag[10];
+		char tag[20];
 		sprintf(tag, "vers%d", i);
 		if (conf_get_bool("nfsd", tag, NFSCTL_VERISSET(versbits, i)))
 			NFSCTL_VERSET(versbits, i);
diff --git a/utils/nfsdcltrack/nfsdcltrack.c b/utils/nfsdcltrack/nfsdcltrack.c
index 76b06d2..b45a904 100644
--- a/utils/nfsdcltrack/nfsdcltrack.c
+++ b/utils/nfsdcltrack/nfsdcltrack.c
@@ -553,7 +553,7 @@ find_cmd(char *cmdname)
 int
 main(int argc, char **argv)
 {
-	char arg;
+	int arg;
 	char *val;
 	int rc = 0;
 	char *progname, *cmdarg = NULL;
diff --git a/utils/nfsidmap/nfsidmap.c b/utils/nfsidmap/nfsidmap.c
index 374bc5d..d3967a3 100644
--- a/utils/nfsidmap/nfsidmap.c
+++ b/utils/nfsidmap/nfsidmap.c
@@ -283,7 +283,7 @@ static int key_invalidate(char *keystr, int keymask)
 {
 	FILE *fp;
 	char buf[BUFSIZ], *ptr;
-	key_serial_t key;
+	unsigned int key;
 	int mask;
 
 	xlog_syslog(0);
diff --git a/utils/nfsstat/nfsstat.c b/utils/nfsstat/nfsstat.c
index c779053..ca84532 100644
--- a/utils/nfsstat/nfsstat.c
+++ b/utils/nfsstat/nfsstat.c
@@ -340,7 +340,7 @@ static struct option longopts[] =
 	{ "all", 0, 0, 'v' },
 	{ "auto", 0, 0, '\3' },
 	{ "client", 0, 0, 'c' },
-	{ "mounted", 0, 0, 'm' },
+	{ "mounts", 0, 0, 'm' },
 	{ "nfs", 0, 0, 'n' },
 	{ "rpc", 0, 0, 'r' },
 	{ "server", 0, 0, 's' },
@@ -1013,7 +1013,7 @@ mounts(const char *name)
 	 * be a fatal error -- it usually means the module isn't loaded.
 	 */
 	if ((fp = fopen(name, "r")) == NULL) {
-		fprintf(stderr, "Warning: %s: %m\n", name);
+		fprintf(stderr, "Warning: %s: %s\n", name, strerror(errno));
 		return 0;
 	}
 
@@ -1089,8 +1089,8 @@ out:
 		fclose(fp);
 	if (err) {
 		if (!other_opt) {
-			fprintf(stderr, "Error: No %s Stats (%s: %m). \n",
-					label, file);
+			fprintf(stderr, "Error: No %s Stats (%s: %s). \n",
+					label, file, strerror(errno));
 			exit(2);
 		}
 		*opt = 0;
diff --git a/utils/statd/sm-notify.c b/utils/statd/sm-notify.c
index 6d19ec1..7a48473 100644
--- a/utils/statd/sm-notify.c
+++ b/utils/statd/sm-notify.c
@@ -569,7 +569,7 @@ usage:		fprintf(stderr,
 		if (name == NULL)
 			exit(1);
 
-		strncpy(nsm_hostname, name, sizeof(nsm_hostname));
+		strncpy(nsm_hostname, name, sizeof(nsm_hostname)-1);
 		free(name);
 	}
 
diff --git a/utils/statd/statd.c b/utils/statd/statd.c
index 563a272..2cc6cf3 100644
--- a/utils/statd/statd.c
+++ b/utils/statd/statd.c
@@ -231,11 +231,12 @@ static void set_nlm_port(char *type, int port)
 	}
 	if (fd >= 0) {
 		if (write(fd, nbuf, strlen(nbuf)) != (ssize_t)strlen(nbuf))
-			fprintf(stderr, "%s: fail to set NLM %s port: %m\n",
-				name_p, type);
+			fprintf(stderr, "%s: fail to set NLM %s port: %s\n",
+				name_p, type, strerror(errno));
 		close(fd);
 	} else
-		fprintf(stderr, "%s: failed to open %s: %m\n", name_p, pathbuf);
+		fprintf(stderr, "%s: failed to open %s: %s\n", 
+			name_p, pathbuf, strerror(errno));
 }
 
 /*