mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 00:42:44 +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 | ||||
| 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] | ||||
| Socket=/tmp/portal/webcontent | ||||
| SocketPermissions=600 | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ add_subdirectory(Clipboard) | |||
| add_subdirectory(CrashDaemon) | ||||
| add_subdirectory(DHCPClient) | ||||
| add_subdirectory(EchoServer) | ||||
| add_subdirectory(FileSystemAccessServer) | ||||
| add_subdirectory(FileOperation) | ||||
| add_subdirectory(ImageDecoder) | ||||
| 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