Commit 96a2dae3525ba28cea954d6d445bb2d03c0cff66

Guillem Jover 2013-05-25T15:31:45

Update setmode module from NetBSD Merge some interesting changes.

diff --git a/man/setmode.3 b/man/setmode.3
index ae7c568..5073d43 100644
--- a/man/setmode.3
+++ b/man/setmode.3
@@ -1,3 +1,5 @@
+.\"	$NetBSD: setmode.3,v 1.18.28.1 2009/01/04 17:02:19 christos Exp $
+.\"
 .\" Copyright (c) 1989, 1991, 1993
 .\"	The Regents of the University of California.  All rights reserved.
 .\"
@@ -9,7 +11,7 @@
 .\" 2. Redistributions in binary form must reproduce the above copyright
 .\"    notice, this list of conditions and the following disclaimer in the
 .\"    documentation and/or other materials provided with the distribution.
-.\" 4. Neither the name of the University nor the names of its contributors
+.\" 3. Neither the name of the University nor the names of its contributors
 .\"    may be used to endorse or promote products derived from this software
 .\"    without specific prior written permission.
 .\"
@@ -28,7 +30,7 @@
 .\"     @(#)setmode.3	8.2 (Berkeley) 4/28/95
 .\" $FreeBSD: src/lib/libc/gen/setmode.3,v 1.12 2007/01/09 00:27:55 imp Exp $
 .\"
-.Dd April 28, 1995
+.Dd January 4, 2009
 .Dt SETMODE 3
 .Os
 .Sh NAME
@@ -40,34 +42,40 @@
 .Lb libbsd
 .Sh SYNOPSIS
 .In bsd/unistd.h
-.Ft mode_t
-.Fn getmode "const void *set" "mode_t mode"
 .Ft void *
 .Fn setmode "const char *mode_str"
+.Ft mode_t
+.Fn getmode "const void *set" "mode_t mode"
 .Sh DESCRIPTION
 The
+.Fn setmode
+function accepts a string representation of a file mode change,
+compiles it to binary form, and returns an abstract representation
+that may be passed to
+.Fn getmode .
+The string may be an numeric (octal) or symbolic string of the form
+accepted by
+.Xr chmod 1 ,
+and may represent either an exact mode to set or a change to make to
+the existing mode.
+.Pp
+The
 .Fn getmode
 function
-returns a copy of the file permission bits
+adjusts the file permission bits given by
 .Fa mode
-as altered by the values pointed to by
-.Fa set .
-While only the mode bits are altered, other parts of the file mode
-may be examined.
+according to the compiled change representation
+.Fa set ,
+and returns the adjusted mode.
+While only the permission bits are altered, other parts of the file
+mode, particularly the type, may be examined.
 .Pp
-The
-.Fn setmode
-function
-takes an absolute (octal) or symbolic value, as described in
-.Xr chmod 1 ,
-as an argument
-and returns a pointer to mode values to be supplied to
-.Fn getmode .
-Because some of the symbolic values are relative to the file
-creation mask,
+Because some of the possible symbolic values are defined relative to
+the file creation mask,
 .Fn setmode
 may call
-.Xr umask 2 .
+.Xr umask 2 ,
+temporarily changing the mask.
 If this occurs, the file creation mask will be restored before
 .Fn setmode
 returns.
@@ -75,13 +83,13 @@ If the calling program changes the value of its file creation mask
 after calling
 .Fn setmode ,
 .Fn setmode
-must be called again if
+must be called again to recompile the mode string if
 .Fn getmode
 is to modify future file modes correctly.
 .Pp
 If the mode passed to
 .Fn setmode
-is invalid or if memory cannot be allocated for the return value,
+is invalid,
 .Fn setmode
 returns
 .Dv NULL .
@@ -94,13 +102,40 @@ and should be returned to the system with
 .Fn free
 when the program is done with it, generally after a call to
 .Fn getmode .
