fs: fix CreateDirectoryRecursivelyWithPath() for root files. save: fix restore detection. devoptab: return proper errno codes.
This commit is contained in:
@@ -235,7 +235,7 @@ Result CreateDirectoryRecursivelyWithPath(FsFileSystem* fs, const FsPath& _path,
|
|||||||
|
|
||||||
// strip file name form path.
|
// strip file name form path.
|
||||||
const auto last_slash = std::strrchr(_path, '/');
|
const auto last_slash = std::strrchr(_path, '/');
|
||||||
if (!last_slash) {
|
if (!last_slash || last_slash == _path.s) {
|
||||||
R_SUCCEED();
|
R_SUCCEED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
uLong minizip_read_file_func_stdio(voidpf opaque, voidpf stream, void* buf, uLong size) {
|
||||||
auto file = static_cast<std::FILE*>(stream);
|
auto file = static_cast<std::FILE*>(stream);
|
||||||
log_write("[ZIP] doing read\n");
|
|
||||||
return std::fread(buf, 1, size, file);
|
return std::fread(buf, 1, size, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -777,9 +777,14 @@ Result TransferUnzipAll(ui::ProgressBox* pbox, void* zfile, fs::Fs* fs, const fs
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const auto path_len = std::strlen(path);
|
||||||
|
if (!path_len) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
pbox->NewTransfer(name);
|
pbox->NewTransfer(name);
|
||||||
|
|
||||||
if (path[std::strlen(path) -1] == '/') {
|
if (path[path_len -1] == '/') {
|
||||||
Result rc;
|
Result rc;
|
||||||
if (R_FAILED(rc = fs->CreateDirectoryRecursively(path)) && rc != FsError_PathAlreadyExists) {
|
if (R_FAILED(rc = fs->CreateDirectoryRecursively(path)) && rc != FsError_PathAlreadyExists) {
|
||||||
log_write("failed to create folder: %s 0x%04X\n", path.s, rc);
|
log_write("failed to create folder: %s 0x%04X\n", path.s, rc);
|
||||||
|
|||||||
@@ -730,17 +730,23 @@ void Menu::BackupSaves(std::vector<std::reference_wrapper<Entry>>& entries) {
|
|||||||
|
|
||||||
void Menu::RestoreSave() {
|
void Menu::RestoreSave() {
|
||||||
dump::DumpGetLocation("Select restore location"_i18n, dump::DumpLocationFlag_SdCard|dump::DumpLocationFlag_Stdio, [this](const dump::DumpLocation& location){
|
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) {
|
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);
|
fs = std::make_unique<fs::FsStdio>(true, location.stdio[location.entry.index].mount);
|
||||||
} else if (location.entry.type == dump::DumpLocationType_SdCard) {
|
} else if (location.entry.type == dump::DumpLocationType_SdCard) {
|
||||||
fs = std::make_unique<fs::FsNativeSd>();
|
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
|
// get saves in /Saves/Name and /Saves/app_id
|
||||||
filebrowser::FsDirCollection collections[2]{};
|
filebrowser::FsDirCollection collections[2]{};
|
||||||
for (auto i = 0; i < std::size(collections); i++) {
|
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);
|
filebrowser::FsView::get_collection(fs.get(), save_path, "", collections[i], true, false, false);
|
||||||
// reverse as they will be sorted in oldest -> newest.
|
// reverse as they will be sorted in oldest -> newest.
|
||||||
// todo: better impl when both id and normal app folders are used.
|
// todo: better impl when both id and normal app folders are used.
|
||||||
@@ -763,7 +769,7 @@ void Menu::RestoreSave() {
|
|||||||
|
|
||||||
if (paths.empty()) {
|
if (paths.empty()) {
|
||||||
App::Push<ui::OptionBox>(
|
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
|
"OK"_i18n
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -58,13 +58,13 @@ private:
|
|||||||
static bool ftp_parse_mlist(std::string_view chunk, struct stat* st);
|
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);
|
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);
|
int ftp_dirlist(const std::string& path, DirEntries& out);
|
||||||
bool ftp_stat(const std::string& path, struct stat* st, bool is_dir);
|
int ftp_stat(const std::string& path, struct stat* st, bool is_dir);
|
||||||
bool ftp_remove_file_folder(const std::string& path, bool is_dir);
|
int ftp_remove_file_folder(const std::string& path, bool is_dir);
|
||||||
bool ftp_unlink(const std::string& path);
|
int ftp_unlink(const std::string& path);
|
||||||
bool ftp_rename(const std::string& old_path, const std::string& new_path, bool is_dir);
|
int ftp_rename(const std::string& old_path, const std::string& new_path, bool is_dir);
|
||||||
bool ftp_mkdir(const std::string& path);
|
int ftp_mkdir(const std::string& path);
|
||||||
bool ftp_rmdir(const std::string& path);
|
int ftp_rmdir(const std::string& path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool mounted{};
|
bool mounted{};
|
||||||
@@ -267,7 +267,7 @@ std::pair<bool, long> Device::ftp_quote(std::span<const std::string> commands, b
|
|||||||
return {true, response_code};
|
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);
|
const auto url = build_url(path, true);
|
||||||
std::vector<char> chunk;
|
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);
|
const auto res = curl_easy_perform(this->curl);
|
||||||
if (res != CURLE_OK) {
|
if (res != CURLE_OK) {
|
||||||
log_write("[FTP] curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
|
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);
|
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::memset(st, 0, sizeof(*st));
|
||||||
|
|
||||||
std::vector<char> chunk;
|
std::vector<char> chunk;
|
||||||
const auto [success, response_code] = ftp_quote({"MLST " + path}, is_dir, &chunk);
|
const auto [success, response_code] = ftp_quote({"MLST " + path}, is_dir, &chunk);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
return false;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!success || response_code >= 400) {
|
switch (response_code) {
|
||||||
log_write("[FTP] MLST command failed with response code: %ld\n", response_code);
|
case 250: // Requested file action okay, completed.
|
||||||
return false;
|
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)) {
|
if (!ftp_parse_mlist({chunk.data(), chunk.size()}, st)) {
|
||||||
log_write("[FTP] Failed to parse MLST response for path: %s\n", path.c_str());
|
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 cmd = (is_dir ? "RMD " : "DELE ") + path;
|
||||||
const auto [success, response_code] = ftp_quote({cmd}, is_dir);
|
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);
|
if (!success) {
|
||||||
return false;
|
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);
|
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);
|
const auto url = build_url("/", is_dir);
|
||||||
|
|
||||||
std::vector<std::string> commands;
|
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);
|
commands.emplace_back("RNTO " + new_path);
|
||||||
|
|
||||||
const auto [success, response_code] = ftp_quote(commands, is_dir);
|
const auto [success, response_code] = ftp_quote(commands, is_dir);
|
||||||
if (!success || response_code >= 400) {
|
if (!success) {
|
||||||
log_write("[FTP] MLST command failed with response code: %ld\n", response_code);
|
return -EIO;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
std::vector<char> chunk;
|
||||||
const auto [success, response_code] = ftp_quote({"MKD " + path}, true);
|
const auto [success, response_code] = ftp_quote({"MKD " + path}, true);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
return false;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: handle result if directory already exists.
|
switch (response_code) {
|
||||||
if (response_code >= 400) {
|
case 257: // "PATHNAME" created.
|
||||||
log_write("[FTP] MLST command failed with response code: %ld\n", response_code);
|
case 250: // Requested file action okay, completed.
|
||||||
return false;
|
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);
|
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)) {
|
if ((flags & O_ACCMODE) == O_RDONLY || (flags & O_APPEND)) {
|
||||||
// ensure the file exists and get its size.
|
// ensure the file exists and get its size.
|
||||||
if (!ftp_stat(path, &st, false)) {
|
const auto ret = ftp_stat(path, &st, false);
|
||||||
log_write("[FTP] File not found: %s\n", path);
|
if (ret < 0) {
|
||||||
return -ENOENT;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (st.st_mode & S_IFDIR) {
|
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) {
|
int Device::devoptab_unlink(const char *path) {
|
||||||
if (!ftp_unlink(path)) {
|
const auto ret = ftp_unlink(path);
|
||||||
return -EIO;
|
if (ret < 0) {
|
||||||
|
log_write("[FTP] ftp_unlink() failed: %s errno: %s\n", path, std::strerror(-ret));
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Device::devoptab_rename(const char *oldName, const char *newName) {
|
int Device::devoptab_rename(const char *oldName, const char *newName) {
|
||||||
if (!ftp_rename(oldName, newName, false) && !ftp_rename(oldName, newName, true)) {
|
auto ret = ftp_rename(oldName, newName, false);
|
||||||
return -EIO;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Device::devoptab_mkdir(const char *path, int mode) {
|
int Device::devoptab_mkdir(const char *path, int mode) {
|
||||||
if (!ftp_mkdir(path)) {
|
const auto ret = ftp_mkdir(path);
|
||||||
return -EIO;
|
if (ret < 0) {
|
||||||
|
log_write("[FTP] ftp_mkdir() failed: %s errno: %s\n", path, std::strerror(-ret));
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Device::devoptab_rmdir(const char *path) {
|
int Device::devoptab_rmdir(const char *path) {
|
||||||
if (!ftp_rmdir(path)) {
|
const auto ret = ftp_rmdir(path);
|
||||||
return -EIO;
|
if (ret < 0) {
|
||||||
|
log_write("[FTP] ftp_rmdir() failed: %s errno: %s\n", path, std::strerror(-ret));
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -557,9 +619,11 @@ int Device::devoptab_diropen(void* fd, const char *path) {
|
|||||||
auto dir = static_cast<Dir*>(fd);
|
auto dir = static_cast<Dir*>(fd);
|
||||||
|
|
||||||
auto entries = new DirEntries();
|
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;
|
delete entries;
|
||||||
return -ENOENT;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
dir->entries = entries;
|
dir->entries = entries;
|
||||||
@@ -602,8 +666,14 @@ int Device::devoptab_dirclose(void* fd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int Device::devoptab_lstat(const char *path, struct stat *st) {
|
int Device::devoptab_lstat(const char *path, struct stat *st) {
|
||||||
if (!ftp_stat(path, st, false) && !ftp_stat(path, st, true)) {
|
auto ret = ftp_stat(path, st, false);
|
||||||
return -ENOENT;
|
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;
|
return 0;
|
||||||
@@ -614,7 +684,7 @@ int Device::devoptab_ftruncate(void *fd, off_t len) {
|
|||||||
|
|
||||||
if (!file->write_mode) {
|
if (!file->write_mode) {
|
||||||
log_write("[FTP] Attempt to truncate a read-only file\n");
|
log_write("[FTP] Attempt to truncate a read-only file\n");
|
||||||
return EBADF;
|
return -EBADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
file->entry->st.st_size = len;
|
file->entry->st.st_size = len;
|
||||||
|
|||||||
@@ -59,14 +59,14 @@ private:
|
|||||||
int devoptab_dirclose(void* fd) override;
|
int devoptab_dirclose(void* fd) override;
|
||||||
int devoptab_lstat(const char *path, struct stat *st) override;
|
int devoptab_lstat(const char *path, struct stat *st) override;
|
||||||
|
|
||||||
bool http_dirlist(const std::string& path, DirEntries& out);
|
int http_dirlist(const std::string& path, DirEntries& out);
|
||||||
bool http_stat(const std::string& path, struct stat* st, bool is_dir);
|
int http_stat(const std::string& path, struct stat* st, bool is_dir);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool mounted{};
|
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);
|
const auto url = build_url(path, true);
|
||||||
std::vector<char> chunk;
|
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);
|
const auto res = curl_easy_perform(this->curl);
|
||||||
if (res != CURLE_OK) {
|
if (res != CURLE_OK) {
|
||||||
log_write("[HTTP] curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
|
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());
|
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());
|
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));
|
std::memset(st, 0, sizeof(*st));
|
||||||
const auto url = build_url(path, is_dir);
|
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);
|
const auto res = curl_easy_perform(this->curl);
|
||||||
if (res != CURLE_OK) {
|
if (res != CURLE_OK) {
|
||||||
log_write("[HTTP] curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
|
log_write("[HTTP] curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
|
||||||
return false;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
long response_code = 0;
|
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{};
|
const char* effective_url{};
|
||||||
curl_easy_getinfo(this->curl, CURLINFO_EFFECTIVE_URL, &effective_url);
|
curl_easy_getinfo(this->curl, CURLINFO_EFFECTIVE_URL, &effective_url);
|
||||||
|
|
||||||
// handle error codes.
|
switch (response_code) {
|
||||||
if (response_code != 200 && response_code != 206) {
|
case 200: // OK
|
||||||
log_write("[HTTP] Unexpected HTTP response code: %ld\n", response_code);
|
case 206: // Partial Content
|
||||||
return false;
|
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) {
|
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_ctime = st->st_mtime;
|
||||||
st->st_nlink = 1;
|
st->st_nlink = 1;
|
||||||
|
|
||||||
return true;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Device::Mount() {
|
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);
|
auto file = static_cast<File*>(fileStruct);
|
||||||
|
|
||||||
struct stat st;
|
struct stat st;
|
||||||
if (!http_stat(path, &st, false)) {
|
const auto ret = http_stat(path, &st, false);
|
||||||
log_write("[HTTP] http_stat() failed for file: %s\n", path);
|
if (ret < 0) {
|
||||||
return -ENOENT;
|
log_write("[HTTP] http_stat() failed for file: %s errno: %s\n", path, std::strerror(-ret));
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (st.st_mode & S_IFDIR) {
|
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);
|
log_write("[HTTP] Opening directory: %s\n", path);
|
||||||
auto entries = new DirEntries();
|
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;
|
delete entries;
|
||||||
return -ENOENT;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_write("[HTTP] Opened directory: %s with %zu entries\n", path, entries->size());
|
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) {
|
int Device::devoptab_lstat(const char *path, struct stat *st) {
|
||||||
if (!http_stat(path, st, false) && !http_stat(path, st, true)) {
|
auto ret = http_stat(path, st, false);
|
||||||
return -ENOENT;
|
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;
|
return 0;
|
||||||
|
|||||||
@@ -73,13 +73,13 @@ private:
|
|||||||
int devoptab_fsync(void *fd) override;
|
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);
|
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);
|
int webdav_dirlist(const std::string& path, DirEntries& out);
|
||||||
bool webdav_stat(const std::string& path, struct stat* st, bool is_dir);
|
int webdav_stat(const std::string& path, struct stat* st, bool is_dir);
|
||||||
bool webdav_remove_file_folder(const std::string& path, bool is_dir);
|
int webdav_remove_file_folder(const std::string& path, bool is_dir);
|
||||||
bool webdav_unlink(const std::string& path);
|
int webdav_unlink(const std::string& path);
|
||||||
bool webdav_rename(const std::string& old_path, const std::string& new_path, bool is_dir);
|
int webdav_rename(const std::string& old_path, const std::string& new_path, bool is_dir);
|
||||||
bool webdav_mkdir(const std::string& path);
|
int webdav_mkdir(const std::string& path);
|
||||||
bool webdav_rmdir(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) {
|
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};
|
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 =
|
const std::string_view post_fields =
|
||||||
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
|
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
|
||||||
"<d:propfind xmlns:d=\"DAV:\">"
|
"<d:propfind xmlns:d=\"DAV:\">"
|
||||||
@@ -143,9 +143,20 @@ bool Device::webdav_dirlist(const std::string& path, DirEntries& out) {
|
|||||||
|
|
||||||
std::vector<char> chunk;
|
std::vector<char> chunk;
|
||||||
const auto [success, response_code] = webdav_custom_command(path, "PROPFIND", post_fields, custom_headers, true, &chunk);
|
const auto [success, response_code] = webdav_custom_command(path, "PROPFIND", post_fields, custom_headers, true, &chunk);
|
||||||
if (!success || response_code != 207) {
|
if (!success) {
|
||||||
log_write("[WEBDAV] PROPFIND failed or returned HTTP error code: %ld\n", response_code);
|
return -EIO;
|
||||||
return false;
|
}
|
||||||
|
|
||||||
|
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");
|
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());
|
const auto result = doc.load_buffer_inplace(chunk.data(), chunk.size());
|
||||||
if (!result) {
|
if (!result) {
|
||||||
log_write("[WEBDAV] Failed to parse XML: %s\n", result.description());
|
log_write("[WEBDAV] Failed to parse XML: %s\n", result.description());
|
||||||
return false;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_write("\n[WEBDAV] XML parsed successfully\n");
|
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());
|
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...
|
// 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));
|
std::memset(st, 0, sizeof(*st));
|
||||||
const auto url = build_url(path, is_dir);
|
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);
|
const auto res = curl_easy_perform(this->curl);
|
||||||
if (res != CURLE_OK) {
|
if (res != CURLE_OK) {
|
||||||
log_write("[WEBDAV] curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
|
log_write("[WEBDAV] curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
|
||||||
return false;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
long response_code = 0;
|
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{};
|
const char* effective_url{};
|
||||||
curl_easy_getinfo(this->curl, CURLINFO_EFFECTIVE_URL, &effective_url);
|
curl_easy_getinfo(this->curl, CURLINFO_EFFECTIVE_URL, &effective_url);
|
||||||
|
|
||||||
// handle error codes.
|
switch (response_code) {
|
||||||
if (response_code != 200 && response_code != 206) {
|
case 200: // OK
|
||||||
log_write("[WEBDAV] Unexpected HTTP response code: %ld\n", response_code);
|
case 206: // Partial Content
|
||||||
return false;
|
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) {
|
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_ctime = st->st_mtime;
|
||||||
st->st_nlink = 1;
|
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);
|
const auto [success, response_code] = webdav_custom_command(path, "DELETE", "", {}, is_dir);
|
||||||
if (!success || (response_code != 200 && response_code != 204)) {
|
if (!success) {
|
||||||
log_write("[WEBDAV] DELETE command failed with response code: %ld\n", response_code);
|
return -EIO;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
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());
|
log_write("[WEBDAV] Renaming %s to %s\n", old_path.c_str(), new_path.c_str());
|
||||||
|
|
||||||
const std::string custom_headers[] = {
|
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);
|
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);
|
if (!success) {
|
||||||
return false;
|
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);
|
const auto [success, response_code] = webdav_custom_command(path, "MKCOL", "", {}, true);
|
||||||
if (!success || response_code != 201) {
|
if (!success) {
|
||||||
log_write("[WEBDAV] MKCOL command failed with response code: %ld\n", response_code);
|
return -EIO;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
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) {
|
if ((flags & O_ACCMODE) == O_RDONLY) {
|
||||||
// ensure the file exists and get its size.
|
// ensure the file exists and get its size.
|
||||||
if (!webdav_stat(path, &st, false)) {
|
const auto ret = webdav_stat(path, &st, false);
|
||||||
log_write("[WEBDAV] File not found: %s\n", path);
|
if (ret < 0) {
|
||||||
return -ENOENT;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (st.st_mode & S_IFDIR) {
|
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) {
|
int Device::devoptab_unlink(const char *path) {
|
||||||
if (!webdav_unlink(path)) {
|
const auto ret = webdav_unlink(path);
|
||||||
return -EIO;
|
if (ret < 0) {
|
||||||
|
log_write("[WEBDAV] webdav_unlink() failed: %s errno: %s\n", path, std::strerror(-ret));
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Device::devoptab_rename(const char *oldName, const char *newName) {
|
int Device::devoptab_rename(const char *oldName, const char *newName) {
|
||||||
if (!webdav_rename(oldName, newName, false) && !webdav_rename(oldName, newName, true)) {
|
auto ret = webdav_rename(oldName, newName, false);
|
||||||
return -EIO;
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Device::devoptab_mkdir(const char *path, int mode) {
|
int Device::devoptab_mkdir(const char *path, int mode) {
|
||||||
if (!webdav_mkdir(path)) {
|
const auto ret = webdav_mkdir(path);
|
||||||
return -EIO;
|
if (ret < 0) {
|
||||||
|
log_write("[WEBDAV] webdav_mkdir() failed: %s errno: %s\n", path, std::strerror(-ret));
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Device::devoptab_rmdir(const char *path) {
|
int Device::devoptab_rmdir(const char *path) {
|
||||||
if (!webdav_rmdir(path)) {
|
const auto ret = webdav_rmdir(path);
|
||||||
return -EIO;
|
if (ret < 0) {
|
||||||
|
log_write("[WEBDAV] webdav_rmdir() failed: %s errno: %s\n", path, std::strerror(-ret));
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -491,9 +556,11 @@ int Device::devoptab_diropen(void* fd, const char *path) {
|
|||||||
auto dir = static_cast<Dir*>(fd);
|
auto dir = static_cast<Dir*>(fd);
|
||||||
|
|
||||||
auto entries = new DirEntries();
|
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;
|
delete entries;
|
||||||
return -ENOENT;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
dir->entries = entries;
|
dir->entries = entries;
|
||||||
@@ -536,8 +603,14 @@ int Device::devoptab_dirclose(void* fd) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int Device::devoptab_lstat(const char *path, struct stat *st) {
|
int Device::devoptab_lstat(const char *path, struct stat *st) {
|
||||||
if (!webdav_stat(path, st, false) && !webdav_stat(path, st, true)) {
|
auto ret = webdav_stat(path, st, false);
|
||||||
return -ENOENT;
|
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;
|
return 0;
|
||||||
@@ -548,7 +621,7 @@ int Device::devoptab_ftruncate(void *fd, off_t len) {
|
|||||||
|
|
||||||
if (!file->write_mode) {
|
if (!file->write_mode) {
|
||||||
log_write("[WEBDAV] Attempt to truncate a read-only file\n");
|
log_write("[WEBDAV] Attempt to truncate a read-only file\n");
|
||||||
return EBADF;
|
return -EBADF;
|
||||||
}
|
}
|
||||||
|
|
||||||
file->entry->st.st_size = len;
|
file->entry->st.st_size = len;
|
||||||
|
|||||||
Reference in New Issue
Block a user