fs: fix CreateDirectoryRecursivelyWithPath() for root files. save: fix restore detection. devoptab: return proper errno codes.

This commit is contained in:
ITotalJustice
2025-09-07 14:40:45 +01:00
parent 6e1eabbe0f
commit 43969a773e
7 changed files with 327 additions and 130 deletions

View File

@@ -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();
}

View File

@@ -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<std::FILE*>(stream);
log_write("[ZIP] doing read\n");
return std::fread(buf, 1, size, file);
}

View File

@@ -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);

View File

@@ -730,17 +730,23 @@ void Menu::BackupSaves(std::vector<std::reference_wrapper<Entry>>& 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::Fs> fs;
std::unique_ptr<fs::Fs> 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<fs::FsStdio>(true, location.stdio[location.entry.index].mount);
} else if (location.entry.type == dump::DumpLocationType_SdCard) {
fs = std::make_unique<fs::FsNativeSd>();
} 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<ui::OptionBox>(
"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;

View File

@@ -58,13 +58,13 @@ private:
static bool ftp_parse_mlist(std::string_view chunk, struct stat* st);
std::pair<bool, long> ftp_quote(std::span<const std::string> commands, bool is_dir, std::vector<char>* 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<bool, long> Device::ftp_quote(std::span<const std::string> 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<char> 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<char> 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<std::string> 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<char> 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<Dir*>(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;

View File

@@ -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<char> 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<File*>(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;

View File

@@ -73,13 +73,13 @@ private:
int devoptab_fsync(void *fd) override;
std::pair<bool, long> webdav_custom_command(const std::string& path, const std::string& cmd, std::string_view postfields, std::span<const std::string> headers, bool is_dir, std::vector<char>* 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<bool, long> 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 =
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
"<d:propfind xmlns:d=\"DAV:\">"
@@ -143,9 +143,20 @@ bool Device::webdav_dirlist(const std::string& path, DirEntries& out) {
std::vector<char> 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<Dir*>(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;