Browse Source

LibCore: Implement the 'X' modifier into `FilePermissionMask`

Tim Schumacher 3 years ago
parent
commit
130f04c493

+ 10 - 0
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(0), 0555);
     EXPECT_EQ(mask.value().apply(0664), 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);
     mask = Core::FilePermissionsMask::from_symbolic_notation("z+rw"sv);
     EXPECT(mask.is_error());
     EXPECT(mask.is_error());
     EXPECT(mask.error().string_literal().starts_with("invalid class"sv));
     EXPECT(mask.error().string_literal().starts_with("invalid class"sv));

+ 19 - 7
Userland/Libraries/LibCore/FilePermissionsMask.cpp

@@ -42,7 +42,7 @@ ErrorOr<FilePermissionsMask> FilePermissionsMask::from_numeric_notation(StringVi
     mode_t mode = AK::StringUtils::convert_to_uint_from_octal<u16>(string).value_or(01000);
     mode_t mode = AK::StringUtils::convert_to_uint_from_octal<u16>(string).value_or(01000);
     if (mode > 0777)
     if (mode > 0777)
         return Error::from_string_literal("invalid octal representation");
         return Error::from_string_literal("invalid octal representation");
-    return FilePermissionsMask().assign_permissions(mode);
+    return move(FilePermissionsMask().assign_permissions(mode));
 }
 }
 
 
 ErrorOr<FilePermissionsMask> FilePermissionsMask::from_symbolic_notation(StringView string)
 ErrorOr<FilePermissionsMask> FilePermissionsMask::from_symbolic_notation(StringView string)
@@ -98,25 +98,37 @@ ErrorOr<FilePermissionsMask> FilePermissionsMask::from_symbolic_notation(StringV
             }
             }
 
 
             mode_t write_bits = 0;
             mode_t write_bits = 0;
+            bool apply_to_directories_and_executables_only = false;
 
 
-            if (ch == 'r')
+            switch (ch) {
+            case 'r':
                 write_bits = 4;
                 write_bits = 4;
-            else if (ch == 'w')
+                break;
+            case 'w':
                 write_bits = 2;
                 write_bits = 2;
-            else if (ch == 'x')
+                break;
+            case 'x':
                 write_bits = 1;
                 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'");
                 return Error::from_string_literal("invalid symbolic permission: expected 'r', 'w' or 'x'");
+            }
 
 
             mode_t clear_bits = operation == Operation::Assign ? 7 : write_bits;
             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
             // Update masks one class at a time in other, group, user order
             for (auto cls = classes; cls != 0; cls >>= 1) {
             for (auto cls = classes; cls != 0; cls >>= 1) {
                 if (cls & 1) {
                 if (cls & 1) {
                     if (operation == Operation::Add || operation == Operation::Assign)
                     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)
                     if (operation == Operation::Remove || operation == Operation::Assign)
-                        mask.remove_permissions(clear_bits);
+                        edit_mask.remove_permissions(clear_bits);
                 }
                 }
                 write_bits <<= 3;
                 write_bits <<= 3;
                 clear_bits <<= 3;
                 clear_bits <<= 3;

+ 19 - 1
Userland/Libraries/LibCore/FilePermissionsMask.h

@@ -7,6 +7,7 @@
 #pragma once
 #pragma once
 
 
 #include <AK/Error.h>
 #include <AK/Error.h>
+#include <AK/OwnPtr.h>
 #include <AK/String.h>
 #include <AK/String.h>
 #include <sys/stat.h>
 #include <sys/stat.h>
 
 
@@ -28,13 +29,30 @@ public:
     FilePermissionsMask& add_permissions(mode_t mode);
     FilePermissionsMask& add_permissions(mode_t mode);
     FilePermissionsMask& remove_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 clear_mask() const { return m_clear_mask; }
     mode_t write_mask() const { return m_write_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<FilePermissionsMask>();
+
+        return *m_directory_or_executable_mask;
+    }
+
 private:
 private:
     mode_t m_clear_mask; // the bits that will be cleared
     mode_t m_clear_mask; // the bits that will be cleared
     mode_t m_write_mask; // the bits that will be set
     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<FilePermissionsMask> m_directory_or_executable_mask;
 };
 };
 
 
 }
 }