This commit is contained in:
spaceeye 2023-06-11 18:40:04 +03:00
parent 16086a346f
commit 3ad9525a14
4 changed files with 161 additions and 7 deletions

View File

@ -31,11 +31,12 @@ struct FileIO {
Str file; Str file;
Str mode; Str mode;
FILE* fp; FILE* fp= nullptr;
bool is_text() const { return mode != "rb" && mode != "wb" && mode != "ab"; } bool is_text() const { return mode != "rb" && mode != "wb" && mode != "ab"; }
FileIO(VM* vm, std::string file, std::string mode): file(file), mode(mode) { FileIO(VM* vm, std::string file, std::string mode): file(file), mode(mode) {
if (vm->check_is_invalid_io_path(file)) {vm->IOError(strerror(errno));}
fp = fopen(file.c_str(), mode.c_str()); fp = fopen(file.c_str(), mode.c_str());
if(!fp) vm->IOError(strerror(errno)); if(!fp) vm->IOError(strerror(errno));
} }
@ -125,7 +126,7 @@ inline void add_module_os(VM* vm){
di = std::filesystem::directory_iterator(path); di = std::filesystem::directory_iterator(path);
}catch(std::filesystem::filesystem_error& e){ }catch(std::filesystem::filesystem_error& e){
std::string msg = e.what(); std::string msg = e.what();
auto pos = msg.find_last_of(":"); auto pos = msg.find_last_of(':');
if(pos != std::string::npos) msg = msg.substr(pos + 1); if(pos != std::string::npos) msg = msg.substr(pos + 1);
vm->IOError(Str(msg).lstrip()); vm->IOError(Str(msg).lstrip());
} }

142
src/isolated_io.h Normal file
View File

@ -0,0 +1,142 @@
#pragma once
#include "ceval.h"
#include "cffi.h"
#include "common.h"
#if PK_ENABLE_OS
#include <filesystem>
#include <cstdio>
namespace pkpy {
inline std::filesystem::path get_rel(const VM* vm, const std::filesystem::path & rel) {
return rel.lexically_relative(vm->_lowest_isolated_cwd_path);
}
bool check_if_path_is_isolated(const std::string & path_to_check_str,
const std::string & toplevel_path_str) {
//.lexically_normal() expands somepath/somdir/.. to somepath/
auto toplevel_path = std::filesystem::path(toplevel_path_str).lexically_normal();
auto path_to_check = std::filesystem::path(toplevel_path_str+path_to_check_str).lexically_normal();
//toplevel_path is part of toplevel_path, so if toplevel_path is less than path_to_check, then toplevel_path is certainly not a part of it
if (path_to_check < toplevel_path) {return false;}
if (path_to_check == toplevel_path) {return true;}
while (true) {
auto temp = path_to_check.parent_path();
if (path_to_check == temp) { break;}
path_to_check = temp;
if (path_to_check < toplevel_path) {return false;}
if (path_to_check == toplevel_path) {return true;}
}
return false;
}
inline void add_module_isolated_os(VM *vm) {
if (vm->_lowest_isolated_cwd_path.empty()) {
throw std::invalid_argument("vm->_lowest_isolated_cwd_path is empty. Change it to a path of VM's allowed operation.");
}
if (!std::filesystem::exists(vm->_lowest_isolated_cwd_path)) {
throw std::invalid_argument("vm->_lowest_isolated_cwd_path doesn't exist.");
}
PyObject* mod = vm->new_module("os");
PyObject* path_obj = vm->heap.gcnew<DummyInstance>(vm->tp_object, {});
mod->attr().set("path", path_obj);
vm->bind_func<0>(mod, "getcwd", [](VM* vm, ArgsView args){
return VAR(get_rel(vm, std::filesystem::current_path()).string());
});
vm->bind_func<1>(mod, "chdir", [](VM* vm, ArgsView args){
std::filesystem::path path(CAST(Str&, args[0]).sv());
if (!check_if_path_is_isolated(path, vm->_lowest_isolated_cwd_path)) {
std::filesystem::current_path(vm->_lowest_isolated_cwd_path);
return vm->None;
}
std::filesystem::current_path(vm->_lowest_isolated_cwd_path / path);
return vm->None;
});
vm->bind_func<1>(mod, "listdir", [](VM* vm, ArgsView args){
std::filesystem::path path(CAST(Str&, args[0]).sv());
if (!check_if_path_is_isolated(path, vm->_lowest_isolated_cwd_path)) {vm->IOError(Str("Invalid path."));}
path = vm->_lowest_isolated_cwd_path / path;
std::filesystem::directory_iterator di;
try{
di = std::filesystem::directory_iterator(path);
}catch(std::filesystem::filesystem_error& e){
std::string msg = e.what();
auto pos = msg.find_last_of(':');
if(pos != std::string::npos) msg = msg.substr(pos + 1);
vm->IOError(Str(msg).lstrip());
}
List ret;
for(auto& p: di) ret.push_back(VAR(get_rel(vm, p.path()).filename().string()));
return VAR(ret);
});
vm->bind_func<1>(mod, "remove", [](VM* vm, ArgsView args){
std::filesystem::path path(CAST(Str&, args[0]).sv());
if (!check_if_path_is_isolated(path, vm->_lowest_isolated_cwd_path)) {vm->IOError(Str("Operation failed."));}
path = vm->_lowest_isolated_cwd_path / path;
bool ok = std::filesystem::remove(path);
if(!ok) vm->IOError("Operation failed.");
return vm->None;
});
vm->bind_func<1>(mod, "mkdir", [](VM* vm, ArgsView args){
std::filesystem::path path(CAST(Str&, args[0]).sv());
if (!check_if_path_is_isolated(path, vm->_lowest_isolated_cwd_path)) {vm->IOError(Str("Operation failed."));}
path = vm->_lowest_isolated_cwd_path / path;
bool ok = std::filesystem::create_directory(path);
if(!ok) vm->IOError("Operation failed.");
return vm->None;
});
vm->bind_func<1>(mod, "rmdir", [](VM* vm, ArgsView args){
std::filesystem::path path(CAST(Str&, args[0]).sv());
if (!check_if_path_is_isolated(path, vm->_lowest_isolated_cwd_path)) {vm->IOError(Str("Operation failed."));}
path = vm->_lowest_isolated_cwd_path / path;
bool ok = std::filesystem::remove(path);
if(!ok) vm->IOError("Operation failed.");
return vm->None;
});
vm->bind_func<-1>(path_obj, "join", [](VM* vm, ArgsView args){
std::filesystem::path path;
for(int i=0; i<args.size(); i++){
path /= CAST(Str&, args[i]).sv();
}
return VAR(path.string());
});
vm->bind_func<1>(path_obj, "exists", [](VM* vm, ArgsView args){
std::filesystem::path path(CAST(Str&, args[0]).sv());
path = vm->_lowest_isolated_cwd_path / path;
bool exists = std::filesystem::exists(path);
return VAR(exists);
});
vm->bind_func<1>(path_obj, "basename", [](VM* vm, ArgsView args){
std::filesystem::path path(CAST(Str&, args[0]).sv());
return VAR(path.filename().string());
});
}
}// namespace pkpy
#else
namespace pkpy{
inline void add_module_isolated_os(void* vm){}
} // namespace pkpy
#endif

