Skip to content

Instantly share code, notes, and snippets.

@clausecker
Created November 12, 2014 16:49
Show Gist options
  • Save clausecker/1b35a38570c66ed25fc7 to your computer and use it in GitHub Desktop.
Save clausecker/1b35a38570c66ed25fc7 to your computer and use it in GitHub Desktop.
/*-
* Copyright (c) 2014 Robert Clausecker
*/
/*
* parse_mode() - parse an octal or symbolic mode string into a mode_t
*/
#define _XOPEN_SOURCE 700
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "perm.h"
/* some enumerations for parse_symbolic() */
enum op {
OP_NONE, /* no op seen yet */
OP_ADD, /* + and = */
OP_SUB, /* - */
};
enum {
/* all permission bits, equal to 07777 */
PERMBITS = S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID | S_ISVTX,
};
/* compose user group and other permissions from bits */
#define MAKEBITS(bits, who) (((bits) | (bits) << 3 | (bits) << 6) & (who))
static int parse_octal(const char*, mode_t*);
static int parse_symbolic(const char*, mode_t, mode_t*);
/*
* Parse an octal or symbolic mode string as specified in IEEE Std 1003.1 2008
* Edition for the command chmod from mstr and apply the result to mode,
* applying mask in the process. Notice that this function evaluates the file
* type of mode and does the right thing for both files and directories. Return
* 0 if parsing was successful, 1 otherwise. If parsing was not succesful, the
* content of mode is undefined, otherwise it contains the result of applying
* the mode described in mstr onto mode.
*/
extern int
parse_mode(const char *mstr, mode_t mask, mode_t *mode)
{
if (parse_octal(mstr, mode))
return (0);
return (parse_symbolic(mstr, mask, mode));
}
/*
* Check if mstr contains an octal mode. Return 1 if mstr is an octal mode
* string, 0 otherwise. If mstr contains an octal mode, update mode to said
* mode.
*/
static int
parse_octal(const char *mstr, mode_t *mode)
{
char *endptr;
unsigned long newmode;
if (*mstr == '\0')
return (0);
newmode = strtoul(mstr, &endptr, 8);
if (*endptr != '\0' || newmode > PERMBITS)
return (0);
*mode &= ~PERMBITS;
*mode |= newmode;
return (1);
}
/*
* Parse one clause of a symbolic mode string and apply it to mode respecting
* mask. If the clause cannot be parsed, return 1, else 0. For
*/
static int
parse_symbolic(const char *mstr, mode_t mask, mode_t *mode)
{
/*
* In a permission string like +w which does not contain a wholist, the
* receiver of the new permission is taken from the umask instead. For
* this reason, it is neccessary to record if we have seen parts of a
* wholist: The algorithm I use sets the who bits from the mask first
* and clears them when it encounters the first character of a wholist.
*/
int who_seen = 0; /* Have we seen a wholist? */
mode_t who = ~mask; /* Whom to apply permission changes to */
enum op op = OP_NONE;
mode_t m;
for (;; mstr++)
if (op == OP_NONE)
switch (*mstr) {
case '=':
*mode &= ~PERMBITS;
op = OP_ADD;
break;
case '+':
op = OP_ADD;
break;
case '-':
op = OP_SUB;
break;
case 'u':
if (!who_seen)
who = 0;
who_seen = 1;
who |= S_IRWXU | S_ISUID;
break;
case 'g':
if (!who_seen)
who = 0;
who_seen = 1;
who |= S_IRWXG | S_ISGID;
break;
case 'o':
if (!who_seen)
who = 0;
who_seen = 1;
who |= S_IRWXO | S_ISVTX;
break;
case 'a':
who_seen = 1;
who = PERMBITS;
break;
default:
return (1);
}
else
switch (*mstr) {
case '\0':
return (0);
case ',':
who_seen = 0;
who = ~mask;
op = OP_NONE;
break;
case '=':
*mode &= ~PERMBITS;
op = OP_ADD;
break;
case '+':
op = OP_ADD;
break;
case '-':
op = OP_SUB;
break;
case 'u':
m = (*mode & S_IRWXU);
m = MAKEBITS(m, who);
goto apply_perm;
case 'g':
m = (*mode & S_IRWXG) >> 3;
m = MAKEBITS(m, who);
goto apply_perm;
case 'o':
m = (*mode & S_IRWXO) >> 0;
m = MAKEBITS(m, who);
goto apply_perm;
case 'r':
m = MAKEBITS(S_IROTH, who);
goto apply_perm;
case 'w':
m = MAKEBITS(S_IWOTH, who);
goto apply_perm;
case 'X':
if (!S_ISDIR(*mode) && (*mode
& (S_IXUSR | S_IXGRP | S_IXOTH)) == 0)
break;
/* FALLTHROUGH */
case 'x':
m = MAKEBITS(S_IXOTH, who);
goto apply_perm;
case 's':
m = (S_ISUID | S_ISGID) & who;
goto apply_perm;
case 't':
m = S_ISVTX & who;
goto apply_perm;
default:
return (1);
apply_perm:
/* remember: op cannot be OP_NONE here */
if (op == OP_ADD)
*mode |= m;
else
*mode &= ~m;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment