+.\" $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.
.\"
.\" 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.
.\"
.\" @(#)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
.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.
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 .
.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 ,
.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 * .
+/* $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.
* 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.
*
* 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
#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 *);
}
}
-#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.
* 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);
* 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);
}
getop: if ((op = *p++) != '+' && op != '-' && op != '=') {
- free(saveset);
- return (NULL);
+ errno = EINVAL;
+ goto out;
}
if (op == '=')
equalopdone = 0;
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;
}
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 '=':