mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-23 08:00:20 +00:00
176 lines
4.8 KiB
C++
176 lines
4.8 KiB
C++
/*
|
|
* 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;
|
|
}
|
|
|
|
}
|