457 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			457 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| //
 | |
| // Created by WolverinDEV on 29/04/2020.
 | |
| //
 | |
| #include <experimental/filesystem>
 | |
| #define FS_INCLUDED
 | |
| 
 | |
| #include <log/LogUtils.h>
 | |
| #include "LocalFileProvider.h"
 | |
| #include "clnpath.h"
 | |
| 
 | |
| using namespace ts::server::file;
 | |
| using namespace ts::server::file::filesystem;
 | |
| namespace fs = std::experimental::filesystem;
 | |
| using directory_query_response_t = AbstractProvider::directory_query_response_t;
 | |
| 
 | |
| LocalFileSystem::~LocalFileSystem() = default;
 | |
| 
 | |
| bool LocalFileSystem::initialize(std::string &error_message, const std::string &root_path_string) {
 | |
|     auto root_path = fs::u8path(root_path_string);
 | |
|     std::error_code error{};
 | |
| 
 | |
|     if(!fs::exists(root_path, error)) {
 | |
|         if(error)
 | |
|             logWarning(LOG_FT, "Failed to check root path existence. Assuming it does not exist. ({}/{})", error.value(), error.message());
 | |
|         if(!fs::create_directories(root_path, error) || error) {
 | |
|             error_message = "Failed to create root file system at " + root_path_string + ": " + std::to_string(error.value()) + "/" + error.message();
 | |
|             return false;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     auto croot = clnpath(fs::absolute(root_path).string());
 | |
|     logMessage(LOG_FT, "Started file system root at {}", croot);
 | |
|     this->root_path_ = croot;
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| void LocalFileSystem::finalize() {}
 | |
| 
 | |
| fs::path LocalFileSystem::server_path(const std::shared_ptr<VirtualFileServer> &server) {
 | |
|     return fs::u8path(this->root_path_) / fs::u8path("server_" + std::to_string(server->server_id()));
 | |
| }
 | |
| 
 | |
| fs::path LocalFileSystem::server_channel_path(const std::shared_ptr<VirtualFileServer> &server, ts::ChannelId cid) {
 | |
|     return this->server_path(server) / fs::u8path("channel_" + std::to_string(cid));
 | |
| }
 | |
| 
 | |
| bool LocalFileSystem::exceeds_base_path(const fs::path &base, const fs::path &target) {
 | |
|     auto rel_target = clnpath(target.string());
 | |
|     if(rel_target.starts_with("..")) return true;
 | |
| 
 | |
|     auto base_string = clnpath(fs::absolute(base).string());
 | |
|     auto target_string = clnpath(fs::absolute(target).string());
 | |
|     return !target_string.starts_with(base_string);
 | |
| }
 | |
| 
 | |
| bool LocalFileSystem::is_any_file_locked(const fs::path &base, const std::string &path, std::string &locked_file) {
 | |
|     auto c_path = clnpath(fs::absolute(base / fs::u8path(path)).string());
 | |
| 
 | |
|     std::lock_guard lock{this->locked_files_mutex};
 | |
|     for(const auto& lfile : this->locked_files_) {
 | |
|         if(lfile.starts_with(c_path)) {
 | |
|             locked_file = lfile.substr(base.string().length());
 | |
|             return true;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return false;
 | |
| }
 | |
| 
 | |
| std::string LocalFileSystem::target_file_path(FileCategory type, const std::shared_ptr<VirtualFileServer> &server, ts::ChannelId cid, const std::string &path) {
 | |
|     fs::path target_path{};
 | |
|     switch (type) {
 | |
|         case FileCategory::CHANNEL:
 | |
|             target_path = this->server_channel_path(server, cid) / path;
 | |
|             break;
 | |
|         case FileCategory::ICON:
 | |
|             target_path = this->server_path(server) / "icons" / path;
 | |
|             break;
 | |
|         case FileCategory::AVATAR:
 | |
|             target_path = this->server_path(server) / "avatars" / path;
 | |
|             break;
 | |
|     }
 | |
| 
 | |
|     return clnpath(fs::absolute(target_path).string());
 | |
| }
 | |
| 
 | |
| std::string LocalFileSystem::absolute_avatar_path(const std::shared_ptr<VirtualFileServer> &sid, const std::string &path) {
 | |
|     return this->target_file_path(FileCategory::AVATAR, sid, 0, path);
 | |
| }
 | |
| 
 | |
| std::string LocalFileSystem::absolute_icon_path(const std::shared_ptr<VirtualFileServer> &sid, const std::string &path) {
 | |
|     return this->target_file_path(FileCategory::ICON, sid, 0, path);
 | |
| }
 | |
| 
 | |
| std::string LocalFileSystem::absolute_channel_path(const std::shared_ptr<VirtualFileServer> &sid, ChannelId cid, const std::string &path) {
 | |
|     return this->target_file_path(FileCategory::CHANNEL, sid, cid, path);
 | |
| }
 | |
| 
 | |
| void LocalFileSystem::lock_file(const std::string &c_path) {
 | |
|     std::lock_guard lock{this->locked_files_mutex};
 | |
|     this->locked_files_.push_back(c_path);
 | |
| }
 | |
| 
 | |
| void LocalFileSystem::unlock_file(const std::string &c_path) {
 | |
|     std::lock_guard lock{this->locked_files_mutex};
 | |
| 
 | |
|     this->locked_files_.erase(std::remove_if(this->locked_files_.begin(), this->locked_files_.end(), [&](const auto& p) { return p == c_path; }), this->locked_files_.end());
 | |
| }
 | |
| 
 | |
| std::shared_ptr<ExecuteResponse<ServerCommandError>> LocalFileSystem::initialize_server(const std::shared_ptr<VirtualFileServer> &id) {
 | |
|     auto path = this->server_path(id);
 | |
|     std::error_code error{};
 | |
| 
 | |
|     auto response = this->create_execute_response<ServerCommandError>();
 | |
| 
 | |
|     if(!fs::exists(path, error)) {
 | |
|         if(!fs::create_directories(path, error) || error) {
 | |
|             response->emplace_fail(ServerCommandErrorType::FAILED_TO_CREATE_DIRECTORIES, std::to_string(error.value()) + "/" + error.message());
 | |
|             return response;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     //TODO: Copy the default icon
 | |
| 
 | |
|     response->emplace_success();
 | |
|     return response;
 | |
| }
 | |
| 
 | |
| std::shared_ptr<ExecuteResponse<ServerCommandError>> LocalFileSystem::delete_server(const std::shared_ptr<VirtualFileServer> &id) {
 | |
|     auto path = this->server_path(id);
 | |
|     std::error_code error{};
 | |
| 
 | |
|     auto response = this->create_execute_response<ServerCommandError>();
 | |
| 
 | |
|     //TODO: Stop all running file transfers!
 | |
| 
 | |
|     if(fs::exists(path, error)) {
 | |
|         if(!fs::remove_all(path, error) || error) {
 | |
|             response->emplace_fail(ServerCommandErrorType::FAILED_TO_DELETE_DIRECTORIES, std::to_string(error.value()) + "/" + error.message());
 | |
|             return response;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     response->emplace_success();
 | |
|     return response;
 | |
| }
 | |
| 
 | |
| std::shared_ptr<directory_query_response_t> LocalFileSystem::query_directory(const fs::path &base,
 | |
|                                                                              const std::string &path,
 | |
|                                                                              bool allow_non_existance) {
 | |
|     std::error_code error{};
 | |
|     auto response = this->create_execute_response<DirectoryQueryError, std::deque<DirectoryEntry>>();
 | |
|     auto target_path = base / fs::u8path(path);
 | |
|     if(this->exceeds_base_path(base, target_path)) {
 | |
|         response->emplace_fail(DirectoryQueryErrorType::PATH_EXCEEDS_ROOT_PATH, "");
 | |
|         return response;
 | |
|     }
 | |
| 
 | |
|     if(!fs::exists(target_path, error)) {
 | |
|         if(allow_non_existance)
 | |
|             response->emplace_success();
 | |
|         else
 | |
|             response->emplace_fail(DirectoryQueryErrorType::PATH_DOES_NOT_EXISTS, "");
 | |
|         return response;
 | |
|     } else if(error) {
 | |
|         logWarning(LOG_FT, "Failed to check for file at {}: {}. Assuming it does not exists.", target_path.string(), error.value(), error.message());
 | |
|         response->emplace_fail(DirectoryQueryErrorType::PATH_DOES_NOT_EXISTS, "");
 | |
|         return response;
 | |
|     }
 | |
| 
 | |
|     if(!fs::is_directory(target_path, error)) {
 | |
|         response->emplace_fail(DirectoryQueryErrorType::PATH_IS_A_FILE, "");
 | |
|         return response;
 | |
|     } else if(error) {
 | |
|         logWarning(LOG_FT, "Failed to check for directory at {}: {}. Assuming its not a directory.", target_path.string(), error.value(), error.message());
 | |
|         response->emplace_fail(DirectoryQueryErrorType::PATH_IS_A_FILE, "");
 | |
|         return response;
 | |
|     }
 | |
| 
 | |
|     std::deque<DirectoryEntry> entries{};
 | |
|     for(auto& entry : fs::directory_iterator(target_path, error)) {
 | |
|         auto status = entry.status(error);
 | |
|         if(error) {
 | |
|             logWarning(LOG_FT, "Failed to query file status for {} ({}/{}). Skipping entry for directory query.", entry.path().string(), error.value(), error.message());
 | |
|             continue;
 | |
|         }
 | |
| 
 | |
|         if(status.type() == fs::file_type::directory) {
 | |
|             auto& dentry = entries.emplace_back();
 | |
|             dentry.type = DirectoryEntry::DIRECTORY;
 | |
|             dentry.name = entry.path().filename();
 | |
| 
 | |
|             dentry.empty = fs::is_empty(entry.path(), error);
 | |
|             if(error)
 | |
|                 logWarning(LOG_FT, "Failed to query directory empty state for directory {} ({}/{})", target_path.string(), error.value(), error.message());
 | |
| 
 | |
|             dentry.modified_at = fs::last_write_time(entry.path(), error);
 | |
|             if(error)
 | |
|                 logWarning(LOG_FT, "Failed to query last write time for directory {} ({}/{})", entry.path().string(), error.value(), error.message());
 | |
|             dentry.size = 0;
 | |
|         } else if(status.type() == fs::file_type::regular) {
 | |
|             auto& dentry = entries.emplace_back();
 | |
|             dentry.type = DirectoryEntry::FILE;
 | |
|             dentry.name = entry.path().filename();
 | |
| 
 | |
|             dentry.modified_at = fs::last_write_time(entry.path(), error);
 | |
|             if(error)
 | |
|                 logWarning(LOG_FT, "Failed to query last write time for file {} ({}/{}).", entry.path().string(), error.value(), error.message());
 | |
|             dentry.size = fs::file_size(entry.path(), error);
 | |
|             if(error)
 | |
|                 logWarning(LOG_FT, "Failed to query size for file {} ({}/{}).", entry.path().string(), error.value(), error.message());
 | |
|         } else {
 | |
|             logWarning(LOG_FT, "Directory query listed an unknown file type for file {} ({}).", entry.path().string(), (int) status.type());
 | |
|         }
 | |
|     }
 | |
|     if(error && entries.empty()) {
 | |
|         response->emplace_fail(DirectoryQueryErrorType::FAILED_TO_LIST_FILES, std::to_string(error.value()) + "/" + error.message());
 | |
|         return response;
 | |
|     }
 | |
|     response->emplace_success(std::forward<decltype(entries)>(entries));
 | |
|     return response;
 | |
| }
 | |
| 
 | |
| std::shared_ptr<directory_query_response_t> LocalFileSystem::query_icon_directory(const std::shared_ptr<VirtualFileServer> &id) {
 | |
|     return this->query_directory(this->server_path(id) / fs::u8path("icons"), "/", true);
 | |
| }
 | |
| 
 | |
| std::shared_ptr<directory_query_response_t> LocalFileSystem::query_avatar_directory(const std::shared_ptr<VirtualFileServer> &id) {
 | |
|     return this->query_directory(this->server_path(id) / fs::u8path("avatars"), "/", true);
 | |
| }
 | |
| 
 | |
| std::shared_ptr<directory_query_response_t> LocalFileSystem::query_channel_directory(const std::shared_ptr<VirtualFileServer> &id, ChannelId channelId, const std::string &path) {
 | |
|     return this->query_directory(this->server_channel_path(id, channelId), path, false);
 | |
| }
 | |
| 
 | |
| std::shared_ptr<ExecuteResponse<DirectoryModifyError>> LocalFileSystem::create_channel_directory(const std::shared_ptr<VirtualFileServer> &id, ChannelId channelId, const std::string &path) {
 | |
|     auto channel_path_root = this->server_channel_path(id, channelId);
 | |
|     std::error_code error{};
 | |
| 
 | |
|     auto response = this->create_execute_response<DirectoryModifyError>();
 | |
|     auto target_path = channel_path_root / fs::u8path(path);
 | |
|     if(this->exceeds_base_path(channel_path_root, target_path)) {
 | |
|         response->emplace_fail(DirectoryModifyErrorType::PATH_EXCEEDS_ROOT_PATH, "");
 | |
|         return response;
 | |
|     }
 | |
| 
 | |
|     if(fs::exists(target_path, error)) {
 | |
|         response->emplace_fail(DirectoryModifyErrorType::PATH_ALREADY_EXISTS, "");
 | |
|         return response;
 | |
|     } else if(error) {
 | |
|         logWarning(LOG_FT, "Failed to check for file at {}: {}. Assuming it does not exists.", target_path.string(), error.value(), error.message());
 | |
|     }
 | |
| 
 | |
|     if(!fs::create_directories(target_path, error) || error) {
 | |
|         response->emplace_fail(DirectoryModifyErrorType::FAILED_TO_CREATE_DIRECTORIES, std::to_string(error.value()) + "/" + error.message());
 | |
|         return response;
 | |
|     }
 | |
| 
 | |
|     response->emplace_success();
 | |
|     return response;
 | |
| }
 | |
| 
 | |
| std::shared_ptr<ExecuteResponse<FileModifyError>> LocalFileSystem::rename_channel_file(const std::shared_ptr<VirtualFileServer> &id, ChannelId channelId, const std::string ¤t_path_string, ChannelId targetChannelId, const std::string &new_path_string) {
 | |
|     auto channel_path_root = this->server_channel_path(id, channelId);
 | |
|     auto target_path_root = this->server_channel_path(id, targetChannelId);
 | |
|     std::error_code error{};
 | |
|     std::string locked_file{};
 | |
| 
 | |
|     auto response = this->create_execute_response<FileModifyError>();
 | |
|     auto current_path = channel_path_root / fs::u8path(current_path_string);
 | |
|     auto target_path = target_path_root / fs::u8path(new_path_string);
 | |
| 
 | |
|     if(this->exceeds_base_path(channel_path_root, current_path)) {
 | |
|         response->emplace_fail(FileModifyErrorType::PATH_EXCEEDS_ROOT_PATH, "");
 | |
|         return response;
 | |
|     }
 | |
|     if(this->exceeds_base_path(target_path_root, target_path)) {
 | |
|         response->emplace_fail(FileModifyErrorType::TARGET_PATH_EXCEEDS_ROOT_PATH, "");
 | |
|         return response;
 | |
|     }
 | |
| 
 | |
|     if(!fs::exists(current_path, error)) {
 | |
|         response->emplace_fail(FileModifyErrorType::PATH_DOES_NOT_EXISTS, "");
 | |
|         return response;
 | |
|     } else if(error) {
 | |
|         logWarning(LOG_FT, "Failed to check for file at {}: {}. Assuming it does not exists.", current_path.string(), error.value(), error.message());
 | |
|         response->emplace_fail(FileModifyErrorType::PATH_DOES_NOT_EXISTS, "");
 | |
|         return response;
 | |
|     }
 | |
| 
 | |
|     if(!fs::exists(target_path.parent_path(), error)) {
 | |
|         if(!fs::create_directories(target_path.parent_path(), error)) {
 | |
|             response->emplace_fail(FileModifyErrorType::FAILED_TO_CREATE_DIRECTORIES, std::to_string(error.value()) + "/" + error.message());
 | |
|             return response;
 | |
|         }
 | |
|     } else if(error) {
 | |
|         logWarning(LOG_FT, "Failed to test for target directory existence for {}: {}/{}. Assuming it does exists", target_path.parent_path().string(), error.value(), error.message());
 | |
|     }
 | |
| 
 | |
|     if(fs::exists(target_path, error)) {
 | |
|         response->emplace_fail(FileModifyErrorType::TARGET_PATH_ALREADY_EXISTS, "");
 | |
|         return response;
 | |
|     } else if(error) {
 | |
|         logWarning(LOG_FT, "Failed to check for file at {}: {}. Assuming it does exists.", current_path.string(), error.value(), error.message());
 | |
|         response->emplace_fail(FileModifyErrorType::TARGET_PATH_ALREADY_EXISTS, "");
 | |
|         return response;
 | |
|     }
 | |
| 
 | |
|     if(this->is_any_file_locked(channel_path_root, current_path, locked_file)) {
 | |
|         response->emplace_fail(FileModifyErrorType::SOME_FILES_ARE_LOCKED, locked_file);
 | |
|         return response;
 | |
|     }
 | |
| 
 | |
|     fs::rename(current_path, target_path, error);
 | |
|     if(error) {
 | |
|         response->emplace_fail(FileModifyErrorType::FAILED_TO_RENAME_FILE, std::to_string(error.value()) + "/" + error.message());
 | |
|         return response;
 | |
|     }
 | |
| 
 | |
|     response->emplace_success();
 | |
|     return response;
 | |
| }
 | |
| 
 | |
| std::shared_ptr<ExecuteResponse<FileDeleteError, FileDeleteResponse>> LocalFileSystem::delete_files(const fs::path &base,
 | |
|                                                                                const std::vector<std::string> &paths) {
 | |
|     std::error_code error{};
 | |
|     std::string locked_file{};
 | |
|     auto response = this->create_execute_response<FileDeleteError, FileDeleteResponse>();
 | |
| 
 | |
|     std::vector<FileDeleteResponse::DeleteResult> delete_results{};
 | |
|     for(const auto& path : paths) {
 | |
|         auto target_path = base / fs::u8path(path);
 | |
| 
 | |
|         if(!fs::exists(target_path, error)) {
 | |
|             delete_results.emplace_back(FileDeleteResponse::StatusType::PATH_DOES_NOT_EXISTS, "");
 | |
|             continue;
 | |
|         } else if(error) {
 | |
|             logWarning(LOG_FT, "Failed to check for file at {}: {}. Assuming it does exists.", target_path.string(), error.value(), error.message());
 | |
|             delete_results.emplace_back(FileDeleteResponse::StatusType::PATH_DOES_NOT_EXISTS, "");
 | |
|             continue;
 | |
|         }
 | |
| 
 | |
|         if(this->is_any_file_locked(base, path, locked_file)) {
 | |
|             delete_results.emplace_back(FileDeleteResponse::StatusType::SOME_FILES_ARE_LOCKED, locked_file);
 | |
|             continue;
 | |
|         }
 | |
| 
 | |
|         if(!fs::remove_all(target_path, error) || error) {
 | |
|             delete_results.emplace_back(FileDeleteResponse::StatusType::FAILED_TO_DELETE_FILES, std::to_string(error.value()) + "/" + error.message());
 | |
|             continue;
 | |
|         }
 | |
| 
 | |
|         delete_results.emplace_back(FileDeleteResponse::StatusType::SUCCESS, "");
 | |
|     }
 | |
| 
 | |
|     response->emplace_success(FileDeleteResponse{delete_results});
 | |
|     return response;
 | |
| }
 | |
| 
 | |
| std::shared_ptr<ExecuteResponse<FileDeleteError, FileDeleteResponse>> LocalFileSystem::delete_channel_files(const std::shared_ptr<VirtualFileServer> &id, ChannelId channelId, const std::vector<std::string> &path) {
 | |
|     return this->delete_files(this->server_channel_path(id, channelId), path);
 | |
| }
 | |
| 
 | |
| std::shared_ptr<ExecuteResponse<FileDeleteError, FileDeleteResponse>> LocalFileSystem::delete_icons(const std::shared_ptr<VirtualFileServer> &id, const std::vector<std::string> &icon) {
 | |
|     return this->delete_files(this->server_path(id) / fs::u8path("icons"), icon);
 | |
| }
 | |
| 
 | |
| std::shared_ptr<ExecuteResponse<FileDeleteError, FileDeleteResponse>> LocalFileSystem::delete_avatars(const std::shared_ptr<VirtualFileServer> &id, const std::vector<std::string> &avatar) {
 | |
|     return this->delete_files(this->server_path(id) / fs::u8path("avatars"), avatar);
 | |
| }
 | |
| 
 | |
| std::shared_ptr<ExecuteResponse<FileInfoError, FileInfoResponse>> LocalFileSystem::query_file_info(const std::vector<std::tuple<fs::path, std::string>> &paths) {
 | |
|     std::error_code error{};
 | |
|     auto response = this->create_execute_response<FileInfoError, FileInfoResponse>();
 | |
|     std::vector<FileInfoResponse::FileInfo> file_infos{};
 | |
|     file_infos.reserve(paths.size());
 | |
| 
 | |
|     for(const auto& [base, path] : paths) {
 | |
|         auto target_path = base / fs::u8path(path);
 | |
|         if(this->exceeds_base_path(base, target_path)) {
 | |
|             file_infos.emplace_back(FileInfoResponse::StatusType::PATH_EXCEEDS_ROOT_PATH, "", DirectoryEntry{});
 | |
|             continue;
 | |
|         }
 | |
| 
 | |
|         if(!fs::exists(target_path, error)) {
 | |
|             file_infos.emplace_back(FileInfoResponse::StatusType::PATH_DOES_NOT_EXISTS, "", DirectoryEntry{});
 | |
|             continue;
 | |
|         } else if(error) {
 | |
|             logWarning(LOG_FT, "Failed to check for file at {}: {}. Assuming it does not exists.", target_path.string(), error.value(), error.message());
 | |
| 
 | |
|             file_infos.emplace_back(FileInfoResponse::StatusType::PATH_DOES_NOT_EXISTS, "", DirectoryEntry{});
 | |
|             continue;
 | |
|         }
 | |
| 
 | |
|         auto status = fs::status(target_path, error);
 | |
|         if(error) {
 | |
|             logWarning(LOG_FT, "Failed to query file status for {} ({}/{}). Skipping entry for file info query.", target_path.string(), error.value(), error.message());
 | |
| 
 | |
|             file_infos.emplace_back(FileInfoResponse::StatusType::FAILED_TO_QUERY_INFO, "", DirectoryEntry{});
 | |
|             continue;
 | |
|         }
 | |
| 
 | |
|         if(status.type() == fs::file_type::directory) {
 | |
|             DirectoryEntry dentry{};
 | |
|             dentry.type = DirectoryEntry::DIRECTORY;
 | |
|             dentry.name = target_path.filename();
 | |
|             dentry.empty = fs::is_empty(target_path, error);
 | |
|             if(error)
 | |
|                 logWarning(LOG_FT, "Failed to query directory empty state for directory {} ({}/{})", target_path.string(), error.value(), error.message());
 | |
| 
 | |
|             dentry.modified_at = fs::last_write_time(target_path, error);
 | |
|             if(error)
 | |
|                 logWarning(LOG_FT, "Failed to query last write time for directory {} ({}/{})", target_path.string(), error.value(), error.message());
 | |
|             dentry.size = 0;
 | |
|             file_infos.emplace_back(FileInfoResponse::StatusType::SUCCESS, "", dentry);
 | |
|         } else if(status.type() == fs::file_type::regular) {
 | |
|             DirectoryEntry dentry{};
 | |
|             dentry.type = DirectoryEntry::FILE;
 | |
|             dentry.name = target_path.filename();
 | |
| 
 | |
|             dentry.modified_at = fs::last_write_time(target_path, error);
 | |
|             if(error)
 | |
|                 logWarning(LOG_FT, "Failed to query last write time for file {} ({}/{}).", target_path.string(), error.value(), error.message());
 | |
|             dentry.size = fs::file_size(target_path, error);
 | |
|             if(error)
 | |
|                 logWarning(LOG_FT, "Failed to query size for file {} ({}/{}).", target_path.string(), error.value(), error.message());
 | |
|             file_infos.emplace_back(FileInfoResponse::StatusType::SUCCESS, "", dentry);
 | |
|         } else {
 | |
|             logWarning(LOG_FT, "File info query contains an unknown file type for file {} ({}).", target_path.string(), (int) status.type());
 | |
|             file_infos.emplace_back(FileInfoResponse::StatusType::UNKNOWN_FILE_TYPE, "", DirectoryEntry{});
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     response->emplace_success(FileInfoResponse{file_infos});
 | |
|     return response;
 | |
| }
 | |
| 
 | |
| std::shared_ptr<ExecuteResponse<FileInfoError, FileInfoResponse>> LocalFileSystem::query_channel_info(const std::shared_ptr<VirtualFileServer> &sid, const std::vector<std::tuple<ChannelId, std::string>>& files) {
 | |
|     std::vector<std::tuple<fs::path, std::string>> file_paths{};
 | |
|     for(const auto& [channelId, path] : files)
 | |
|         file_paths.emplace_back(this->server_channel_path(sid, channelId), path);
 | |
|     return this->query_file_info(file_paths);
 | |
| }
 | |
| 
 | |
| std::shared_ptr<ExecuteResponse<FileInfoError, FileInfoResponse>> LocalFileSystem::query_icon_info(const std::shared_ptr<VirtualFileServer> &sid, const std::vector<std::string> &paths) {
 | |
|     std::vector<std::tuple<fs::path, std::string>> file_paths{};
 | |
|     for(const auto& path : paths)
 | |
|         file_paths.emplace_back(this->server_path(sid) / fs::u8path("icons"), path);
 | |
|     return this->query_file_info(file_paths);
 | |
| }
 | |
| 
 | |
| std::shared_ptr<ExecuteResponse<FileInfoError, FileInfoResponse> > LocalFileSystem::query_avatar_info(const std::shared_ptr<VirtualFileServer> &sid, const std::vector<std::string> &paths) {
 | |
|     std::vector<std::tuple<fs::path, std::string>> file_paths{};
 | |
|     for(const auto& path : paths)
 | |
|         file_paths.emplace_back(this->server_path(sid) / fs::u8path("avatars"), path);
 | |
|     return this->query_file_info(file_paths);
 | |
| } |