diff --git a/Tests/LibCore/TestLibCoreFilePermissionsMask.cpp b/Tests/LibCore/TestLibCoreFilePermissionsMask.cpp index 96bc6064e0..1dc303c0c6 100644 --- a/Tests/LibCore/TestLibCoreFilePermissionsMask.cpp +++ b/Tests/LibCore/TestLibCoreFilePermissionsMask.cpp @@ -72,6 +72,16 @@ TEST_CASE(file_permission_mask_from_symbolic_notation) EXPECT_EQ(mask.value().apply(0), 0555); EXPECT_EQ(mask.value().apply(0664), 0555); + mask = Core::FilePermissionsMask::from_symbolic_notation("a+X"sv); + EXPECT(!mask.is_error()); + EXPECT_EQ(mask.value().clear_mask(), 0); + EXPECT_EQ(mask.value().write_mask(), 0); + EXPECT_EQ(mask.value().directory_or_executable_mask().clear_mask(), 0); + EXPECT_EQ(mask.value().directory_or_executable_mask().write_mask(), 0111); + EXPECT_EQ(mask.value().apply(0), 0); + EXPECT_EQ(mask.value().apply(0100), 0111); + EXPECT_EQ(mask.value().apply(S_IFDIR | 0), S_IFDIR | 0111); + mask = Core::FilePermissionsMask::from_symbolic_notation("z+rw"sv); EXPECT(mask.is_error()); EXPECT(mask.error().string_literal().starts_with("invalid class"sv)); diff --git a/Userland/Libraries/LibCore/FilePermissionsMask.cpp b/Userland/Libraries/LibCore/FilePermissionsMask.cpp index 52f76b809a..bbb674ad01 100644 --- a/Userland/Libraries/LibCore/FilePermissionsMask.cpp +++ b/Userland/Libraries/LibCore/FilePermissionsMask.cpp @@ -42,7 +42,7 @@ ErrorOr FilePermissionsMask::from_numeric_notation(StringVi mode_t mode = AK::StringUtils::convert_to_uint_from_octal(string).value_or(01000); if (mode > 0777) return Error::from_string_literal("invalid octal representation"); - return FilePermissionsMask().assign_permissions(mode); + return move(FilePermissionsMask().assign_permissions(mode)); } ErrorOr FilePermissionsMask::from_symbolic_notation(StringView string) @@ -98,25 +98,37 @@ ErrorOr FilePermissionsMask::from_symbolic_notation(StringV } mode_t write_bits = 0; + bool apply_to_directories_and_executables_only = false; - if (ch == 'r') + switch (ch) { + case 'r': write_bits = 4; - else if (ch == 'w') + break; + case 'w': write_bits = 2; - else if (ch == 'x') + break; + case 'x': write_bits = 1; - else + 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) - mask.add_permissions(write_bits); + edit_mask.add_permissions(write_bits); if (operation == Operation::Remove || operation == Operation::Assign) - mask.remove_permissions(clear_bits); + edit_mask.remove_permissions(clear_bits); } write_bits <<= 3; clear_bits <<= 3; diff --git a/Userland/Libraries/LibCore/FilePermissionsMask.h b/Userland/Libraries/LibCore/FilePermissionsMask.h index c238344d91..3a664387e4 100644 --- a/Userland/Libraries/LibCore/FilePermissionsMask.h +++ b/Userland/Libraries/LibCore/FilePermissionsMask.h @@ -7,6 +7,7 @@ #pragma once #include +#include #include #include @@ -28,13 +29,30 @@ public: FilePermissionsMask& add_permissions(mode_t mode); FilePermissionsMask& remove_permissions(mode_t mode); - mode_t apply(mode_t mode) const { return m_write_mask | (mode & ~m_clear_mask); } + mode_t apply(mode_t mode) const + { + if (m_directory_or_executable_mask && (S_ISDIR(mode) || (mode & 0111) != 0)) + mode = m_directory_or_executable_mask->apply(mode); + + return m_write_mask | (mode & ~m_clear_mask); + } mode_t clear_mask() const { return m_clear_mask; } mode_t write_mask() const { return m_write_mask; } + FilePermissionsMask& directory_or_executable_mask() + { + if (!m_directory_or_executable_mask) + m_directory_or_executable_mask = make(); + + return *m_directory_or_executable_mask; + } + private: mode_t m_clear_mask; // the bits that will be cleared mode_t m_write_mask; // the bits that will be set + + // A separate mask, only for files that already have some executable bit set or directories. + OwnPtr m_directory_or_executable_mask; }; }