View File

@ -1333,7 +1333,11 @@ inline void VM::post_init(){
if(enable_os){ if(enable_os){
add_module_io(this); add_module_io(this);
add_module_os(this); if (isolated_os) {
add_module_isolated_os(this);
} else {
add_module_os(this);
}
add_module_requests(this); add_module_requests(this);
_import_handler = _default_import_handler; _import_handler = _default_import_handler;
} }

View File

@ -25,6 +25,8 @@ namespace pkpy{
#define POPX() (s_data.popx()) #define POPX() (s_data.popx())
#define STACK_VIEW(n) (s_data.view(n)) #define STACK_VIEW(n) (s_data.view(n))
typedef std::function<Bytes(const Str & name)> ReadFileCwdFunc;
#define DEF_NATIVE_2(ctype, ptype) \ #define DEF_NATIVE_2(ctype, ptype) \
template<> inline ctype py_cast<ctype>(VM* vm, PyObject* obj) { \ template<> inline ctype py_cast<ctype>(VM* vm, PyObject* obj) { \
vm->check_non_tagged_type(obj, vm->ptype); \ vm->check_non_tagged_type(obj, vm->ptype); \
@ -98,7 +100,7 @@ struct FrameId{
Frame* operator->() const { return &data->operator[](index); } Frame* operator->() const { return &data->operator[](index); }
}; };
typedef void(*PrintFunc)(VM*, const Str&); typedef std::function<void(VM*, const Str&)> PrintFunc;
class VM { class VM {
VM* vm; // self reference for simplify code VM* vm; // self reference for simplify code
@ -123,7 +125,7 @@ public:
PrintFunc _stdout; PrintFunc _stdout;
PrintFunc _stderr; PrintFunc _stderr;
Bytes (*_import_handler)(const Str& name); std::function<Bytes(const Str&)> _import_handler;
// for quick access // for quick access
Type tp_object, tp_type, tp_int, tp_float, tp_bool, tp_str; Type tp_object, tp_type, tp_int, tp_float, tp_bool, tp_str;
@ -133,9 +135,14 @@ public:
Type tp_super, tp_exception, tp_bytes, tp_mappingproxy; Type tp_super, tp_exception, tp_bytes, tp_mappingproxy;
Type tp_dict, tp_property, tp_star_wrapper; Type tp_dict, tp_property, tp_star_wrapper;
const bool enable_os; std::function<bool(std::string)> check_is_invalid_io_path = [](const std::string&){return false;};
std::string _lowest_isolated_cwd_path; // should be absolute, only used in isolated_io.h
VM(bool enable_os=true) : heap(this), enable_os(enable_os) { const bool enable_os;
const bool isolated_os;
VM(bool enable_os=true, bool isolated_os=false):
heap(this), enable_os(enable_os), isolated_os(isolated_os) {
this->vm = this; this->vm = this;
_stdout = [](VM* vm, const Str& s) { std::cout << s; }; _stdout = [](VM* vm, const Str& s) { std::cout << s; };
_stderr = [](VM* vm, const Str& s) { std::cerr << s; }; _stderr = [](VM* vm, const Str& s) { std::cerr << s; };