Преглед на файлове

chmod: Add understanding of simple permission strings. (#295)

Fixes #162.
Albert Gafiyatullin преди 6 години
родител
ревизия
f5a9b27f3f
променени са 1 файла, в които са добавени 209 реда и са изтрити 11 реда
  1. 209 11
      Userland/chmod.cpp

+ 209 - 11
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;
+}