-
Notifications
You must be signed in to change notification settings - Fork 30k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
src: avoid copying string in fs_permission
PR-URL: #47746 Reviewed-By: Rafael Gonzaga <[email protected]> Reviewed-By: Daeyeon Jeong <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: Deokjin Kim <[email protected]> Reviewed-By: Darshan Sen <[email protected]>
- Loading branch information
1 parent
7771b61
commit 4fe96b1
Showing
2 changed files
with
332 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
#include "fs_permission.h" | ||
#include "base_object-inl.h" | ||
#include "util.h" | ||
#include "v8.h" | ||
|
||
#include <fcntl.h> | ||
#include <limits.h> | ||
#include <stdlib.h> | ||
#include <algorithm> | ||
#include <filesystem> | ||
#include <string> | ||
#include <vector> | ||
|
||
namespace { | ||
|
||
std::string WildcardIfDir(const std::string& res) noexcept { | ||
uv_fs_t req; | ||
int rc = uv_fs_stat(nullptr, &req, res.c_str(), nullptr); | ||
if (rc == 0) { | ||
const uv_stat_t* const s = static_cast<const uv_stat_t*>(req.ptr); | ||
if (s->st_mode & S_IFDIR) { | ||
// add wildcard when directory | ||
if (res.back() == node::kPathSeparator) { | ||
return res + "*"; | ||
} | ||
return res + node::kPathSeparator + "*"; | ||
} | ||
} | ||
uv_fs_req_cleanup(&req); | ||
return res; | ||
} | ||
|
||
void FreeRecursivelyNode( | ||
node::permission::FSPermission::RadixTree::Node* node) { | ||
if (node == nullptr) { | ||
return; | ||
} | ||
|
||
if (node->children.size()) { | ||
for (auto& c : node->children) { | ||
FreeRecursivelyNode(c.second); | ||
} | ||
} | ||
|
||
if (node->wildcard_child != nullptr) { | ||
delete node->wildcard_child; | ||
} | ||
delete node; | ||
} | ||
|
||
bool is_tree_granted(node::permission::FSPermission::RadixTree* granted_tree, | ||
const std::string_view& param) { | ||
#ifdef _WIN32 | ||
// is UNC file path | ||
if (param.rfind("\\\\", 0) == 0) { | ||
// return lookup with normalized param | ||
int starting_pos = 4; // "\\?\" | ||
if (param.rfind("\\\\?\\UNC\\") == 0) { | ||
starting_pos += 4; // "UNC\" | ||
} | ||
auto normalized = param.substr(starting_pos); | ||
return granted_tree->Lookup(normalized, true); | ||
} | ||
#endif | ||
return granted_tree->Lookup(param, true); | ||
} | ||
|
||
} // namespace | ||
|
||
namespace node { | ||
|
||
namespace permission { | ||
|
||
// allow = '*' | ||
// allow = '/tmp/,/home/example.js' | ||
void FSPermission::Apply(const std::string& allow, PermissionScope scope) { | ||
for (const auto& res : SplitString(allow, ',')) { | ||
if (res == "*") { | ||
if (scope == PermissionScope::kFileSystemRead) { | ||
deny_all_in_ = false; | ||
allow_all_in_ = true; | ||
} else { | ||
deny_all_out_ = false; | ||
allow_all_out_ = true; | ||
} | ||
return; | ||
} | ||
GrantAccess(scope, res); | ||
} | ||
} | ||
|
||
void FSPermission::GrantAccess(PermissionScope perm, const std::string& res) { | ||
const std::string path = WildcardIfDir(res); | ||
if (perm == PermissionScope::kFileSystemRead) { | ||
granted_in_fs_.Insert(path); | ||
deny_all_in_ = false; | ||
} else if (perm == PermissionScope::kFileSystemWrite) { | ||
granted_out_fs_.Insert(path); | ||
deny_all_out_ = false; | ||
} | ||
} | ||
|
||
bool FSPermission::is_granted(PermissionScope perm, | ||
const std::string_view& param = "") { | ||
switch (perm) { | ||
case PermissionScope::kFileSystem: | ||
return allow_all_in_ && allow_all_out_; | ||
case PermissionScope::kFileSystemRead: | ||
return !deny_all_in_ && | ||
((param.empty() && allow_all_in_) || allow_all_in_ || | ||
is_tree_granted(&granted_in_fs_, param)); | ||
case PermissionScope::kFileSystemWrite: | ||
return !deny_all_out_ && | ||
((param.empty() && allow_all_out_) || allow_all_out_ || | ||
is_tree_granted(&granted_out_fs_, param)); | ||
default: | ||
return false; | ||
} | ||
} | ||
|
||
FSPermission::RadixTree::RadixTree() : root_node_(new Node("")) {} | ||
|
||
FSPermission::RadixTree::~RadixTree() { | ||
FreeRecursivelyNode(root_node_); | ||
} | ||
|
||
bool FSPermission::RadixTree::Lookup(const std::string_view& s, | ||
bool when_empty_return = false) { | ||
FSPermission::RadixTree::Node* current_node = root_node_; | ||
if (current_node->children.size() == 0) { | ||
return when_empty_return; | ||
} | ||
|
||
unsigned int parent_node_prefix_len = current_node->prefix.length(); | ||
const std::string path(s); | ||
auto path_len = path.length(); | ||
|
||
while (true) { | ||
if (parent_node_prefix_len == path_len && current_node->IsEndNode()) { | ||
return true; | ||
} | ||
|
||
auto node = current_node->NextNode(path, parent_node_prefix_len); | ||
if (node == nullptr) { | ||
return false; | ||
} | ||
|
||
current_node = node; | ||
parent_node_prefix_len += current_node->prefix.length(); | ||
if (current_node->wildcard_child != nullptr && | ||
path_len >= (parent_node_prefix_len - 2 /* slash* */)) { | ||
return true; | ||
} | ||
} | ||
} | ||
|
||
void FSPermission::RadixTree::Insert(const std::string& path) { | ||
FSPermission::RadixTree::Node* current_node = root_node_; | ||
|
||
unsigned int parent_node_prefix_len = current_node->prefix.length(); | ||
int path_len = path.length(); | ||
|
||
for (int i = 1; i <= path_len; ++i) { | ||
bool is_wildcard_node = path[i - 1] == '*'; | ||
bool is_last_char = i == path_len; | ||
|
||
if (is_wildcard_node || is_last_char) { | ||
std::string node_path = path.substr(parent_node_prefix_len, i); | ||
current_node = current_node->CreateChild(node_path); | ||
} | ||
|
||
if (is_wildcard_node) { | ||
current_node = current_node->CreateWildcardChild(); | ||
parent_node_prefix_len = i; | ||
} | ||
} | ||
} | ||
|
||
} // namespace permission | ||
} // namespace node |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
#ifndef SRC_PERMISSION_FS_PERMISSION_H_ | ||
#define SRC_PERMISSION_FS_PERMISSION_H_ | ||
|
||
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS | ||
|
||
#include "v8.h" | ||
|
||
#include <unordered_map> | ||
#include <vector> | ||
#include "permission/permission_base.h" | ||
#include "util.h" | ||
|
||
namespace node { | ||
|
||
namespace permission { | ||
|
||
class FSPermission final : public PermissionBase { | ||
public: | ||
void Apply(const std::string& deny, PermissionScope scope) override; | ||
bool is_granted(PermissionScope perm, const std::string_view& param) override; | ||
|
||
// For debugging purposes, use the gist function to print the whole tree | ||
// https://gist.github.com/RafaelGSS/5b4f09c559a54f53f9b7c8c030744d19 | ||
struct RadixTree { | ||
struct Node { | ||
std::string prefix; | ||
std::unordered_map<char, Node*> children; | ||
Node* wildcard_child; | ||
|
||
explicit Node(const std::string& pre) | ||
: prefix(pre), wildcard_child(nullptr) {} | ||
|
||
Node() : wildcard_child(nullptr) {} | ||
|
||
Node* CreateChild(std::string prefix) { | ||
char label = prefix[0]; | ||
|
||
Node* child = children[label]; | ||
if (child == nullptr) { | ||
children[label] = new Node(prefix); | ||
return children[label]; | ||
} | ||
|
||
// swap prefix | ||
unsigned int i = 0; | ||
unsigned int prefix_len = prefix.length(); | ||
for (; i < child->prefix.length(); ++i) { | ||
if (i > prefix_len || prefix[i] != child->prefix[i]) { | ||
std::string parent_prefix = child->prefix.substr(0, i); | ||
std::string child_prefix = child->prefix.substr(i); | ||
|
||
child->prefix = child_prefix; | ||
Node* split_child = new Node(parent_prefix); | ||
split_child->children[child_prefix[0]] = child; | ||
children[parent_prefix[0]] = split_child; | ||
|
||
return split_child->CreateChild(prefix.substr(i)); | ||
} | ||
} | ||
return child->CreateChild(prefix.substr(i)); | ||
} | ||
|
||
Node* CreateWildcardChild() { | ||
if (wildcard_child != nullptr) { | ||
return wildcard_child; | ||
} | ||
wildcard_child = new Node(); | ||
return wildcard_child; | ||
} | ||
|
||
Node* NextNode(const std::string& path, unsigned int idx) { | ||
if (idx >= path.length()) { | ||
return nullptr; | ||
} | ||
|
||
auto it = children.find(path[idx]); | ||
if (it == children.end()) { | ||
return nullptr; | ||
} | ||
auto child = it->second; | ||
// match prefix | ||
unsigned int prefix_len = child->prefix.length(); | ||
for (unsigned int i = 0; i < path.length(); ++i) { | ||
if (i >= prefix_len || child->prefix[i] == '*') { | ||
return child; | ||
} | ||
|
||
// Handle optional trailing | ||
// path = /home/subdirectory | ||
// child = subdirectory/* | ||
if (idx >= path.length() && | ||
child->prefix[i] == node::kPathSeparator) { | ||
continue; | ||
} | ||
|
||
if (path[idx++] != child->prefix[i]) { | ||
return nullptr; | ||
} | ||
} | ||
return child; | ||
} | ||
|
||
// A node can be a *end* node and have children | ||
// E.g: */slower*, */slown* are inserted: | ||
// /slow | ||
// ---> er | ||
// ---> n | ||
// If */slow* is inserted right after, it will create an | ||
// empty node | ||
// /slow | ||
// ---> '\000' ASCII (0) || \0 | ||
// ---> er | ||
// ---> n | ||
bool IsEndNode() { | ||
if (children.size() == 0) { | ||
return true; | ||
} | ||
return children['\0'] != nullptr; | ||
} | ||
}; | ||
|
||
RadixTree(); | ||
~RadixTree(); | ||
void Insert(const std::string& s); | ||
bool Lookup(const std::string_view& s) { return Lookup(s, false); } | ||
bool Lookup(const std::string_view& s, bool when_empty_return); | ||
|
||
private: | ||
Node* root_node_; | ||
}; | ||
|
||
private: | ||
void GrantAccess(PermissionScope scope, const std::string& param); | ||
void RestrictAccess(PermissionScope scope, | ||
const std::vector<std::string>& params); | ||
// fs granted on startup | ||
RadixTree granted_in_fs_; | ||
RadixTree granted_out_fs_; | ||
|
||
bool deny_all_in_ = true; | ||
bool deny_all_out_ = true; | ||
|
||
bool allow_all_in_ = false; | ||
bool allow_all_out_ = false; | ||
}; | ||
|
||
} // namespace permission | ||
|
||
} // namespace node | ||
|
||
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS | ||
#endif // SRC_PERMISSION_FS_PERMISSION_H_ |