Added option to resolve external symlinks and fixed symlink handling.

This commit is contained in:
2025-01-01 12:41:44 -06:00
parent 33a1f096ac
commit 0dc5bb53c0
2 changed files with 40 additions and 19 deletions

View File

@@ -23,6 +23,7 @@ struct file_data {
enum FilerStatusCodes : uint8_t { enum FilerStatusCodes : uint8_t {
Success = 0, Success = 0,
IsDirectory = 248,
AccessDenied = 249, AccessDenied = 249,
FileExists = 250, FileExists = 250,
DirectoryNotEmpty = 251, DirectoryNotEmpty = 251,

View File

@@ -4,6 +4,7 @@
#include <fstream> #include <fstream>
#include <string> #include <string>
#include <filesystem> #include <filesystem>
#include <system_error>
#include <sys/stat.h> #include <sys/stat.h>
#include <time.h> #include <time.h>
@@ -13,6 +14,14 @@ class LocalFiler : public IPlugin, public Filer {
public: public:
LocalFiler() {} LocalFiler() {}
bool initialize(const std::map<std::string, std::string>& 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) { file_data setRoot(std::string _root) {
struct file_data fd; struct file_data fd;
try { try {
@@ -74,6 +83,10 @@ public:
} }
bool resolvePath(const std::string& path, fs::path* target) { 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 { try {
if (path.empty() || path == ".") { if (path.empty() || path == ".") {
*target = root / cwd.relative_path(); *target = root / cwd.relative_path();
@@ -88,13 +101,19 @@ public:
*target = root / cwd.relative_path() / path; *target = root / cwd.relative_path() / path;
} }
// Remove .. and . components *target = fs::path(*target).lexically_normal();
*target = fs::weakly_canonical(*target); if (fs::is_symlink(*target) && resolve_symlink) {
fs::path resolved_sym = fs::weakly_canonical(*target);
// Make sure we haven't escaped the root logger->print(LOGLEVEL_DEBUG, "Resolved symlink path: %s", resolved_sym.c_str());
if (!(*target).string().starts_with(root.string())) 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; return false;
logger->print(LOGLEVEL_DEBUG, "Resolved path: %s", target->c_str());
return true; return true;
} catch (const std::exception& e) { } catch (const std::exception& e) {
logger->print(LOGLEVEL_ERROR, "Path resolution error: %s", e.what()); logger->print(LOGLEVEL_ERROR, "Path resolution error: %s", e.what());
@@ -206,24 +225,24 @@ public:
struct file_data fd; struct file_data fd;
try { try {
fs::path resolved; fs::path resolved;
if (!resolvePath(name, &resolved)) { if (!resolvePath(name, &resolved, false)) {
fd.error = file_error{FilerStatusCodes::NoPermission, "Invalid Permissions"}; fd.error = file_error{FilerStatusCodes::NoPermission, "Invalid Permissions"};
return fd; return fd;
} }
fd.path = resolved.string().c_str(); fd.path = resolved.c_str();
if (!fs::exists(resolved)) { std::error_code err;
fd.error = file_error{FilerStatusCodes::NotFound, "File Not Found"}; if (!fs::remove(resolved, err)) {
return fd; 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)
if (fs::is_directory(resolved) && !fs::is_empty(resolved)) { fd.error = file_error{FilerStatusCodes::NoPermission, "Invalid Permissions"};
fd.error = file_error{FilerStatusCodes::DirectoryNotEmpty, "Directory not empty"}; else if (err == std::errc::directory_not_empty)
return fd; 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"};
if (fs::remove_all(resolved) == 0) { else
fd.error = file_error{FilerStatusCodes::NoPermission, "Unable to delete file"}; fd.error = file_error{FilerStatusCodes::NoPermission, "Unable to delete file"};
} }
} catch (const std::filesystem::filesystem_error& ex) { } catch (const std::filesystem::filesystem_error& ex) {
logger->print(LOGLEVEL_ERROR, ex.what()); logger->print(LOGLEVEL_ERROR, ex.what());
@@ -405,6 +424,7 @@ public:
private: private:
fs::path root; fs::path root;
fs::path cwd; fs::path cwd;
bool resolve_ext_symlinks = false;
char type = 'I'; char type = 'I';
}; };