mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-30 05:42:37 +00:00 
			
		
		
		
	FileSystemAccessServer: Add service for accessing veiled files nicely
Adds new service FileSystemAccessServer which allows programs to request a file descriptor for any file on the file system. The user can be prompted to choose the path with a FilePicker, or the path can be provided by the application which will show a MessageBox showing the pid and name of the calling process and allows the user to approve or deny the request.
This commit is contained in:
		
							parent
							
								
									5e823d3de0
								
							
						
					
					
						commit
						41ce2debda
					
				
					 8 changed files with 246 additions and 0 deletions
				
			
		|  | @ -8,6 +8,16 @@ BootModes=text,graphical,self-test | ||||||
| MultiInstance=1 | MultiInstance=1 | ||||||
| AcceptSocketConnections=1 | AcceptSocketConnections=1 | ||||||
| 
 | 
 | ||||||
|  | [FileSystemAccessServer] | ||||||
|  | Socket=/tmp/portal/filesystemaccess | ||||||
|  | SocketPermissions=660 | ||||||
|  | Lazy=1 | ||||||
|  | Priority=low | ||||||
|  | User=anon | ||||||
|  | BootModes=text,graphical,self-test | ||||||
|  | MultiInstance=1 | ||||||
|  | AcceptSocketConnections=1 | ||||||
|  | 
 | ||||||
