Serenity Operating System
at master 176 lines 4.9 kB view raw
1/* 2 * Copyright (c) 2021, Xavier Defrang <xavier.defrang@gmail.com> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#include <AK/Assertions.h> 8#include <AK/CharacterTypes.h> 9#include <AK/StringUtils.h> 10 11#include <LibCore/FilePermissionsMask.h> 12 13namespace Core { 14 15enum State { 16 Classes, 17 Mode 18}; 19 20enum ClassFlag { 21 Other = 1, 22 Group = 2, 23 User = 4, 24 All = 7 25}; 26 27enum Operation { 28 Add, 29 Remove, 30 Assign, 31}; 32 33ErrorOr<FilePermissionsMask> FilePermissionsMask::parse(StringView string) 34{ 35 return (!string.is_empty() && is_ascii_digit(string[0])) 36 ? from_numeric_notation(string) 37 : from_symbolic_notation(string); 38} 39 40ErrorOr<FilePermissionsMask> FilePermissionsMask::from_numeric_notation(StringView string) 41{ 42 string = string.trim_whitespace(); 43 mode_t mode = AK::StringUtils::convert_to_uint_from_octal<u16>(string, TrimWhitespace::No).value_or(010000); 44 if (mode > 07777) 45 return Error::from_string_literal("invalid octal representation"); 46 47 FilePermissionsMask mask; 48 mask.assign_permissions(mode); 49 50 // For compatibility purposes, just clear the special mode bits if we explicitly passed a 4-character mode. 51 if (string.length() >= 4) 52 mask.remove_permissions(07000); 53 54 return mask; 55} 56 57ErrorOr<FilePermissionsMask> FilePermissionsMask::from_symbolic_notation(StringView string) 58{ 59 auto mask = FilePermissionsMask(); 60 61 u8 state = State::Classes; 62 u8 classes = 0; 63 u8 operation = 0; 64 65 for (auto ch : string) { 66 switch (state) { 67 case State::Classes: { 68 // zero or more [ugoa] terminated by one operator [+-=] 69 if (ch == 'u') 70 classes |= ClassFlag::User; 71 else if (ch == 'g') 72 classes |= ClassFlag::Group; 73 else if (ch == 'o') 74 classes |= ClassFlag::Other; 75 else if (ch == 'a') 76 classes = ClassFlag::All; 77 else { 78 if (ch == '+') 79 operation = Operation::Add; 80 else if (ch == '-') 81 operation = Operation::Remove; 82 else if (ch == '=') 83 operation = Operation::Assign; 84 else if (classes == 0) 85 return Error::from_string_literal("invalid class: expected 'u', 'g', 'o' or 'a'"); 86 else 87 return Error::from_string_literal("invalid operation: expected '+', '-' or '='"); 88 89 // if an operation was specified without a class, assume all 90 if (classes == 0) 91 classes = ClassFlag::All; 92 93 state = State::Mode; 94 } 95 96 break; 97 } 98 99 case State::Mode: { 100 // one or more [rwx] terminated by a comma 101 102 // End of mode part, expect class next 103 if (ch == ',') { 104 state = State::Classes; 105 classes = operation = 0; 106 continue; 107 } 108 109 mode_t write_bits = 0; 110 bool apply_to_directories_and_executables_only = false; 111 112 switch (ch) { 113 case 'r': 114 write_bits = 4; 115 break; 116 case 'w': 117 write_bits = 2; 118 break; 119 case 'x': 120 write_bits = 1; 121 break; 122 case 'X': 123 write_bits = 1; 124 apply_to_directories_and_executables_only = true; 125 break; 126 default: 127 return Error::from_string_literal("invalid symbolic permission: expected 'r', 'w' or 'x'"); 128 } 129 130 mode_t clear_bits = operation == Operation::Assign ? 7 : write_bits; 131 132 FilePermissionsMask& edit_mask = apply_to_directories_and_executables_only ? mask.directory_or_executable_mask() : mask; 133 134 // Update masks one class at a time in other, group, user order 135 for (auto cls = classes; cls != 0; cls >>= 1) { 136 if (cls & 1) { 137 if (operation == Operation::Add || operation == Operation::Assign) 138 edit_mask.add_permissions(write_bits); 139 if (operation == Operation::Remove || operation == Operation::Assign) 140 edit_mask.remove_permissions(clear_bits); 141 } 142 write_bits <<= 3; 143 clear_bits <<= 3; 144 } 145 146 break; 147 } 148 149 default: 150 VERIFY_NOT_REACHED(); 151 } 152 } 153 154 return mask; 155} 156 157FilePermissionsMask& FilePermissionsMask::assign_permissions(mode_t mode) 158{ 159 m_write_mask = mode; 160 m_clear_mask = 0777; 161 return *this; 162} 163 164FilePermissionsMask& FilePermissionsMask::add_permissions(mode_t mode) 165{ 166 m_write_mask |= mode; 167 return *this; 168} 169 170FilePermissionsMask& FilePermissionsMask::remove_permissions(mode_t mode) 171{ 172 m_clear_mask |= mode; 173 return *this; 174} 175 176}