diff options
Diffstat (limited to 'helpers/mode.c')
-rw-r--r-- | helpers/mode.c | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/helpers/mode.c b/helpers/mode.c new file mode 100644 index 0000000..e3e4878 --- /dev/null +++ b/helpers/mode.c @@ -0,0 +1,269 @@ +/* + * 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 <https://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <sys/stat.h> +#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_ = 00777; + +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': + _opkg_opk_helper_mode_chmod(mode, 00444); + break; + case 'w': + _opkg_opk_helper_mode_chmod(mode, 00222); + break; + case 'x': + _opkg_opk_helper_mode_chmod(mode, 00111); + break; + case 'X': + if (S_ISDIR(mode->cur_mode) + || mode->cur_mode & 00111) { + _opkg_opk_helper_mode_chmod(mode, 00111); + } + break; + case 's': + _opkg_opk_helper_mode_chmod(mode, 06000); + break; + case 't': + if (S_ISDIR(mode->cur_mode) + && (mode->who & OPKG_OPK_HELPER_MODE_A_) + == 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; + } + if (mode->who == 0000) { + mode->who = OPKG_OPK_HELPER_MODE_A_ ^ mode->umask; + } + ++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) >> 001 + | (mode->cur_mode & 0700) >> 010; + break; + case 'g': + perm = (mode->cur_mode & 0070) << 001 + | (mode->cur_mode & 0070) + | (mode->cur_mode & 0070) >> 001; + break; + case 'o': + perm = (mode->cur_mode & 0007) << 010 + | (mode->cur_mode & 0007) << 001 + | (mode->cur_mode & 0007); + break; + default: + return OPKG_OPK_ERROR; + } + _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 = 00000; + + return _opkg_opk_helper_mode_parse_symbolic_mode(&mode); +} |