mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 02:42:44 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			155 lines
		
	
	
	
		
			4.1 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			155 lines
		
	
	
	
		
			4.1 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)
 | |
| {
 | |
|     mode_t mode = AK::StringUtils::convert_to_uint_from_octal<u16>(string).value_or(01000);
 | |
|     if (mode > 0777)
 | |
|         return Error::from_string_literal("invalid octal representation"sv);
 | |
|     return FilePermissionsMask().assign_permissions(mode);
 | |
| }
 | |
| 
 | |
| 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'"sv);
 | |
|                 else
 | |
|                     return Error::from_string_literal("invalid operation: expected '+', '-' or '='"sv);
 | |
| 
 | |
|                 // 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;
 | |
| 
 | |
|             if (ch == 'r')
 | |
|                 write_bits = 4;
 | |
|             else if (ch == 'w')
 | |
|                 write_bits = 2;
 | |
|             else if (ch == 'x')
 | |
|                 write_bits = 1;
 | |
|             else
 | |
|                 return Error::from_string_literal("invalid symbolic permission: expected 'r', 'w' or 'x'"sv);
 | |
| 
 | |
|             mode_t clear_bits = operation == Operation::Assign ? 7 : write_bits;
 | |
| 
 | |
|             // 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);
 | |
|                     if (operation == Operation::Remove || operation == Operation::Assign)
 | |
|                         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;
 | |
| }
 | |
| 
 | |
| }
 | 
