/*
* Copyright (C) 2023 Patrick McDermott
*
* This file is part of opkg-opk.
*
* opkg-opk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* opkg-opk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with opkg-opk. If not, see .
*/
#include
#include
#include "defs.h"
#include "mode.h"
static const mode_t OPKG_OPK_HELPER_MODE_U_ = 04700;
static const mode_t OPKG_OPK_HELPER_MODE_G_ = 02070;
static const mode_t OPKG_OPK_HELPER_MODE_O_ = 00007;
static const mode_t OPKG_OPK_HELPER_MODE_A_ = 01777;
struct _opkg_opk_helper_mode {
mode_t umask;
mode_t cur_mode;
const char *sym_mode;
mode_t *new_mode;
mode_t who;
char op;
};
/*
* Grammar and operations from POSIX chown utility:
* https://pubs.opengroup.org/onlinepubs/9699919799/utilities/chmod.html
*/
static void
_opkg_opk_helper_mode_chmod(struct _opkg_opk_helper_mode *mode, mode_t perm)
{
switch (mode->op) {
case '+':
*mode->new_mode |= (mode->who & perm);
break;
case '-':
default: /* Should be unreachable */
*mode->new_mode ^= (mode->who & perm);
break;
}
}
static int
_opkg_opk_helper_mode_parse_perm(struct _opkg_opk_helper_mode *mode)
{
switch (*mode->sym_mode) {
case 'r':
if (mode->who == 0000) {
mode->who = OPKG_OPK_HELPER_MODE_A_^mode->umask;
}
_opkg_opk_helper_mode_chmod(mode, 00444);
break;
case 'w':
if (mode->who == 0000) {
mode->who = OPKG_OPK_HELPER_MODE_A_^mode->umask;
}
_opkg_opk_helper_mode_chmod(mode, 00222);
break;
case 'x':
if (mode->who == 0000) {
mode->who = OPKG_OPK_HELPER_MODE_A_^mode->umask;
}
_opkg_opk_helper_mode_chmod(mode, 00111);
break;
case 'X':
if (mode->who == 0000) {
mode->who = OPKG_OPK_HELPER_MODE_A_^mode->umask;
}
if (S_ISDIR(mode->cur_mode)
|| mode->cur_mode & 00111) {
_opkg_opk_helper_mode_chmod(mode, 00111);
}
break;
case 's':
if (mode->who == 0000) {
mode->who = OPKG_OPK_HELPER_MODE_A_^mode->umask;
}
_opkg_opk_helper_mode_chmod(mode, 06000);
break;
case 't':
if (S_ISDIR(mode->cur_mode) && (mode->who ==
OPKG_OPK_HELPER_MODE_A_ ||
mode->who == 0000)) {
mode->who = OPKG_OPK_HELPER_MODE_A_;
_opkg_opk_helper_mode_chmod(mode, 01000);
}
break;
default:
return OPKG_OPK_ERROR;
}
++mode->sym_mode;
return OPKG_OPK_OK;
}
static int
_opkg_opk_helper_mode_parse_permlist(struct _opkg_opk_helper_mode *mode)
{
if (_opkg_opk_helper_mode_parse_perm(mode) != OPKG_OPK_OK) {
return OPKG_OPK_ERROR;
}
_opkg_opk_helper_mode_parse_permlist(mode);
return OPKG_OPK_OK;
}
static int
_opkg_opk_helper_mode_parse_op(struct _opkg_opk_helper_mode *mode)
{
switch (*mode->sym_mode) {
case '+':
case '-':
mode->op = *mode->sym_mode;
break;
case '=':
if (mode->who == 0000) {
*mode->new_mode &= ~07777;
} else {
*mode->new_mode &= ~mode->who;
}
mode->op = '+';
break;
default:
return OPKG_OPK_ERROR;
}
++mode->sym_mode;
return OPKG_OPK_OK;
}
static int
_opkg_opk_helper_mode_parse_permcopy(struct _opkg_opk_helper_mode *mode)
{
mode_t perm;
switch (*mode->sym_mode) {
case 'u':
perm = (mode->cur_mode & 0700)
| (mode->cur_mode & 0700) >> 3
| (mode->cur_mode & 0700) >> 6;
break;
case 'g':
perm = (mode->cur_mode & 0070) << 3
| (mode->cur_mode & 0070)
| (mode->cur_mode & 0070) >> 3;
break;
case 'o':
perm = (mode->cur_mode & 0007) << 6
| (mode->cur_mode & 0007) << 3
| (mode->cur_mode & 0007);
break;
default:
return OPKG_OPK_ERROR;
}
if (mode->who == 0000) {
mode->who = OPKG_OPK_HELPER_MODE_A_ ^ mode->umask;
}
_opkg_opk_helper_mode_chmod(mode, perm);
++mode->sym_mode;
return OPKG_OPK_OK;
}
static int
_opkg_opk_helper_mode_parse_action(struct _opkg_opk_helper_mode *mode)
{
if (_opkg_opk_helper_mode_parse_op(mode) != OPKG_OPK_OK) {
return OPKG_OPK_ERROR;
}
if (_opkg_opk_helper_mode_parse_permlist(mode) != OPKG_OPK_OK) {
_opkg_opk_helper_mode_parse_permcopy(mode);
}
return OPKG_OPK_OK;
}
static int
_opkg_opk_helper_mode_parse_actionlist(struct _opkg_opk_helper_mode *mode)
{
if (_opkg_opk_helper_mode_parse_action(mode) != OPKG_OPK_OK) {
return OPKG_OPK_ERROR;
}
_opkg_opk_helper_mode_parse_actionlist(mode);
return OPKG_OPK_OK;
}
static int
_opkg_opk_helper_mode_parse_who(struct _opkg_opk_helper_mode *mode)
{
switch (*mode->sym_mode) {
case 'u':
mode->who |= OPKG_OPK_HELPER_MODE_U_;
break;
case 'g':
mode->who |= OPKG_OPK_HELPER_MODE_G_;
break;
case 'o':
mode->who |= OPKG_OPK_HELPER_MODE_O_;
break;
case 'a':
mode->who |= OPKG_OPK_HELPER_MODE_A_;
break;
default:
return OPKG_OPK_ERROR;
}
++mode->sym_mode;
return OPKG_OPK_OK;
}
static int
_opkg_opk_helper_mode_parse_wholist(struct _opkg_opk_helper_mode *mode)
{
if (_opkg_opk_helper_mode_parse_who(mode) != OPKG_OPK_OK) {
return OPKG_OPK_ERROR;
}
_opkg_opk_helper_mode_parse_wholist(mode);
return OPKG_OPK_OK;
}
static int
_opkg_opk_helper_mode_parse_clause(struct _opkg_opk_helper_mode *mode)
{
mode->who = 0000;
mode->op = '\0';
_opkg_opk_helper_mode_parse_wholist(mode);
if (_opkg_opk_helper_mode_parse_actionlist(mode) != OPKG_OPK_OK) {
return OPKG_OPK_ERROR;
}
return OPKG_OPK_OK;
}
static int
_opkg_opk_helper_mode_parse_symbolic_mode(struct _opkg_opk_helper_mode *mode)
{
if (_opkg_opk_helper_mode_parse_clause(mode) != OPKG_OPK_OK) {
return OPKG_OPK_ERROR;
}
if (*mode->sym_mode != ',') {
return OPKG_OPK_OK;
}
++mode->sym_mode;
if (_opkg_opk_helper_mode_parse_symbolic_mode(mode) != OPKG_OPK_OK) {
return OPKG_OPK_ERROR;
}
return OPKG_OPK_OK;
}
int
opkg_opk_helper_mode_parse(mode_t cur_mode, const char *mode_str,
mode_t *new_mode)
{
char *end;
long int mode_long;
struct _opkg_opk_helper_mode mode;
mode_long = strtol(mode_str, &end, 8);
if (*end == '\0') {
if ((mode_long & 07777) != mode_long) {
return OPKG_OPK_ERROR;
}
*new_mode = (mode_long & 07777);
return OPKG_OPK_OK;
}
mode.umask = umask(0);
umask(mode.umask);
mode.cur_mode = cur_mode;
mode.sym_mode = mode_str;
mode.new_mode = new_mode;
*new_mode = cur_mode;
return _opkg_opk_helper_mode_parse_symbolic_mode(&mode);
}