+.Sh EXAMPLES
+The effects of the shell command
+.Ql "chmod a+x myscript.sh"
+can be duplicated as follows:
+.Bd -literal -offset indent
+const char *file = "myscript.sh";
+struct stat st;
+mode_t newmode;
+
+stat(file, \*[Am]st);
+newmode = getmode(setmode("a+x"), st.st_mode);
+chmod(file, newmode);
+.Ed
 .Sh ERRORS
 The
 .Fn setmode
 function
-may fail and set errno for any of the errors specified for the library
-routine
-.Xr malloc 3 .
+may fail and set
+.Va errno
+for any of the errors specified for the library routines
+.Xr malloc 3
+or
+.Xr strtol 3 .
+In addition,
+.Fn setmode
+will fail and set
+.Va errno
+to:
+.Bl -tag -width Er
+.It Bq Er EINVAL
+The
+.Fa mode
+argument does not represent a valid mode.
+.El
 .Sh SEE ALSO
 .Xr chmod 1 ,
 .Xr stat 2 ,
@@ -113,3 +148,9 @@ and
 .Fn setmode
 functions first appeared in
 .Bx 4.4 .
+.Sh BUGS
+The type of
+.Fa set
+should really be some opaque struct type used only by these functions
+rather than
+.Ft void * .
diff --git a/src/setmode.c b/src/setmode.c
index d9c7b8d..c3c9a8b 100644
--- a/src/setmode.c
+++ b/src/setmode.c
@@ -1,3 +1,5 @@
+/*	$NetBSD: setmode.c,v 1.34 2012/06/25 22:32:43 abs Exp $	*/
+
 /*
  * Copyright (c) 1989, 1993, 1994
  *	The Regents of the University of California.  All rights reserved.
@@ -13,7 +15,7 @@
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 4. Neither the name of the University nor the names of its contributors
+ * 3. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  *
@@ -30,19 +32,23 @@
  * SUCH DAMAGE.
  */
 
+#include <sys/cdefs.h>
 #if defined(LIBC_SCCS) && !defined(lint)
+#if 0
 static char sccsid[] = "@(#)setmode.c	8.2 (Berkeley) 3/25/94";
+#else
+__RCSID("$NetBSD: setmode.c,v 1.34 2012/06/25 22:32:43 abs Exp $");
+#endif
 #endif /* LIBC_SCCS and not lint */
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD: src/lib/libc/gen/setmode.c,v 1.11 2007/01/09 00:27:55 imp Exp $");
 
 #include <sys/types.h>
 #include <sys/stat.h>
 
 #include <ctype.h>
+#include <errno.h>
 #include <signal.h>
-#include <stddef.h>
 #include <stdlib.h>
+#include <limits.h>
 #include <unistd.h>
 
 #ifdef SETMODE_DEBUG
@@ -64,7 +70,7 @@ typedef struct bitcmd {
 #define	CMD2_OBITS	0x08
 #define	CMD2_UBITS	0x10
 
-static BITCMD	*addcmd(BITCMD *, int, int, int, u_int);
+static BITCMD	*addcmd(BITCMD *, mode_t, mode_t, mode_t, mode_t);
 static void	 compress_mode(BITCMD *);
 #ifdef SETMODE_DEBUG
 static void	 dumpmode(BITCMD *);
@@ -144,38 +150,38 @@ common:			if (set->cmd2 & CMD2_CLR) {
 		}
 }
 
-#define	ADDCMD(a, b, c, d)						\
+#define	ADDCMD(a, b, c, d) do {						\
 	if (set >= endset) {						\
 		BITCMD *newset;						\
 		setlen += SET_LEN_INCR;					\
 		newset = realloc(saveset, sizeof(BITCMD) * setlen);	\
-		if (!newset) {						\
-			if (saveset)					\
-				free(saveset);				\
-			saveset = NULL;					\
-			return (NULL);					\
-		}							\
+		if (newset == NULL)					\
+			goto out;					\
 		set = newset + (set - saveset);				\
 		saveset = newset;					\
 		endset = newset + (setlen - 2);				\
 	}								\
