/* * 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); }