summaryrefslogtreecommitdiffstats
path: root/common/mode.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/mode.c')
-rw-r--r--common/mode.c285
1 files changed, 285 insertions, 0 deletions
diff --git a/common/mode.c b/common/mode.c
new file mode 100644
index 0000000..b643a60
--- /dev/null
+++ b/common/mode.c
@@ -0,0 +1,285 @@
+/*
+ * 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_ = 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);
+}