| [WebContent] | [WebContent] | ||||||
| Socket=/tmp/portal/webcontent | Socket=/tmp/portal/webcontent | ||||||
| SocketPermissions=600 | SocketPermissions=600 | ||||||
|  |  | ||||||
|  | @ -4,6 +4,7 @@ add_subdirectory(Clipboard) | ||||||
| add_subdirectory(CrashDaemon) | add_subdirectory(CrashDaemon) | ||||||
| add_subdirectory(DHCPClient) | add_subdirectory(DHCPClient) | ||||||
| add_subdirectory(EchoServer) | add_subdirectory(EchoServer) | ||||||
|  | add_subdirectory(FileSystemAccessServer) | ||||||
| add_subdirectory(FileOperation) | add_subdirectory(FileOperation) | ||||||
| add_subdirectory(ImageDecoder) | add_subdirectory(ImageDecoder) | ||||||
| add_subdirectory(InspectorServer) | add_subdirectory(InspectorServer) | ||||||
|  |  | ||||||
							
								
								
									
										18
									
								
								Userland/Services/FileSystemAccessServer/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								Userland/Services/FileSystemAccessServer/CMakeLists.txt
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,18 @@ | ||||||
|  | serenity_component( | ||||||
|  |     FileSystemAccessServer | ||||||
|  |     REQUIRED | ||||||
|  |     TARGETS FileSystemAccessServer | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | compile_ipc(FileSystemAccessServer.ipc FileSystemAccessServerEndpoint.h) | ||||||
|  | compile_ipc(FileSystemAccessClient.ipc FileSystemAccessClientEndpoint.h) | ||||||
|  | 
 | ||||||
|  | set(SOURCES | ||||||
|  |     ClientConnection.cpp | ||||||
|  |     main.cpp | ||||||
|  |     FileSystemAccessServerEndpoint.h | ||||||
|  |     FileSystemAccessClientEndpoint.h | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | serenity_bin(FileSystemAccessServer) | ||||||
|  | target_link_libraries(FileSystemAccessServer LibCore LibIPC LibGUI) | ||||||
							
								
								
									
										141
									
								
								Userland/Services/FileSystemAccessServer/ClientConnection.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								Userland/Services/FileSystemAccessServer/ClientConnection.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,141 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2021, timmot <tiwwot@protonmail.com> | ||||||
|  |  * | ||||||
|  |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <AK/Debug.h> | ||||||
|  | #include <FileSystemAccessServer/ClientConnection.h> | ||||||
|  | #include <LibCore/File.h> | ||||||
|  | #include <LibCore/IODevice.h> | ||||||
|  | #include <LibGUI/Application.h> | ||||||
|  | #include <LibGUI/FilePicker.h> | ||||||
|  | #include <LibGUI/MessageBox.h> | ||||||
|  | 
 | ||||||
|  | namespace FileSystemAccessServer { | ||||||
|  | 
 | ||||||
|  | static HashMap<int, NonnullRefPtr<ClientConnection>> s_connections; | ||||||
|  | 
 | ||||||
|  | ClientConnection::ClientConnection(NonnullRefPtr<Core::LocalSocket> socket, int client_id) | ||||||
|  |     : IPC::ClientConnection<FileSystemAccessClientEndpoint, FileSystemAccessServerEndpoint>(*this, move(socket), client_id) | ||||||
|  | { | ||||||
|  |     s_connections.set(client_id, *this); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | ClientConnection::~ClientConnection() | ||||||
|  | { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void ClientConnection::die() | ||||||
|  | { | ||||||
|  |     s_connections.remove(client_id()); | ||||||
|  |     GUI::Application::the()->quit(); | ||||||
|  |     exit(0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Messages::FileSystemAccessServer::RequestFileResponse ClientConnection::request_file(String const& path, Core::OpenMode const& requested_access) | ||||||
|  | { | ||||||
|  |     VERIFY(path.starts_with("/"sv)); | ||||||
|  | 
 | ||||||
|  |     bool approved = false; | ||||||
|  |     auto maybe_permissions = m_approved_files.get(path); | ||||||
|  | 
 | ||||||
|  |     auto relevant_permissions = requested_access & (Core::OpenMode::ReadOnly | Core::OpenMode::WriteOnly); | ||||||
|  |     VERIFY(relevant_permissions != Core::OpenMode::NotOpen); | ||||||
|  | 
 | ||||||
|  |     if (maybe_permissions.has_value()) | ||||||
|  |         approved = has_flag(maybe_permissions.value(), relevant_permissions); | ||||||
|  | 
 | ||||||
|  |     if (!approved) { | ||||||
|  |         StringBuilder builder; | ||||||
|  |         if (has_flag(requested_access, Core::OpenMode::ReadOnly)) | ||||||
|  |             builder.append('r'); | ||||||
|  | 
 | ||||||
|  |         if (has_flag(requested_access, Core::OpenMode::WriteOnly)) | ||||||
|  |             builder.append('w'); | ||||||
|  | 
 | ||||||
|  |         auto access_string = builder.to_string(); | ||||||
|  | 
 | ||||||
|  |         auto pid = this->socket().peer_pid(); | ||||||
|  |         auto exe_link = LexicalPath("/proc").append(String::number(pid)).append("exe").string(); | ||||||
|  |         auto exe_path = Core::File::real_path_for(exe_link); | ||||||
|  |         auto exe_name = LexicalPath::basename(exe_path); | ||||||
|  | 
 | ||||||
|  |         auto result = GUI::MessageBox::show(nullptr, String::formatted("Give {} ({}) \"{}\" access to \"{}\"?", exe_name, pid, access_string, path), "File Permissions Requested", GUI::MessageBox::Type::Warning, GUI::MessageBox::InputType::YesNo); | ||||||
|  | 
 | ||||||
|  |         approved = result == GUI::MessageBox::ExecYes; | ||||||
|  | 
 | ||||||
|  |         if (approved) { | ||||||
|  |             auto new_permissions = relevant_permissions; | ||||||
|  | 
 | ||||||
|  |             if (maybe_permissions.has_value()) | ||||||
|  |                 new_permissions |= maybe_permissions.value(); | ||||||
|  | 
 | ||||||
|  |             m_approved_files.set(path, new_permissions); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (approved) { | ||||||
|  |         auto file = Core::File::open(path, requested_access); | ||||||
|  | 
 | ||||||
|  |         if (file.is_error()) { | ||||||
|  |             dbgln("FileSystemAccessServer: Couldn't open {}, error {}", path, file.error()); | ||||||
|  | 
 | ||||||
|  |             return { errno, Optional<IPC::File> {} }; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return { 0, IPC::File(file.value()->leak_fd(), IPC::File::CloseAfterSending) }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return { -1, Optional<IPC::File> {} }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Messages::FileSystemAccessServer::PromptOpenFileResponse ClientConnection::prompt_open_file(String const& path_to_view, Core::OpenMode const& requested_access) | ||||||
|  | { | ||||||
|  |     auto relevant_permissions = requested_access & (Core::OpenMode::ReadOnly | Core::OpenMode::WriteOnly); | ||||||
|  |     VERIFY(relevant_permissions != Core::OpenMode::NotOpen); | ||||||
|  | 
 | ||||||
|  |     auto main_window = GUI::Window::construct(); | ||||||
|  |     auto user_picked_file = GUI::FilePicker::get_open_filepath(main_window, "Select file", path_to_view); | ||||||
|  | 
 | ||||||
|  |     return prompt_helper<Messages::FileSystemAccessServer::PromptOpenFileResponse>(user_picked_file, requested_access); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Messages::FileSystemAccessServer::PromptSaveFileResponse ClientConnection::prompt_save_file(String const& name, String const& ext, String const& path_to_view, Core::OpenMode const& requested_access) | ||||||
|  | { | ||||||
|  |     auto relevant_permissions = requested_access & (Core::OpenMode::ReadOnly | Core::OpenMode::WriteOnly); | ||||||
|  |     VERIFY(relevant_permissions != Core::OpenMode::NotOpen); | ||||||
|  | 
 | ||||||
|  |     auto main_window = GUI::Window::construct(); | ||||||
|  |     auto user_picked_file = GUI::FilePicker::get_save_filepath(main_window, name, ext, path_to_view); | ||||||
|  | 
 | ||||||
|  |     return prompt_helper<Messages::FileSystemAccessServer::PromptSaveFileResponse>(user_picked_file, requested_access); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | template<typename T> | ||||||
|  | T ClientConnection::prompt_helper(Optional<String> const& user_picked_file, Core::OpenMode const& requested_access) | ||||||
|  | { | ||||||
|  |     if (user_picked_file.has_value()) { | ||||||
|  |         VERIFY(user_picked_file->starts_with("/"sv)); | ||||||
|  |         auto file = Core::File::open(user_picked_file.value(), requested_access); | ||||||
|  | 
 | ||||||
|  |         if (file.is_error()) { | ||||||
|  |             dbgln("FileSystemAccessServer: Couldn't open {}, error {}", user_picked_file.value(), file.error()); | ||||||
|  | 
 | ||||||
|  |             return { errno, Optional<IPC::File> {}, Optional<String> {} }; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         auto maybe_permissions = m_approved_files.get(user_picked_file.value()); | ||||||
|  |         auto new_permissions = requested_access & (Core::OpenMode::ReadOnly | Core::OpenMode::WriteOnly); | ||||||
|  |         if (maybe_permissions.has_value()) | ||||||
|  |             new_permissions |= maybe_permissions.value(); | ||||||
|  | 
 | ||||||
|  |         m_approved_files.set(user_picked_file.value(), new_permissions); | ||||||
|  | 
 | ||||||
|  |         return { 0, IPC::File(file.value()->leak_fd(), IPC::File::CloseAfterSending), user_picked_file.value() }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return { -1, Optional<IPC::File> {}, Optional<String> {} }; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										38
									
								
								Userland/Services/FileSystemAccessServer/ClientConnection.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								Userland/Services/FileSystemAccessServer/ClientConnection.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,38 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2021, timmot <tiwwot@protonmail.com> | ||||||
|  |  * | ||||||
|  |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <AK/HashMap.h> | ||||||
|  | #include <FileSystemAccessServer/FileSystemAccessClientEndpoint.h> | ||||||
|  | #include <FileSystemAccessServer/FileSystemAccessServerEndpoint.h> | ||||||
|  | #include <LibCore/Forward.h> | ||||||
|  | #include <LibIPC/ClientConnection.h> | ||||||
|  | 
 | ||||||
|  | namespace FileSystemAccessServer { | ||||||
|  | 
 | ||||||
|  | class ClientConnection final | ||||||
|  |     : public IPC::ClientConnection<FileSystemAccessClientEndpoint, FileSystemAccessServerEndpoint> { | ||||||
|  |     C_OBJECT(ClientConnection); | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     explicit ClientConnection(NonnullRefPtr<Core::LocalSocket>, int client_id); | ||||||
|  |     ~ClientConnection() override; | ||||||
|  | 
 | ||||||
|  |     virtual void die() override; | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     virtual Messages::FileSystemAccessServer::RequestFileResponse request_file(String const&, Core::OpenMode const&) override; | ||||||
|  |     virtual Messages::FileSystemAccessServer::PromptOpenFileResponse prompt_open_file(String const&, Core::OpenMode const&) override; | ||||||
|  |     virtual Messages::FileSystemAccessServer::PromptSaveFileResponse prompt_save_file(String const&, String const&, String const&, Core::OpenMode const&) override; | ||||||
|  | 
 | ||||||
|  |     template<typename T> | ||||||
|  |     T prompt_helper(Optional<String> const&, Core::OpenMode const&); | ||||||
|  | 
 | ||||||
|  |     HashMap<String, Core::OpenMode> m_approved_files; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,3 @@ | ||||||
|  | endpoint FileSystemAccessClient | ||||||
|  | { | ||||||
|  | } | ||||||
|  | @ -0,0 +1,9 @@ | ||||||
|  | #include <AK/URL.h> | ||||||
|  | #include <LibCore/IODevice.h> | ||||||
|  | 
 | ||||||
|  | endpoint FileSystemAccessServer | ||||||
|  | { | ||||||
|  |     request_file(String path, Core::OpenMode requested_access) => (i32 error, Optional<IPC::File> fd) | ||||||
|  |     prompt_open_file(String path_to_view, Core::OpenMode requested_access) => (i32 error, Optional<IPC::File> fd, Optional<String> chosen_file) | ||||||
|  |     prompt_save_file(String title, String ext, String path_to_view, Core::OpenMode requested_access) => (i32 error, Optional<IPC::File> fd, Optional<String> chosen_file) | ||||||
|  | } | ||||||
							
								
								
									
										26
									
								
								Userland/Services/FileSystemAccessServer/main.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								Userland/Services/FileSystemAccessServer/main.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,26 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2021, timmot <tiwwot@protonmail.com> | ||||||
|  |  * | ||||||
|  |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <FileSystemAccessServer/ClientConnection.h> | ||||||
|  | #include <LibCore/EventLoop.h> | ||||||
|  | #include <LibCore/LocalServer.h> | ||||||
|  | #include <LibGUI/Application.h> | ||||||
|  | #include <LibIPC/ClientConnection.h> | ||||||
|  | 
 | ||||||
|  | int main(int, char**) | ||||||
|  | { | ||||||
|  |     if (pledge("stdio recvfd sendfd rpath cpath wpath unix thread", nullptr) < 0) { | ||||||
|  |         perror("pledge"); | ||||||
|  |         return 1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     auto app = GUI::Application::construct(0, nullptr); | ||||||
|  |     app->set_quit_when_last_window_deleted(false); | ||||||
|  | 
 | ||||||
|  |     auto socket = Core::LocalSocket::take_over_accepted_socket_from_system_server(); | ||||||
|  |     IPC::new_client_connection<FileSystemAccessServer::ClientConnection>(socket.release_nonnull(), 1); | ||||||
|  |     return app->exec(); | ||||||
|  | } | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Timothy
						Timothy