diff --git a/src/filer.h b/src/filer.h index 0e2b3b6..54d0e54 100644 --- a/src/filer.h +++ b/src/filer.h @@ -23,6 +23,7 @@ struct file_data { enum FilerStatusCodes : uint8_t { Success = 0, + IsDirectory = 248, AccessDenied = 249, FileExists = 250, DirectoryNotEmpty = 251, diff --git a/src/plugins/filer_local/filer_local.cpp b/src/plugins/filer_local/filer_local.cpp index 2d17d19..4d661ec 100644 --- a/src/plugins/filer_local/filer_local.cpp +++ b/src/plugins/filer_local/filer_local.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -13,6 +14,14 @@ class LocalFiler : public IPlugin, public Filer { public: LocalFiler() {} + bool initialize(const std::map& config) { + auto symreslv_it = config.find("resolve_external_symlinks"); + this->resolve_ext_symlinks = (symreslv_it != config.end()) ? + (symreslv_it->second == "on" || symreslv_it->second == "true" || symreslv_it->second == "yes") : + false; + return true; + } + file_data setRoot(std::string _root) { struct file_data fd; try { @@ -74,6 +83,10 @@ public: } bool resolvePath(const std::string& path, fs::path* target) { + return this->resolvePath(path, target, true); + } + + bool resolvePath(const std::string& path, fs::path* target, bool resolve_symlink) { try { if (path.empty() || path == ".") { *target = root / cwd.relative_path(); @@ -88,13 +101,19 @@ public: *target = root / cwd.relative_path() / path; } - // Remove .. and . components - *target = fs::weakly_canonical(*target); - - // Make sure we haven't escaped the root - if (!(*target).string().starts_with(root.string())) + *target = fs::path(*target).lexically_normal(); + if (fs::is_symlink(*target) && resolve_symlink) { + fs::path resolved_sym = fs::weakly_canonical(*target); + logger->print(LOGLEVEL_DEBUG, "Resolved symlink path: %s", resolved_sym.c_str()); + if (!resolve_ext_symlinks) { + if (!resolved_sym.string().starts_with(root.string())) + return false; + } else *target = resolved_sym; + } else if (!(*target).string().starts_with(root.string())) return false; + logger->print(LOGLEVEL_DEBUG, "Resolved path: %s", target->c_str()); + return true; } catch (const std::exception& e) { logger->print(LOGLEVEL_ERROR, "Path resolution error: %s", e.what()); @@ -206,24 +225,24 @@ public: struct file_data fd; try { fs::path resolved; - if (!resolvePath(name, &resolved)) { + if (!resolvePath(name, &resolved, false)) { fd.error = file_error{FilerStatusCodes::NoPermission, "Invalid Permissions"}; return fd; } - fd.path = resolved.string().c_str(); + fd.path = resolved.c_str(); - if (!fs::exists(resolved)) { - fd.error = file_error{FilerStatusCodes::NotFound, "File Not Found"}; - return fd; - } - - if (fs::is_directory(resolved) && !fs::is_empty(resolved)) { - fd.error = file_error{FilerStatusCodes::DirectoryNotEmpty, "Directory not empty"}; - return fd; - } - - if (fs::remove_all(resolved) == 0) { - fd.error = file_error{FilerStatusCodes::NoPermission, "Unable to delete file"}; + std::error_code err; + if (!fs::remove(resolved, err)) { + if (err == std::errc::no_such_file_or_directory) + fd.error = file_error{FilerStatusCodes::NotFound, "File Not Found"}; + else if (err == std::errc::permission_denied) + fd.error = file_error{FilerStatusCodes::NoPermission, "Invalid Permissions"}; + else if (err == std::errc::directory_not_empty) + fd.error = file_error{FilerStatusCodes::DirectoryNotEmpty, "Directory not empty"}; + else if (err == std::errc::is_a_directory) + fd.error = file_error{FilerStatusCodes::IsDirectory, "Is A Directory"}; + else + fd.error = file_error{FilerStatusCodes::NoPermission, "Unable to delete file"}; } } catch (const std::filesystem::filesystem_error& ex) { logger->print(LOGLEVEL_ERROR, ex.what()); @@ -405,6 +424,7 @@ public: private: fs::path root; fs::path cwd; + bool resolve_ext_symlinks = false; char type = 'I'; };