Serenity Operating System
at portability 256 lines 7.7 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <AK/Optional.h> 28#include <AK/StdLibExtras.h> 29#include <AK/kmalloc.h> 30#include <stdio.h> 31#include <string.h> 32#include <sys/stat.h> 33#include <unistd.h> 34 35/* the new mode will be computed using the boolean function(for each bit): 36 37 |current mode|removal mask|applying mask|result | 38 | 0 | 0 | 0 | 0 | 39 | 0 | 0 | 1 | 1 | 40 | 0 | 1 | 0 | 0 | 41 | 0 | 1 | 1 | 1 | ---> find the CNF --> find the minimal CNF 42 | 1 | 0 | 0 | 1 | 43 | 1 | 0 | 1 | 1 | 44 | 1 | 1 | 0 | 0 | 45 | 1 | 1 | 1 | 1 | 46*/ 47 48class Mask { 49private: 50 mode_t removal_mask; //the bits that will be removed 51 mode_t applying_mask; //the bits that will be setted 52 53public: 54 Mask() 55 : removal_mask(0) 56 , applying_mask(0) 57 { 58 } 59 60 Mask& operator|=(const Mask& other) 61 { 62 removal_mask |= other.removal_mask; 63 applying_mask |= other.applying_mask; 64 65 return *this; 66 } 67 68 mode_t& get_removal_mask() { return removal_mask; } 69 mode_t& get_applying_mask() { return applying_mask; } 70}; 71 72Optional<Mask> string_to_mode(char access_scope, const char*& access_string); 73Optional<Mask> apply_permission(char access_scope, char permission, char operation); 74 75int main(int argc, char** argv) 76{ 77 if (pledge("stdio rpath fattr", nullptr) < 0) { 78 perror("pledge"); 79 return 1; 80 } 81 82 if (argc < 3) { 83 printf("usage: chmod <octal-mode> <path...>\n" 84 " chmod [[ugoa][+-=][rwx...],...] <path...>\n"); 85 return 1; 86 } 87 88 Mask mask; 89 90 /* compute a mask */ 91 if (argv[1][0] >= '0' && argv[1][0] <= '7') { 92 if (sscanf(argv[1], "%ho", &mask.get_applying_mask()) != 1) { 93 perror("sscanf"); 94 return 1; 95 } 96 mask.get_removal_mask() = ~mask.get_applying_mask(); 97 } else { 98 const char* access_string = argv[1]; 99 100 while (*access_string != '\0') { 101 Optional<Mask> tmp_mask; 102 switch (*access_string) { 103 case 'u': 104 tmp_mask = string_to_mode('u', ++access_string); 105 break; 106 case 'g': 107 tmp_mask = string_to_mode('g', ++access_string); 108 break; 109 case 'o': 110 tmp_mask = string_to_mode('o', ++access_string); 111 break; 112 case 'a': 113 tmp_mask = string_to_mode('a', ++access_string); 114 break; 115 case '=': 116 case '+': 117 case '-': 118 tmp_mask = string_to_mode('a', access_string); 119 break; 120 case ',': 121 ++access_string; 122 continue; 123 } 124 if (!tmp_mask.has_value()) { 125 fprintf(stderr, "chmod: invalid mode: %s\n", argv[1]); 126 return 1; 127 } 128 mask |= tmp_mask.value(); 129 } 130 } 131 132 /* set the mask for each files' permissions */ 133 struct stat current_access; 134 int i = 2; 135 while (i < argc) { 136 if (stat(argv[i], &current_access) != 0) { 137 perror("stat"); 138 return 1; 139 } 140 /* found the minimal CNF by The Quine–McCluskey algorithm and use it */ 141 mode_t mode = mask.get_applying_mask() 142 | (current_access.st_mode & ~mask.get_removal_mask()); 143 if (chmod(argv[i++], mode) != 0) { 144 perror("chmod"); 145 } 146 } 147 148 return 0; 149} 150 151Optional<Mask> string_to_mode(char access_scope, const char*& access_string) 152{ 153 char operation = *access_string; 154 155 if (operation != '+' && operation != '-' && operation != '=') { 156 return {}; 157 } 158 159 Mask mask; 160 if (operation == '=') { 161 switch (access_scope) { 162 case 'u': 163 mask.get_removal_mask() = (S_IRUSR | S_IWUSR | S_IXUSR); 164 break; 165 case 'g': 166 mask.get_removal_mask() = (S_IRGRP | S_IWGRP | S_IXGRP); 167 break; 168 case 'o': 169 mask.get_removal_mask() = (S_IROTH | S_IWOTH | S_IXOTH); 170 break; 171 case 'a': 172 mask.get_removal_mask() = (S_IRUSR | S_IWUSR | S_IXUSR 173 | S_IRGRP | S_IWGRP | S_IXGRP 174 | S_IROTH | S_IWOTH | S_IXOTH); 175 break; 176 } 177 operation = '+'; 178 } 179 180 access_string++; 181 while (*access_string != '\0' && *access_string != ',') { 182 Optional<Mask> tmp_mask; 183 tmp_mask = apply_permission(access_scope, *access_string, operation); 184 if (!tmp_mask.has_value()) { 185 return {}; 186 } 187 mask |= tmp_mask.value(); 188 access_string++; 189 } 190 191 return mask; 192} 193 194Optional<Mask> apply_permission(char access_scope, char permission, char operation) 195{ 196 if (permission != 'r' && permission != 'w' && permission != 'x') { 197 return {}; 198 } 199 200 Mask mask; 201 mode_t tmp_mask = 0; 202 switch (access_scope) { 203 case 'u': 204 switch (permission) { 205 case 'r': 206 tmp_mask = S_IRUSR; 207 break; 208 case 'w': 209 tmp_mask = S_IWUSR; 210 break; 211 case 'x': 212 tmp_mask = S_IXUSR; 213 break; 214 } 215 break; 216 case 'g': 217 switch (permission) { 218 case 'r': 219 tmp_mask = S_IRGRP; 220 break; 221 case 'w': 222 tmp_mask = S_IWGRP; 223 break; 224 case 'x': 225 tmp_mask = S_IXGRP; 226 break; 227 } 228 break; 229 case 'o': 230 switch (permission) { 231 case 'r': 232 tmp_mask = S_IROTH; 233 break; 234 case 'w': 235 tmp_mask = S_IWOTH; 236 break; 237 case 'x': 238 tmp_mask = S_IXOTH; 239 break; 240 } 241 break; 242 case 'a': 243 mask |= apply_permission('u', permission, operation).value(); 244 mask |= apply_permission('g', permission, operation).value(); 245 mask |= apply_permission('o', permission, operation).value(); 246 break; 247 } 248 249 if (operation == '+') { 250 mask.get_applying_mask() |= tmp_mask; 251 } else { 252 mask.get_removal_mask() |= tmp_mask; 253 } 254 255 return mask; 256}