-	set = addcmd(set, (a), (b), (c), (d))
+	set = addcmd(set, (mode_t)(a), (mode_t)(b), (mode_t)(c), (d));	\
+} while (/*CONSTCOND*/0)
 
 #define	STANDARD_BITS	(S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
 
 void *
 setmode(const char *p)
 {
-	int perm, who;
+	int serrno;
 	char op, *ep;
 	BITCMD *set, *saveset, *endset;
-	sigset_t sigset, sigoset;
-	mode_t mask;
-	int equalopdone=0, permXbits, setlen;
-	long perml;
-
-	if (!*p)
-		return (NULL);
+	sigset_t signset, sigoset;
+	mode_t mask, perm, permXbits, who;
+	long lval;
+	int equalopdone = 0;	/* pacify gcc */
+	int setlen;
+
+	if (!*p) {
+		errno = EINVAL;
+		return NULL;
+	}
 
 	/*
 	 * Get a copy of the mask for the permissions that are mask relative.
@@ -183,8 +189,8 @@ setmode(const char *p)
 	 * the caller is opening files inside a signal handler, protect them
 	 * as best we can.
 	 */
-	sigfillset(&sigset);
-	(void)sigprocmask(SIG_BLOCK, &sigset, &sigoset);
+	sigfillset(&signset);
+	(void)sigprocmask(SIG_BLOCK, &signset, &sigoset);
 	(void)umask(mask = umask(0));
 	mask = ~mask;
 	(void)sigprocmask(SIG_SETMASK, &sigoset, NULL);
@@ -201,12 +207,19 @@ setmode(const char *p)
 	 * or illegal bits.
 	 */
 	if (isdigit((unsigned char)*p)) {
-		perml = strtol(p, &ep, 8);
-		if (*ep || perml < 0 || perml & ~(STANDARD_BITS|S_ISTXT)) {
-			free(saveset);
-			return (NULL);
+		errno = 0;
+		lval = strtol(p, &ep, 8);
+		if (*ep) {
+			errno = EINVAL;
+			goto out;
+		}
+		if (errno == ERANGE && (lval == LONG_MAX || lval == LONG_MIN))
+			goto out;
+		if (lval & ~(STANDARD_BITS|S_ISTXT)) {
+			errno = EINVAL;
+			goto out;
 		}
-		perm = (mode_t)perml;
+		perm = (mode_t)lval;
 		ADDCMD('=', (STANDARD_BITS|S_ISTXT), perm, mask);
 		set->cmd = 0;
 		return (saveset);
@@ -238,8 +251,8 @@ setmode(const char *p)
 		}
 
 getop:		if ((op = *p++) != '+' && op != '-' && op != '=') {
-			free(saveset);
-			return (NULL);
+			errno = EINVAL;
+			goto out;
 		}
 		if (op == '=')
 			equalopdone = 0;
@@ -251,13 +264,19 @@ getop:		if ((op = *p++) != '+' && op != '-' && op != '=') {
 				perm |= S_IRUSR|S_IRGRP|S_IROTH;
 				break;
 			case 's':
-				/* If only "other" bits ignore set-id. */
-				if (!who || who & ~S_IRWXO)
+				/*
+				 * If specific bits where requested and
+				 * only "other" bits ignore set-id.
+				 */
+				if (who == 0 || (who & ~S_IRWXO))
 					perm |= S_ISUID|S_ISGID;
 				break;
 			case 't':
-				/* If only "other" bits ignore sticky. */
-				if (!who || who & ~S_IRWXO) {
+				/*
+				 * If specific bits where requested and
+				 * only "other" bits ignore set-id.
+				 */
+				if (who == 0 || (who & ~S_IRWXO)) {
 					who |= S_ISTXT;
 					perm |= S_ISTXT;
 				}
@@ -328,10 +347,15 @@ apply:		if (!*p)
 	dumpmode(saveset);
 #endif
 	return (saveset);
+out:
+	serrno = errno;
+	free(saveset);
+	errno = serrno;
+	return NULL;
 }
 
 static BITCMD *
-addcmd(BITCMD *set, int op, int who, int oparg, u_int mask)
+addcmd(BITCMD *set, mode_t op, mode_t who, mode_t oparg, mode_t mask)
 {
 	switch (op) {
 	case '=':