[Add] Implment scp for remote file copy.
This commit is contained in:
parent
4f5436f376
commit
3fbacd4a99
8
assets/scripts/bin/scp.lua
Normal file
8
assets/scripts/bin/scp.lua
Normal file
@ -0,0 +1,8 @@
|
||||
local source = arg[1]
|
||||
local destination = arg[2]
|
||||
|
||||
if not source or not destination then
|
||||
return "usage: scp source destination"
|
||||
end
|
||||
|
||||
return bettola.scp(context, source, destination)
|
||||
@ -117,30 +117,6 @@ std::string CommandProcessor::write_file(const std::string& path, const std::str
|
||||
return api::write_file(*this, path, content);
|
||||
}
|
||||
|
||||
/* Find a VFS node by it's absolute path. */
|
||||
vfs_node* find_node_by_path(vfs_node* root, const std::string& path) {
|
||||
if(path == "/") {
|
||||
return root;
|
||||
}
|
||||
vfs_node* current = root;
|
||||
std::stringstream ss(path);
|
||||
std::string segment;
|
||||
|
||||
/* Discard the first empty segment that comes form the leading '/'. */
|
||||
if(path[0] == '/') {
|
||||
std::getline(ss, segment, '/');
|
||||
}
|
||||
|
||||
while(std::getline(ss, segment, '/')) {
|
||||
if(current->type == DIR_NODE && current->children.count(segment)) {
|
||||
current = current->children[segment];
|
||||
} else {
|
||||
return nullptr; /* Path segmenet not found. */
|
||||
}
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
std::string CommandProcessor::read_file(const std::string& path) {
|
||||
vfs_node* root = get_session_machine()->vfs_root;
|
||||
vfs_node* node = find_node_by_path(root, path);
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
#include <sol/call.hpp>
|
||||
#include <sol/types.hpp>
|
||||
#include <sstream>
|
||||
|
||||
@ -130,4 +131,94 @@ std::string close_terminal(CommandProcessor& context) {
|
||||
return "__CLOSE_CONNECTION__";
|
||||
}
|
||||
|
||||
struct ScpPath {
|
||||
std::string host;
|
||||
std::string path;
|
||||
};
|
||||
|
||||
ScpPath parse_scp_path(const std::string& arg) {
|
||||
size_t colon_pos = arg.find(':');
|
||||
if(colon_pos != std::string::npos) {
|
||||
return {arg.substr(0, colon_pos), arg.substr(colon_pos+1)};
|
||||
} else {
|
||||
return {"", arg};
|
||||
}
|
||||
}
|
||||
|
||||
std::string scp(CommandProcessor& context, const std::string& source_arg,
|
||||
const std::string& dest_arg) {
|
||||
ScpPath source_path = parse_scp_path(source_arg);
|
||||
ScpPath dest_path = parse_scp_path(dest_arg);
|
||||
Machine* session_machine = context.get_session_machine();
|
||||
INetworkBridge* bridge = context.get_network_bridge();
|
||||
|
||||
Machine* source_machine = source_path.host.empty()
|
||||
? session_machine : bridge->get_machine_by_ip(source_path.host);
|
||||
|
||||
Machine* dest_machine = dest_path.host.empty()
|
||||
? session_machine : bridge->get_machine_by_ip(dest_path.host);
|
||||
|
||||
if(!source_machine) {
|
||||
return "scp: " + source_path.host + ": Name or service not known";
|
||||
}
|
||||
if(!dest_machine) {
|
||||
if(source_machine != session_machine) bridge->release_machine(source_machine->id);
|
||||
return "scp: " + dest_path.host + ": Name or service not known";
|
||||
}
|
||||
|
||||
/* Simplified to use only absolute paths for now. */
|
||||
vfs_node* source_node = find_node_by_path(source_machine->vfs_root, source_path.path);
|
||||
|
||||
if(!source_node || source_node->type != FILE_NODE) {
|
||||
if(source_machine != session_machine) bridge->release_machine(source_machine->id);
|
||||
if(dest_machine != session_machine) bridge->release_machine(dest_machine->id);
|
||||
return "scp: " + source_path.path + ": No such file or directory";
|
||||
}
|
||||
|
||||
vfs_node* dest_node = find_node_by_path(dest_machine->vfs_root, dest_path.path);
|
||||
vfs_node* dest_dir = nullptr;
|
||||
std::string dest_filename;
|
||||
|
||||
if(dest_node && dest_node->type == DIR_NODE) {
|
||||
dest_dir = dest_node;
|
||||
dest_filename = source_node->name;
|
||||
} else {
|
||||
size_t last_slash = dest_path.path.find_last_of('/');
|
||||
if(last_slash != std::string::npos) {
|
||||
std::string parent_path = dest_path.path.substr(0, last_slash);
|
||||
if(parent_path.empty()) parent_path = "/";
|
||||
dest_dir = find_node_by_path(dest_machine->vfs_root, parent_path);
|
||||
dest_filename = dest_path.path.substr(last_slash+1);
|
||||
} else {
|
||||
dest_dir = dest_machine->vfs_root;
|
||||
dest_filename = dest_path.path;
|
||||
}
|
||||
}
|
||||
|
||||
if(!dest_dir) {
|
||||
if(source_machine != session_machine) bridge->release_machine(source_machine->id);
|
||||
if(dest_machine != session_machine) bridge->release_machine(dest_machine->id);
|
||||
return "scp: " + dest_path.path + ": No such file or directory";
|
||||
}
|
||||
|
||||
if(dest_dir->children.count(dest_filename)) {
|
||||
vfs_node* existing_node = dest_dir->children[dest_filename];
|
||||
if(existing_node->type == DIR_NODE) {
|
||||
if(source_machine != session_machine) bridge->release_machine(source_machine->id);
|
||||
if(dest_machine != session_machine) bridge->release_machine(dest_machine->id);
|
||||
return "scp: " + dest_path.path + ": Is a directory";
|
||||
}
|
||||
existing_node->content = source_node->content;
|
||||
} else {
|
||||
vfs_node* new_file = new_node(dest_filename, FILE_NODE, dest_dir);
|
||||
new_file->content = source_node->content;
|
||||
dest_dir->children[dest_filename] = new_file;
|
||||
}
|
||||
|
||||
if(source_machine != session_machine) bridge->release_machine(source_machine->id);
|
||||
if(dest_machine != session_machine) bridge->release_machine(dest_machine->id);
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
} /* namespace api */
|
||||
|
||||
@ -15,6 +15,8 @@ std::string write_file(CommandProcessor& context, const std::string& filename,
|
||||
const std::string& content);
|
||||
std::string ls(CommandProcessor& context);
|
||||
std::string cd(CommandProcessor& context, const std::string& path);
|
||||
std::string scp(CommandProcessor& context, const std::string& source,
|
||||
const std::string& destination);
|
||||
|
||||
/* NETWORK ACTIONS. */
|
||||
std::string ssh(CommandProcessor& context, const std::string& ip);
|
||||
|
||||
@ -28,6 +28,7 @@ LuaProcessor::LuaProcessor(CommandProcessor& context) {
|
||||
bettola_api.set_function("write_file", &api::write_file);
|
||||
bettola_api.set_function("get_current_dir", &api::get_current_dir);
|
||||
bettola_api.set_function("cd", &api::cd);
|
||||
bettola_api.set_function("scp", &api::scp);
|
||||
bettola_api.set_function("close_terminal", &api::close_terminal);
|
||||
bettola_api.set_function("ssh", &api::ssh);
|
||||
bettola_api.set_function("nmap", &api::nmap);
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
#include <sstream>
|
||||
|
||||
#include "vfs.h"
|
||||
|
||||
/* Create a new node. */
|
||||
@ -31,3 +33,27 @@ void delete_vfs_tree(vfs_node* node) {
|
||||
}
|
||||
delete node;
|
||||
}
|
||||
|
||||
/* Find a VFS node by it's absolute path. */
|
||||
vfs_node* find_node_by_path(vfs_node* root, const std::string& path) {
|
||||
if(path == "/") {
|
||||
return root;
|
||||
}
|
||||
vfs_node* current = root;
|
||||
std::stringstream ss(path);
|
||||
std::string segment;
|
||||
|
||||
/* Discard the first empty segment that comes form the leading '/'. */
|
||||
if(path[0] == '/') {
|
||||
std::getline(ss, segment, '/');
|
||||
}
|
||||
|
||||
while(std::getline(ss, segment, '/')) {
|
||||
if(current->type == DIR_NODE && current->children.count(segment)) {
|
||||
current = current->children[segment];
|
||||
} else {
|
||||
return nullptr; /* Path segmenet not found. */
|
||||
}
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
@ -30,4 +30,5 @@ struct vfs_node {
|
||||
vfs_node* new_node(std::string name, vfs_node_type type, vfs_node* parent);
|
||||
vfs_node* find_node_by_id(vfs_node* root, long long id);
|
||||
std::string get_full_path(vfs_node* node);
|
||||
vfs_node* find_node_by_path(vfs_node* root, const std::string& path);
|
||||
void delete_vfs_tree(vfs_node* node);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user