summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorAlbert Gafiyatullin <binixary@gmail.com>2019-07-13 22:36:02 +0500
committerAndreas Kling <awesomekling@gmail.com>2019-07-13 19:36:02 +0200
commitf5a9b27f3ffbf02f82329bb37e4c3e867b68fa1a (patch)
treea9e531516decea4a197673497525307d1d04602d /Userland
parentcf1afcafbcd1bd9126de303f2de0605c3319c7b8 (diff)
downloadserenity-f5a9b27f3ffbf02f82329bb37e4c3e867b68fa1a.zip
chmod: Add understanding of simple permission strings. (#295)
Fixes #162.
Diffstat (limited to 'Userland')
-rw-r--r--Userland/chmod.cpp220
1 files changed, 209 insertions, 11 deletions
diff --git a/Userland/chmod.cpp b/Userland/chmod.cpp
index e4057af0c0..ff24a08002 100644
--- a/Userland/chmod.cpp
+++ b/Userland/chmod.cpp
@@ -1,27 +1,225 @@
+#include <AK/Optional.h>
+#include <AK/StdLibExtras.h>
+#include <AK/kmalloc.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
+/* the new mode will be computed using the boolean function(for each bit):
+
+ |current mode|removal mask|applying mask|result |
+ | 0 | 0 | 0 | 0 |
+ | 0 | 0 | 1 | 1 |
+ | 0 | 1 | 0 | 0 |
+ | 0 | 1 | 1 | 1 | ---> find the CNF --> find the minimal CNF
+ | 1 | 0 | 0 | 1 |
+ | 1 | 0 | 1 | 1 |
+ | 1 | 1 | 0 | 0 |
+ | 1 | 1 | 1 | 1 |
+*/
+
+class Mask {
+private:
+ mode_t removal_mask; //the bits that will be removed
+ mode_t applying_mask; //the bits that will be setted
+
+public:
+ Mask()
+ : removal_mask(0)
+ , applying_mask(0)
+ {
+ }
+
+ Mask& operator|=(const Mask& other)
+ {
+ removal_mask |= other.removal_mask;
+ applying_mask |= other.applying_mask;
+
+ return *this;
+ }
+
+ mode_t& get_removal_mask() { return removal_mask; }
+ mode_t& get_applying_mask() { return applying_mask; }
+};
+
+Optional<Mask> string_to_mode(char access_scope, const char*& access_string);
+Optional<Mask> apply_permission(char access_scope, char permission, char operation);
+
int main(int argc, char** argv)
{
- if (argc != 3) {
- printf("usage: chmod <octal-mode> <path>\n");
+ if (argc < 3) {
+ printf("usage: chmod <octal-mode> <path,...>\n"
+ " chmod [[ugoa][+-=][rwx...],...] <path,...>\n");
return 1;
}
- unsigned mode;
- int rc = sscanf(argv[1], "%o", &mode);
- if (rc != 1) {
- perror("sscanf");
- return 1;
+ Mask mask;
+
+ /* compute a mask */
+ if (argv[1][0] >= '0' && argv[1][0] <= '7') {
+ if (sscanf(argv[1], "%ho", &mask.get_applying_mask()) != 1) {
+ perror("sscanf");
+ return 1;
+ }
+ mask.get_removal_mask() = ~mask.get_applying_mask();
+ } else {
+ const char* access_string = argv[1];
+
+ while (*access_string != '\0') {
+ Optional<Mask> tmp_mask;
+ switch (*access_string) {
+ case 'u':
+ tmp_mask = string_to_mode('u', ++access_string);
+ break;
+ case 'g':
+ tmp_mask = string_to_mode('g', ++access_string);
+ break;
+ case 'o':
+ tmp_mask = string_to_mode('o', ++access_string);
+ break;
+ case 'a':
+ tmp_mask = string_to_mode('a', ++access_string);
+ break;
+ case '=':
+ case '+':
+ case '-':
+ tmp_mask = string_to_mode('a', access_string);
+ break;
+ case ',':
+ ++access_string;
+ continue;
+ }
+ if (!tmp_mask.has_value()) {
+ fprintf(stderr, "chmod: invalid mode: %s\n", argv[1]);
+ return 1;
+ }
+ mask |= tmp_mask.value();
+ }
}
- rc = chmod(argv[2], mode);
- if (rc < 0) {
- perror("chmod");
- return 1;
+ /* set the mask for each files' permissions */
+ struct stat current_access;
+ int i = 2;
+ while (i < argc) {
+ if (stat(argv[i], &current_access) != 0) {
+ perror("stat");
+ return 1;
+ }
+ /* found the minimal CNF by The Quine–McCluskey algorithm and use it */
+ mode_t mode = mask.get_applying_mask()
+ | (current_access.st_mode & ~mask.get_removal_mask());
+ if (chmod(argv[i++], mode) != 0) {
+ perror("chmod");
+ }
}
return 0;
}
+
+Optional<Mask> string_to_mode(char access_scope, const char*& access_string)
+{
+ char operation = *access_string;
+
+ if (operation != '+' && operation != '-' && operation != '=') {
+ return {};
+ }
+
+ Mask mask;
+ if (operation == '=') {
+ switch (access_scope) {
+ case 'u':
+ mask.get_removal_mask() = (S_IRUSR | S_IWUSR | S_IXUSR);
+ break;
+ case 'g':
+ mask.get_removal_mask() = (S_IRGRP | S_IWGRP | S_IXGRP);
+ break;
+ case 'o':
+ mask.get_removal_mask() = (S_IROTH | S_IWOTH | S_IXOTH);
+ break;
+ case 'a':
+ mask.get_removal_mask() = (S_IRUSR | S_IWUSR | S_IXUSR
+ | S_IRGRP | S_IWGRP | S_IXGRP
+ | S_IROTH | S_IWOTH | S_IXOTH);
+ break;
+ }
+ operation = '+';
+ }
+
+ access_string++;
+ while (*access_string != '\0' && *access_string != ',') {
+ Optional<Mask> tmp_mask;
+ tmp_mask = apply_permission(access_scope, *access_string, operation);
+ if (!tmp_mask.has_value()) {
+ return {};
+ }
+ mask |= tmp_mask.value();
+ access_string++;
+ }
+
+ return mask;
+}
+
+Optional<Mask> apply_permission(char access_scope, char permission, char operation)
+{
+ if (permission != 'r' && permission != 'w' && permission != 'x') {
+ return {};
+ }
+
+ Mask mask;
+ mode_t tmp_mask = 0;
+ switch (access_scope) {
+ case 'u':
+ switch (permission) {
+ case 'r':
+ tmp_mask = S_IRUSR;
+ break;
+ case 'w':
+ tmp_mask = S_IWUSR;
+ break;
+ case 'x':
+ tmp_mask = S_IXUSR;
+ break;
+ }
+ break;
+ case 'g':
+ switch (permission) {
+ case 'r':
+ tmp_mask = S_IRGRP;
+ break;
+ case 'w':
+ tmp_mask = S_IWGRP;
+ break;
+ case 'x':
+ tmp_mask = S_IXGRP;
+ break;
+ }
+ break;
+ case 'o':
+ switch (permission) {
+ case 'r':
+ tmp_mask = S_IROTH;
+ break;
+ case 'w':
+ tmp_mask = S_IWOTH;
+ break;
+ case 'x':
+ tmp_mask = S_IXOTH;
+ break;
+ }
+ break;
+ case 'a':
+ mask |= apply_permission('u', permission, operation).value();
+ mask |= apply_permission('g', permission, operation).value();
+ mask |= apply_permission('o', permission, operation).value();
+ break;
+ }
+
+ if (operation == '+') {
+ mask.get_applying_mask() |= tmp_mask;
+ } else {
+ mask.get_removal_mask() |= tmp_mask;
+ }
+
+ return mask;
+}