123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- /*
- * Copyright (c) 2021, Xavier Defrang <xavier.defrang@gmail.com>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
- #include <AK/Assertions.h>
- #include <AK/CharacterTypes.h>
- #include <AK/StringUtils.h>
- #include <LibCore/FilePermissionsMask.h>
- namespace Core {
- enum State {
- Classes,
- Mode
- };
- enum ClassFlag {
- Other = 1,
- Group = 2,
- User = 4,
- All = 7
- };
- enum Operation {
- Add,
- Remove,
- Assign,
- };
- ErrorOr<FilePermissionsMask> FilePermissionsMask::parse(StringView string)
- {
- return (!string.is_empty() && is_ascii_digit(string[0]))
- ? from_numeric_notation(string)
- : from_symbolic_notation(string);
- }
- ErrorOr<FilePermissionsMask> FilePermissionsMask::from_numeric_notation(StringView string)
- {
- string = string.trim_whitespace();
- mode_t mode = AK::StringUtils::convert_to_uint_from_octal<u16>(string, TrimWhitespace::No).value_or(010000);
- if (mode > 07777)
- return Error::from_string_literal("invalid octal representation");
- FilePermissionsMask mask;
- mask.assign_permissions(mode);
- // For compatibility purposes, just clear the special mode bits if we explicitly passed a 4-character mode.
- if (string.length() >= 4)
- mask.remove_permissions(07000);
- return mask;
- }
- ErrorOr<FilePermissionsMask> FilePermissionsMask::from_symbolic_notation(StringView string)
- {
- auto mask = FilePermissionsMask();
- u8 state = State::Classes;
- u8 classes = 0;
- u8 operation = 0;
- for (auto ch : string) {
- switch (state) {
- case State::Classes: {
- // zero or more [ugoa] terminated by one operator [+-=]
- if (ch == 'u')
- classes |= ClassFlag::User;
- else if (ch == 'g')
- classes |= ClassFlag::Group;
- else if (ch == 'o')
- classes |= ClassFlag::Other;
- else if (ch == 'a')
- classes = ClassFlag::All;
- else {
- if (ch == '+')
- operation = Operation::Add;
- else if (ch == '-')
- operation = Operation::Remove;
- else if (ch == '=')
- operation = Operation::Assign;
- else if (classes == 0)
- return Error::from_string_literal("invalid class: expected 'u', 'g', 'o' or 'a'");
- else
- return Error::from_string_literal("invalid operation: expected '+', '-' or '='");
- // if an operation was specified without a class, assume all
- if (classes == 0)
- classes = ClassFlag::All;
- state = State::Mode;
- }
- break;
- }
- case State::Mode: {
- // one or more [rwx] terminated by a comma
- // End of mode part, expect class next
- if (ch == ',') {
- state = State::Classes;
- classes = operation = 0;
- continue;
- }
- mode_t write_bits = 0;
- bool apply_to_directories_and_executables_only = false;
- switch (ch) {
- case 'r':
- write_bits = 4;
- break;
- case 'w':
- write_bits = 2;
- break;
- case 'x':
- write_bits = 1;
- break;
- case 'X':
- write_bits = 1;
- apply_to_directories_and_executables_only = true;
- break;
- default:
- return Error::from_string_literal("invalid symbolic permission: expected 'r', 'w' or 'x'");
- }
- mode_t clear_bits = operation == Operation::Assign ? 7 : write_bits;
- FilePermissionsMask& edit_mask = apply_to_directories_and_executables_only ? mask.directory_or_executable_mask() : mask;
- // Update masks one class at a time in other, group, user order
- for (auto cls = classes; cls != 0; cls >>= 1) {
- if (cls & 1) {
- if (operation == Operation::Add || operation == Operation::Assign)
- edit_mask.add_permissions(write_bits);
- if (operation == Operation::Remove || operation == Operation::Assign)
- edit_mask.remove_permissions(clear_bits);
- }
- write_bits <<= 3;
- clear_bits <<= 3;
- }
- break;
- }
- default:
- VERIFY_NOT_REACHED();
- }
- }
- return mask;
- }
- FilePermissionsMask& FilePermissionsMask::assign_permissions(mode_t mode)
- {
- m_write_mask = mode;
- m_clear_mask = 0777;
- return *this;
- }
- FilePermissionsMask& FilePermissionsMask::add_permissions(mode_t mode)
- {
- m_write_mask |= mode;
- return *this;
- }
- FilePermissionsMask& FilePermissionsMask::remove_permissions(mode_t mode)
- {
- m_clear_mask |= mode;
- return *this;
- }
- }
|