diff --git a/sphaira/source/fs.cpp b/sphaira/source/fs.cpp index 17aafbb..813f7e1 100644 --- a/sphaira/source/fs.cpp +++ b/sphaira/source/fs.cpp @@ -235,7 +235,7 @@ Result CreateDirectoryRecursivelyWithPath(FsFileSystem* fs, const FsPath& _path, // strip file name form path. const auto last_slash = std::strrchr(_path, '/'); - if (!last_slash) { + if (!last_slash || last_slash == _path.s) { R_SUCCEED(); } diff --git a/sphaira/source/minizip_helper.cpp b/sphaira/source/minizip_helper.cpp index f6aef04..8370265 100644 --- a/sphaira/source/minizip_helper.cpp +++ b/sphaira/source/minizip_helper.cpp @@ -223,7 +223,6 @@ long minizip_seek_file_func_stdio(voidpf opaque, voidpf stream, ZPOS64_T offset, uLong minizip_read_file_func_stdio(voidpf opaque, voidpf stream, void* buf, uLong size) { auto file = static_cast(stream); - log_write("[ZIP] doing read\n"); return std::fread(buf, 1, size, file); } diff --git a/sphaira/source/threaded_file_transfer.cpp b/sphaira/source/threaded_file_transfer.cpp index 0e351c3..f2967c0 100644 --- a/sphaira/source/threaded_file_transfer.cpp +++ b/sphaira/source/threaded_file_transfer.cpp @@ -777,9 +777,14 @@ Result TransferUnzipAll(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs continue; } + const auto path_len = std::strlen(path); + if (!path_len) { + continue; + } + pbox->NewTransfer(name); - if (path[std::strlen(path) -1] == '/') { + if (path[path_len -1] == '/') { Result rc; if (R_FAILED(rc = fs->CreateDirectoryRecursively(path)) && rc != FsError_PathAlreadyExists) { log_write("failed to create folder: %s 0x%04X\n", path.s, rc); diff --git a/sphaira/source/ui/menus/save_menu.cpp b/sphaira/source/ui/menus/save_menu.cpp index 52ac22d..6ecb2dd 100644 --- a/sphaira/source/ui/menus/save_menu.cpp +++ b/sphaira/source/ui/menus/save_menu.cpp @@ -730,17 +730,23 @@ void Menu::BackupSaves(std::vector>& entries) { void Menu::RestoreSave() { dump::DumpGetLocation("Select restore location"_i18n, dump::DumpLocationFlag_SdCard|dump::DumpLocationFlag_Stdio, [this](const dump::DumpLocation& location){ - std::unique_ptr fs; + std::unique_ptr fs{}; + fs::FsPath mount{}; + if (location.entry.type == dump::DumpLocationType_Stdio) { + mount = fs::AppendPath(location.stdio[location.entry.index].mount, location.stdio[location.entry.index].dump_path); fs = std::make_unique(true, location.stdio[location.entry.index].mount); } else if (location.entry.type == dump::DumpLocationType_SdCard) { fs = std::make_unique(); + } else { + App::PushErrorBox(MAKERESULT(Module_Libnx, LibnxError_BadInput), "Invalid location type!"_i18n); + return; } // get saves in /Saves/Name and /Saves/app_id filebrowser::FsDirCollection collections[2]{}; for (auto i = 0; i < std::size(collections); i++) { - const auto save_path = fs::AppendPath(fs->Root(), BuildSaveBasePath(m_entries[m_index], i != 0)); + const auto save_path = fs::AppendPath(mount, BuildSaveBasePath(m_entries[m_index], i != 0)); filebrowser::FsView::get_collection(fs.get(), save_path, "", collections[i], true, false, false); // reverse as they will be sorted in oldest -> newest. // todo: better impl when both id and normal app folders are used. @@ -763,7 +769,7 @@ void Menu::RestoreSave() { if (paths.empty()) { App::Push( - "No saves found in "_i18n + fs::AppendPath(fs->Root(), BuildSaveBasePath(m_entries[m_index])).toString(), + "No saves found in "_i18n + fs::AppendPath(mount, BuildSaveBasePath(m_entries[m_index])).toString(), "OK"_i18n ); return; diff --git a/sphaira/source/utils/devoptab_ftp.cpp b/sphaira/source/utils/devoptab_ftp.cpp index 7c07376..e63928a 100644 --- a/sphaira/source/utils/devoptab_ftp.cpp +++ b/sphaira/source/utils/devoptab_ftp.cpp @@ -58,13 +58,13 @@ private: static bool ftp_parse_mlist(std::string_view chunk, struct stat* st); std::pair ftp_quote(std::span commands, bool is_dir, std::vector* response_data = nullptr); - bool ftp_dirlist(const std::string& path, DirEntries& out); - bool ftp_stat(const std::string& path, struct stat* st, bool is_dir); - bool ftp_remove_file_folder(const std::string& path, bool is_dir); - bool ftp_unlink(const std::string& path); - bool ftp_rename(const std::string& old_path, const std::string& new_path, bool is_dir); - bool ftp_mkdir(const std::string& path); - bool ftp_rmdir(const std::string& path); + int ftp_dirlist(const std::string& path, DirEntries& out); + int ftp_stat(const std::string& path, struct stat* st, bool is_dir); + int ftp_remove_file_folder(const std::string& path, bool is_dir); + int ftp_unlink(const std::string& path); + int ftp_rename(const std::string& old_path, const std::string& new_path, bool is_dir); + int ftp_mkdir(const std::string& path); + int ftp_rmdir(const std::string& path); private: bool mounted{}; @@ -267,7 +267,7 @@ std::pair Device::ftp_quote(std::span commands, b return {true, response_code}; } -bool Device::ftp_dirlist(const std::string& path, DirEntries& out) { +int Device::ftp_dirlist(const std::string& path, DirEntries& out) { const auto url = build_url(path, true); std::vector chunk; @@ -279,51 +279,82 @@ bool Device::ftp_dirlist(const std::string& path, DirEntries& out) { const auto res = curl_easy_perform(this->curl); if (res != CURLE_OK) { log_write("[FTP] curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); - return false; + return -EIO; + } + + long response_code = 0; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response_code); + + switch (response_code) { + case 125: // Data connection already open; transfer starting. + case 150: // File status okay; about to open data connection. + case 226: // Closing data connection. Requested file action successful. + break; + case 450: // Requested file action not taken. File unavailable (e.g., file busy). + case 550: // Requested action not taken. File unavailable (e.g., file not found, no access). + return -ENOENT; + default: + return -EIO; } ftp_parse_mlsd({chunk.data(), chunk.size()}, out); - return true; + return 0; } -bool Device::ftp_stat(const std::string& path, struct stat* st, bool is_dir) { +int Device::ftp_stat(const std::string& path, struct stat* st, bool is_dir) { std::memset(st, 0, sizeof(*st)); std::vector chunk; const auto [success, response_code] = ftp_quote({"MLST " + path}, is_dir, &chunk); if (!success) { - return false; + return -EIO; } - if (!success || response_code >= 400) { - log_write("[FTP] MLST command failed with response code: %ld\n", response_code); - return false; + switch (response_code) { + case 250: // Requested file action okay, completed. + break; + case 450: // Requested file action not taken. File unavailable (e.g., file busy). + case 550: // Requested action not taken. File unavailable (e.g., file not found, no access). + return -ENOENT; + default: + return -EIO; } if (!ftp_parse_mlist({chunk.data(), chunk.size()}, st)) { log_write("[FTP] Failed to parse MLST response for path: %s\n", path.c_str()); - return false; + return -EIO; } - return true; + return 0; } -bool Device::ftp_remove_file_folder(const std::string& path, bool is_dir) { +int Device::ftp_remove_file_folder(const std::string& path, bool is_dir) { const auto cmd = (is_dir ? "RMD " : "DELE ") + path; const auto [success, response_code] = ftp_quote({cmd}, is_dir); - if (!success || response_code >= 400) { - log_write("[FTP] MLST command failed with response code: %ld\n", response_code); - return false; + + if (!success) { + return -EIO; } - return true; + switch (response_code) { + case 250: // Requested file action okay, completed. + case 200: // Command okay. + break; + case 450: // Requested file action not taken. File unavailable (e.g., file busy). + case 550: // Requested action not taken. File unavailable (e.g., file not found, no access). + return -ENOENT; + default: + return -EIO; + } + + return 0; } -bool Device::ftp_unlink(const std::string& path) { +int Device::ftp_unlink(const std::string& path) { return ftp_remove_file_folder(path, false); } -bool Device::ftp_rename(const std::string& old_path, const std::string& new_path, bool is_dir) { +int Device::ftp_rename(const std::string& old_path, const std::string& new_path, bool is_dir) { const auto url = build_url("/", is_dir); std::vector commands; @@ -331,31 +362,50 @@ bool Device::ftp_rename(const std::string& old_path, const std::string& new_path commands.emplace_back("RNTO " + new_path); const auto [success, response_code] = ftp_quote(commands, is_dir); - if (!success || response_code >= 400) { - log_write("[FTP] MLST command failed with response code: %ld\n", response_code); - return false; + if (!success) { + return -EIO; } - return true; + switch (response_code) { + case 250: // Requested file action okay, completed. + case 200: // Command okay. + break; + case 450: // Requested file action not taken. File unavailable (e.g., file busy). + case 550: // Requested action not taken. File unavailable (e.g., file not found, no access). + return -ENOENT; + case 553: // Requested action not taken. File name not allowed. + return -EEXIST; + default: + return -EIO; + } + + return 0; } -bool Device::ftp_mkdir(const std::string& path) { +int Device::ftp_mkdir(const std::string& path) { std::vector chunk; const auto [success, response_code] = ftp_quote({"MKD " + path}, true); if (!success) { - return false; + return -EIO; } - // todo: handle result if directory already exists. - if (response_code >= 400) { - log_write("[FTP] MLST command failed with response code: %ld\n", response_code); - return false; + switch (response_code) { + case 257: // "PATHNAME" created. + case 250: // Requested file action okay, completed. + case 200: // Command okay. + break; + case 550: // Requested action not taken. File unavailable (e.g., file not found, no access). + return -ENOENT; // Parent directory does not exist or no permission. + case 521: // Directory already exists. + return -EEXIST; + default: + return -EIO; } - return true; + return 0; } -bool Device::ftp_rmdir(const std::string& path) { +int Device::ftp_rmdir(const std::string& path) { return ftp_remove_file_folder(path, true); } @@ -401,9 +451,9 @@ int Device::devoptab_open(void *fileStruct, const char *path, int flags, int mod if ((flags & O_ACCMODE) == O_RDONLY || (flags & O_APPEND)) { // ensure the file exists and get its size. - if (!ftp_stat(path, &st, false)) { - log_write("[FTP] File not found: %s\n", path); - return -ENOENT; + const auto ret = ftp_stat(path, &st, false); + if (ret < 0) { + return ret; } if (st.st_mode & S_IFDIR) { @@ -522,32 +572,44 @@ int Device::devoptab_fstat(void *fd, struct stat *st) { } int Device::devoptab_unlink(const char *path) { - if (!ftp_unlink(path)) { - return -EIO; + const auto ret = ftp_unlink(path); + if (ret < 0) { + log_write("[FTP] ftp_unlink() failed: %s errno: %s\n", path, std::strerror(-ret)); + return ret; } return 0; } int Device::devoptab_rename(const char *oldName, const char *newName) { - if (!ftp_rename(oldName, newName, false) && !ftp_rename(oldName, newName, true)) { - return -EIO; + auto ret = ftp_rename(oldName, newName, false); + if (ret == -ENOENT) { + ret = ftp_rename(oldName, newName, true); + } + + if (ret < 0) { + log_write("[FTP] ftp_rename() failed: %s -> %s errno: %s\n", oldName, newName, std::strerror(-ret)); + return ret; } return 0; } int Device::devoptab_mkdir(const char *path, int mode) { - if (!ftp_mkdir(path)) { - return -EIO; + const auto ret = ftp_mkdir(path); + if (ret < 0) { + log_write("[FTP] ftp_mkdir() failed: %s errno: %s\n", path, std::strerror(-ret)); + return ret; } return 0; } int Device::devoptab_rmdir(const char *path) { - if (!ftp_rmdir(path)) { - return -EIO; + const auto ret = ftp_rmdir(path); + if (ret < 0) { + log_write("[FTP] ftp_rmdir() failed: %s errno: %s\n", path, std::strerror(-ret)); + return ret; } return 0; @@ -557,9 +619,11 @@ int Device::devoptab_diropen(void* fd, const char *path) { auto dir = static_cast(fd); auto entries = new DirEntries(); - if (!ftp_dirlist(path, *entries)) { + const auto ret = ftp_dirlist(path, *entries); + if (ret < 0) { + log_write("[FTP] ftp_dirlist() failed: %s errno: %s\n", path, std::strerror(-ret)); delete entries; - return -ENOENT; + return ret; } dir->entries = entries; @@ -602,8 +666,14 @@ int Device::devoptab_dirclose(void* fd) { } int Device::devoptab_lstat(const char *path, struct stat *st) { - if (!ftp_stat(path, st, false) && !ftp_stat(path, st, true)) { - return -ENOENT; + auto ret = ftp_stat(path, st, false); + if (ret == -ENOENT) { + ret = ftp_stat(path, st, true); + } + + if (ret < 0) { + log_write("[FTP] ftp_stat() failed: %s errno: %s\n", path, std::strerror(-ret)); + return ret; } return 0; @@ -614,7 +684,7 @@ int Device::devoptab_ftruncate(void *fd, off_t len) { if (!file->write_mode) { log_write("[FTP] Attempt to truncate a read-only file\n"); - return EBADF; + return -EBADF; } file->entry->st.st_size = len; diff --git a/sphaira/source/utils/devoptab_http.cpp b/sphaira/source/utils/devoptab_http.cpp index 82d2f5e..97e70d9 100644 --- a/sphaira/source/utils/devoptab_http.cpp +++ b/sphaira/source/utils/devoptab_http.cpp @@ -59,14 +59,14 @@ private: int devoptab_dirclose(void* fd) override; int devoptab_lstat(const char *path, struct stat *st) override; - bool http_dirlist(const std::string& path, DirEntries& out); - bool http_stat(const std::string& path, struct stat* st, bool is_dir); + int http_dirlist(const std::string& path, DirEntries& out); + int http_stat(const std::string& path, struct stat* st, bool is_dir); private: bool mounted{}; }; -bool Device::http_dirlist(const std::string& path, DirEntries& out) { +int Device::http_dirlist(const std::string& path, DirEntries& out) { const auto url = build_url(path, true); std::vector chunk; @@ -79,7 +79,29 @@ bool Device::http_dirlist(const std::string& path, DirEntries& out) { const auto res = curl_easy_perform(this->curl); if (res != CURLE_OK) { log_write("[HTTP] curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); - return false; + return -EIO; + } + + long response_code = 0; + curl_easy_getinfo(this->curl, CURLINFO_RESPONSE_CODE, &response_code); + + switch (response_code) { + case 200: // OK + case 206: // Partial Content + break; + case 301: // Moved Permanently + case 302: // Found + case 303: // See Other + case 307: // Temporary Redirect + case 308: // Permanent Redirect + return -EIO; + case 401: // Unauthorized + case 403: // Forbidden + return -EACCES; + case 404: // Not Found + return -ENOENT; + default: + return -EIO; } log_write("[HTTP] Received %zu bytes for directory listing\n", chunk.size()); @@ -147,10 +169,10 @@ bool Device::http_dirlist(const std::string& path, DirEntries& out) { log_write("[HTTP] Parsed %zu entries from directory listing\n", out.size()); - return true; + return 0; } -bool Device::http_stat(const std::string& path, struct stat* st, bool is_dir) { +int Device::http_stat(const std::string& path, struct stat* st, bool is_dir) { std::memset(st, 0, sizeof(*st)); const auto url = build_url(path, is_dir); @@ -161,7 +183,7 @@ bool Device::http_stat(const std::string& path, struct stat* st, bool is_dir) { const auto res = curl_easy_perform(this->curl); if (res != CURLE_OK) { log_write("[HTTP] curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); - return false; + return -EIO; } long response_code = 0; @@ -179,10 +201,23 @@ bool Device::http_stat(const std::string& path, struct stat* st, bool is_dir) { const char* effective_url{}; curl_easy_getinfo(this->curl, CURLINFO_EFFECTIVE_URL, &effective_url); - // handle error codes. - if (response_code != 200 && response_code != 206) { - log_write("[HTTP] Unexpected HTTP response code: %ld\n", response_code); - return false; + switch (response_code) { + case 200: // OK + case 206: // Partial Content + break; + case 301: // Moved Permanently + case 302: // Found + case 303: // See Other + case 307: // Temporary Redirect + case 308: // Permanent Redirect + return -EIO; + case 401: // Unauthorized + case 403: // Forbidden + return -EACCES; + case 404: // Not Found + return -ENOENT; + default: + return -EIO; } if (effective_url) { @@ -207,7 +242,7 @@ bool Device::http_stat(const std::string& path, struct stat* st, bool is_dir) { st->st_ctime = st->st_mtime; st->st_nlink = 1; - return true; + return 0; } bool Device::Mount() { @@ -229,9 +264,10 @@ int Device::devoptab_open(void *fileStruct, const char *path, int flags, int mod auto file = static_cast(fileStruct); struct stat st; - if (!http_stat(path, &st, false)) { - log_write("[HTTP] http_stat() failed for file: %s\n", path); - return -ENOENT; + const auto ret = http_stat(path, &st, false); + if (ret < 0) { + log_write("[HTTP] http_stat() failed for file: %s errno: %s\n", path, std::strerror(-ret)); + return ret; } if (st.st_mode & S_IFDIR) { @@ -306,9 +342,11 @@ int Device::devoptab_diropen(void* fd, const char *path) { log_write("[HTTP] Opening directory: %s\n", path); auto entries = new DirEntries(); - if (!http_dirlist(path, *entries)) { + const auto ret = http_dirlist(path, *entries); + if (ret < 0) { + log_write("[HTTP] http_dirlist() failed for directory: %s errno: %s\n", path, std::strerror(-ret)); delete entries; - return -ENOENT; + return ret; } log_write("[HTTP] Opened directory: %s with %zu entries\n", path, entries->size()); @@ -352,8 +390,14 @@ int Device::devoptab_dirclose(void* fd) { } int Device::devoptab_lstat(const char *path, struct stat *st) { - if (!http_stat(path, st, false) && !http_stat(path, st, true)) { - return -ENOENT; + auto ret = http_stat(path, st, false); + if (ret < 0) { + ret = http_stat(path, st, true); + } + + if (ret < 0) { + log_write("[HTTP] http_stat() failed for path: %s errno: %s\n", path, std::strerror(-ret)); + return ret; } return 0; diff --git a/sphaira/source/utils/devoptab_webdav.cpp b/sphaira/source/utils/devoptab_webdav.cpp index e45baac..13dc1d6 100644 --- a/sphaira/source/utils/devoptab_webdav.cpp +++ b/sphaira/source/utils/devoptab_webdav.cpp @@ -73,13 +73,13 @@ private: int devoptab_fsync(void *fd) override; std::pair webdav_custom_command(const std::string& path, const std::string& cmd, std::string_view postfields, std::span headers, bool is_dir, std::vector* response_data = nullptr); - bool webdav_dirlist(const std::string& path, DirEntries& out); - bool webdav_stat(const std::string& path, struct stat* st, bool is_dir); - bool webdav_remove_file_folder(const std::string& path, bool is_dir); - bool webdav_unlink(const std::string& path); - bool webdav_rename(const std::string& old_path, const std::string& new_path, bool is_dir); - bool webdav_mkdir(const std::string& path); - bool webdav_rmdir(const std::string& path); + int webdav_dirlist(const std::string& path, DirEntries& out); + int webdav_stat(const std::string& path, struct stat* st, bool is_dir); + int webdav_remove_file_folder(const std::string& path, bool is_dir); + int webdav_unlink(const std::string& path); + int webdav_rename(const std::string& old_path, const std::string& new_path, bool is_dir); + int webdav_mkdir(const std::string& path); + int webdav_rmdir(const std::string& path); }; size_t dummy_data_callback(char *ptr, size_t size, size_t nmemb, void *userdata) { @@ -126,7 +126,7 @@ std::pair Device::webdav_custom_command(const std::string& path, con return {true, response_code}; } -bool Device::webdav_dirlist(const std::string& path, DirEntries& out) { +int Device::webdav_dirlist(const std::string& path, DirEntries& out) { const std::string_view post_fields = "" "" @@ -143,9 +143,20 @@ bool Device::webdav_dirlist(const std::string& path, DirEntries& out) { std::vector chunk; const auto [success, response_code] = webdav_custom_command(path, "PROPFIND", post_fields, custom_headers, true, &chunk); - if (!success || response_code != 207) { - log_write("[WEBDAV] PROPFIND failed or returned HTTP error code: %ld\n", response_code); - return false; + if (!success) { + return -EIO; + } + + switch (response_code) { + case 207: // Multi-Status + break; + case 404: // Not Found + return -ENOENT; + case 403: // Forbidden + return -EACCES; + default: + log_write("[WEBDAV] Unexpected HTTP response code: %ld\n", response_code); + return -EIO; } SCOPED_TIMESTAMP("webdav_dirlist parse"); @@ -154,7 +165,7 @@ bool Device::webdav_dirlist(const std::string& path, DirEntries& out) { const auto result = doc.load_buffer_inplace(chunk.data(), chunk.size()); if (!result) { log_write("[WEBDAV] Failed to parse XML: %s\n", result.description()); - return false; + return -EIO; } log_write("\n[WEBDAV] XML parsed successfully\n"); @@ -219,11 +230,11 @@ bool Device::webdav_dirlist(const std::string& path, DirEntries& out) { log_write("[WEBDAV] Parsed %zu entries from directory listing\n", out.size()); - return true; + return 0; } // todo: use PROPFIND to get file size and time, although it is slower... -bool Device::webdav_stat(const std::string& path, struct stat* st, bool is_dir) { +int Device::webdav_stat(const std::string& path, struct stat* st, bool is_dir) { std::memset(st, 0, sizeof(*st)); const auto url = build_url(path, is_dir); @@ -234,7 +245,7 @@ bool Device::webdav_stat(const std::string& path, struct stat* st, bool is_dir) const auto res = curl_easy_perform(this->curl); if (res != CURLE_OK) { log_write("[WEBDAV] curl_easy_perform() failed: %s\n", curl_easy_strerror(res)); - return false; + return -EIO; } long response_code = 0; @@ -252,10 +263,17 @@ bool Device::webdav_stat(const std::string& path, struct stat* st, bool is_dir) const char* effective_url{}; curl_easy_getinfo(this->curl, CURLINFO_EFFECTIVE_URL, &effective_url); - // handle error codes. - if (response_code != 200 && response_code != 206) { - log_write("[WEBDAV] Unexpected HTTP response code: %ld\n", response_code); - return false; + switch (response_code) { + case 200: // OK + case 206: // Partial Content + break; + case 404: // Not Found + return -ENOENT; + case 403: // Forbidden + return -EACCES; + default: + log_write("[WEBDAV] Unexpected HTTP response code: %ld\n", response_code); + return -EIO; } if (effective_url) { @@ -280,24 +298,35 @@ bool Device::webdav_stat(const std::string& path, struct stat* st, bool is_dir) st->st_ctime = st->st_mtime; st->st_nlink = 1; - return true; + return 0; } -bool Device::webdav_remove_file_folder(const std::string& path, bool is_dir) { +int Device::webdav_remove_file_folder(const std::string& path, bool is_dir) { const auto [success, response_code] = webdav_custom_command(path, "DELETE", "", {}, is_dir); - if (!success || (response_code != 200 && response_code != 204)) { - log_write("[WEBDAV] DELETE command failed with response code: %ld\n", response_code); - return false; + if (!success) { + return -EIO; } - return true; + switch (response_code) { + case 200: // OK + case 204: // No Content + return 0; + case 404: // Not Found + return -ENOENT; + case 403: // Forbidden + return -EACCES; + case 409: // Conflict + return -ENOTEMPTY; // Directory not empty + default: + return -EIO; + } } -bool Device::webdav_unlink(const std::string& path) { +int Device::webdav_unlink(const std::string& path) { return webdav_remove_file_folder(path, false); } -bool Device::webdav_rename(const std::string& old_path, const std::string& new_path, bool is_dir) { +int Device::webdav_rename(const std::string& old_path, const std::string& new_path, bool is_dir) { log_write("[WEBDAV] Renaming %s to %s\n", old_path.c_str(), new_path.c_str()); const std::string custom_headers[] = { @@ -306,25 +335,49 @@ bool Device::webdav_rename(const std::string& old_path, const std::string& new_p }; const auto [success, response_code] = webdav_custom_command(old_path, "MOVE", "", custom_headers, is_dir); - if (!success || (response_code != 200 && response_code != 201 && response_code != 204)) { - log_write("[WEBDAV] MOVE command failed with response code: %ld\n", response_code); - return false; + + if (!success) { + return -EIO; } - return true; + switch (response_code) { + case 201: // Created + case 204: // No Content + return 0; + case 404: // Not Found + return -ENOENT; + case 403: // Forbidden + return -EACCES; + case 412: // Precondition Failed + return -EEXIST; // Destination already exists and Overwrite is F + case 409: // Conflict + return -ENOENT; // Parent directory of destination does not exist + default: + return -EIO; + } } -bool Device::webdav_mkdir(const std::string& path) { +int Device::webdav_mkdir(const std::string& path) { const auto [success, response_code] = webdav_custom_command(path, "MKCOL", "", {}, true); - if (!success || response_code != 201) { - log_write("[WEBDAV] MKCOL command failed with response code: %ld\n", response_code); - return false; + if (!success) { + return -EIO; } - return true; + switch (response_code) { + case 201: // Created + return 0; + case 405: // Method Not Allowed + return -EEXIST; // Collection already exists + case 409: // Conflict + return -ENOENT; // Parent collection does not exist + case 403: // Forbidden + return -EACCES; + default: + return -EIO; + } } -bool Device::webdav_rmdir(const std::string& path) { +int Device::webdav_rmdir(const std::string& path) { return webdav_remove_file_folder(path, true); } @@ -339,9 +392,9 @@ int Device::devoptab_open(void *fileStruct, const char *path, int flags, int mod if ((flags & O_ACCMODE) == O_RDONLY) { // ensure the file exists and get its size. - if (!webdav_stat(path, &st, false)) { - log_write("[WEBDAV] File not found: %s\n", path); - return -ENOENT; + const auto ret = webdav_stat(path, &st, false); + if (ret < 0) { + return ret; } if (st.st_mode & S_IFDIR) { @@ -456,32 +509,44 @@ int Device::devoptab_fstat(void *fd, struct stat *st) { } int Device::devoptab_unlink(const char *path) { - if (!webdav_unlink(path)) { - return -EIO; + const auto ret = webdav_unlink(path); + if (ret < 0) { + log_write("[WEBDAV] webdav_unlink() failed: %s errno: %s\n", path, std::strerror(-ret)); + return ret; } return 0; } int Device::devoptab_rename(const char *oldName, const char *newName) { - if (!webdav_rename(oldName, newName, false) && !webdav_rename(oldName, newName, true)) { - return -EIO; + auto ret = webdav_rename(oldName, newName, false); + if (ret == -ENOENT) { + ret = webdav_rename(oldName, newName, true); + } + + if (ret < 0) { + log_write("[WEBDAV] webdav_rename() failed: %s to %s errno: %s\n", oldName, newName, std::strerror(-ret)); + return ret; } return 0; } int Device::devoptab_mkdir(const char *path, int mode) { - if (!webdav_mkdir(path)) { - return -EIO; + const auto ret = webdav_mkdir(path); + if (ret < 0) { + log_write("[WEBDAV] webdav_mkdir() failed: %s errno: %s\n", path, std::strerror(-ret)); + return ret; } return 0; } int Device::devoptab_rmdir(const char *path) { - if (!webdav_rmdir(path)) { - return -EIO; + const auto ret = webdav_rmdir(path); + if (ret < 0) { + log_write("[WEBDAV] webdav_rmdir() failed: %s errno: %s\n", path, std::strerror(-ret)); + return ret; } return 0; @@ -491,9 +556,11 @@ int Device::devoptab_diropen(void* fd, const char *path) { auto dir = static_cast(fd); auto entries = new DirEntries(); - if (!webdav_dirlist(path, *entries)) { + const auto ret = webdav_dirlist(path, *entries); + if (ret < 0) { + log_write("[WEBDAV] webdav_dirlist() failed: %s errno: %s\n", path, std::strerror(-ret)); delete entries; - return -ENOENT; + return ret; } dir->entries = entries; @@ -536,8 +603,14 @@ int Device::devoptab_dirclose(void* fd) { } int Device::devoptab_lstat(const char *path, struct stat *st) { - if (!webdav_stat(path, st, false) && !webdav_stat(path, st, true)) { - return -ENOENT; + auto ret = webdav_stat(path, st, false); + if (ret == -ENOENT) { + ret = webdav_stat(path, st, true); + } + + if (ret < 0) { + log_write("[WEBDAV] webdav_stat() failed: %s errno: %s\n", path, std::strerror(-ret)); + return ret; } return 0; @@ -548,7 +621,7 @@ int Device::devoptab_ftruncate(void *fd, off_t len) { if (!file->write_mode) { log_write("[WEBDAV] Attempt to truncate a read-only file\n"); - return EBADF; + return -EBADF; } file->entry->st.st_size = len;