From 6618b0e0a2d9593891fae81c49318429eced4e11 Mon Sep 17 00:00:00 2001 From: Ritchie Cunningham Date: Tue, 4 Nov 2025 21:11:43 +0000 Subject: [PATCH] [Add] Implement VFS permissions and ls -l --- assets/scripts/bin/ls.lua | 59 +++++++++++++++++++++++++++++- common/src/db/database_manager.cpp | 20 +++++++--- common/src/db/vfs_repository.cpp | 41 +++++++++++++-------- common/src/db/vfs_repository.h | 6 ++- common/src/lua_processor.cpp | 11 ++++-- common/src/machine_manager.cpp | 21 +++++++---- common/src/vfs.cpp | 14 ++++--- common/src/vfs.h | 8 +++- server/src/network_manager.cpp | 15 +++++--- 9 files changed, 150 insertions(+), 45 deletions(-) diff --git a/assets/scripts/bin/ls.lua b/assets/scripts/bin/ls.lua index 46b18b5..0c83e52 100644 --- a/assets/scripts/bin/ls.lua +++ b/assets/scripts/bin/ls.lua @@ -2,4 +2,61 @@ -- -- Iterate over the 'children' map exposed via C++. -return bettola.ls(context) +local function format_permissions(perms) + local rwx = { "-", "-", "-", "-", "-", "-","-", "-", "-" } + if(perms & 0x100) ~= 0 then rwx[1] = "r" end -- Owner read. + if(perms & 0x080) ~= 0 then rwx[2] = "w" end -- Owner write. + if(perms & 0x040) ~= 0 then rwx[3] = "x" end -- Owner execute. + if(perms & 0x020) ~= 0 then rwx[4] = "r" end -- Group read. + if(perms & 0x010) ~= 0 then rwx[5] = "w" end -- Group write. + if(perms & 0x008) ~= 0 then rwx[6] = "x" end -- Group execute. + if(perms & 0x004) ~= 0 then rwx[7] = "r" end -- Other read. + if(perms & 0x002) ~= 0 then rwx[8] = "w" end -- Other write. + if(perms & 0x001) ~= 0 then rwx[9] = "x" end -- Other execute. + return table.concat(rwx) +end + +local function get_file_size(node) + if node.type == 0 then -- FILE_NODE. + return #node.content + else + return 0 -- Dirs don't have content size in this context. + end +end + +local function ls_long_format(dir) + local output = {} + for name, node in pairs(dir.children) do + local line_type = (node.type == 1) and "d" or "-" + local perms = format_permissions(node.permissions) + local owner = node.owner_id + local group = node.group_id + local size = get_file_size(node) + table.insert(output, string.format("%s%s %d %d %5d %s", line_type, perms, owner, group, size, name)) + end + table.sort(output) + return table.concat(output, "\n") +end + +local function ls_short_format(dir) + local output = {} + for name, node in pairs(dir.children) do + local display_name = name + if node.type == 1 then -- DIR_NODE + display_name = display_name .. "/" + elseif node.type == 2 then --EXEC_NODE + display_name = display_name .. "*" + end + table.insert(output, display_name) + end + table.sort(output) + return table.concat(output, "\t") -- Tab separated short format. +end + +local current_dir = bettola.get_current_dir(context); + +if arg[1] == "-l" then + return ls_long_format(current_dir) +else + return ls_short_format(current_dir) +end diff --git a/common/src/db/database_manager.cpp b/common/src/db/database_manager.cpp index bdc74b1..343307f 100644 --- a/common/src/db/database_manager.cpp +++ b/common/src/db/database_manager.cpp @@ -30,7 +30,10 @@ DatabaseManager::DatabaseManager(const std::string& db_path) : "parent_id INTEGER," "name TEXT NOT NULL," "type INTEGER NOT NULL," - "content TEXT" + "content TEXT," + "owner_id INTEGER NOT NULL," + "group_id INTEGER NOT NULL," + "permissions INTEGER NOT NULL" ");"; _db << "CREATE TABLE IF NOT EXISTS services (" "id INTEGER PRIMARY KEY AUTOINCREMENT," @@ -62,19 +65,24 @@ bool DatabaseManager::create_player(const std::string& username, const std::stri _player_repository->set_home_machine_id(player_id, machine_id); /* Create the root dir for the new machine's VFS. */ - long long root_id = _vfs_repository->create_node(machine_id, nullptr, "/", DIR_NODE); + long long root_id = _vfs_repository->create_node(machine_id, nullptr, "/", DIR_NODE, + "", player_id, player_id, 0755); /* Create default subdirs. */ - _vfs_repository->create_node(machine_id, &root_id, "home", DIR_NODE); - _vfs_repository->create_node(machine_id, &root_id, "etc", DIR_NODE); + _vfs_repository->create_node(machine_id, &root_id, "home", DIR_NODE, + "", player_id, player_id, 0755); + _vfs_repository->create_node(machine_id, &root_id, "etc", DIR_NODE, + "", player_id, player_id, 0755); /* Create /bin and get it's ID */ - long long bin_id = _vfs_repository->create_node(machine_id, &root_id, "bin", DIR_NODE); + long long bin_id = _vfs_repository->create_node(machine_id, &root_id, "bin", DIR_NODE, + "", player_id, player_id, 0755); /* Copy scripts from template into new machine's /bin */ vfs_node* template_bin = vfs_template->children["bin"]; for(auto const& [name, node] : template_bin->children) { - _vfs_repository->create_node(machine_id, &bin_id, name, node->type, node->content); + _vfs_repository->create_node(machine_id, &bin_id, name, node->type, node->content, + player_id, player_id, 0755); } /* Add default SSH service. */ diff --git a/common/src/db/vfs_repository.cpp b/common/src/db/vfs_repository.cpp index 7f90658..71426f8 100644 --- a/common/src/db/vfs_repository.cpp +++ b/common/src/db/vfs_repository.cpp @@ -4,30 +4,41 @@ VFSRepository::VFSRepository(sqlite::database& db) : _db(db) {} long long VFSRepository::create_node(long long machine_id, long long* parent_id, const std::string& name, vfs_node_type type, - const std::string& content) { + const std::string& content, + uint32_t owner_id, uint32_t group_id, + uint16_t permissions) { if(parent_id) { - _db << "INSERT INTO vfs_nodes (machine_id, parent_id, name, type, content) " - "VALUES (?, ?, ?, ?, ?);" - << machine_id << *parent_id << name << type << content; + _db << "INSERT INTO vfs_nodes (machine_id, parent_id, name, type, content, " + "owner_id, group_id, permissions) " + "VALUES (?, ?, ?, ?, ?, ?, ?, ?);" + << machine_id << *parent_id << name << type << content << owner_id + << group_id << permissions; } else { - _db << "INSERT INTO vfs_nodes (machine_id, parent_id, name, type, content) " - "VALUES (?, NULL, ?, ?, ?);" - << machine_id << name << type << content; + _db << "INSERT INTO vfs_nodes (machine_id, parent_id, name, type, content, " + "owner_id, group_id, permissions) " + "VALUES (?, NULL, ?, ?, ?, ?, ?, ?);" + << machine_id << name << type << content + << owner_id << group_id << permissions; } return _db.last_insert_rowid(); } std::vector VFSRepository::get_nodes_for_machine(long long machine_id) { std::vector nodes; - _db << "SELECT id, parent_id, name, type, content FROM vfs_nodes WHERE machine_id = ?;" + _db << "SELECT id, parent_id, name, type, content, owner_id, group_id, permissions " + "FROM vfs_nodes WHERE machine_id = ?;" << machine_id - >> [&](long long id, long long parent_id, std::string name, int type, std::string content) { - vfs_node* node = new vfs_node(); - node->id = id; - node->parent_id = parent_id; - node->name = name; - node->type = (vfs_node_type) type; - node->content = content; + >> [&](long long id, long long parent_id, std::string name, int type, + std::string content, uint32_t owner_id, uint32_t group_id, uint16_t permissions) { + vfs_node* node = new vfs_node(); + node->id = id; + node->parent_id = parent_id; + node->name = name; + node->type = (vfs_node_type) type; + node->content = content; + node->owner_id = owner_id; + node->group_id = group_id; + node->permissions = permissions; nodes.push_back(node); }; diff --git a/common/src/db/vfs_repository.h b/common/src/db/vfs_repository.h index 2a80608..97a3b68 100644 --- a/common/src/db/vfs_repository.h +++ b/common/src/db/vfs_repository.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -12,7 +13,10 @@ public: long long create_node(long long machine_id, long long* parent_id, const std::string& name, vfs_node_type type, - const std::string& content = ""); + const std::string& content = "", + uint32_t owner_id = 0, uint32_t group_id = 0, + uint16_t permissions = 0755); + std::vector get_nodes_for_machine(long long machine_id); private: diff --git a/common/src/lua_processor.cpp b/common/src/lua_processor.cpp index 1b003e5..d74c3ee 100644 --- a/common/src/lua_processor.cpp +++ b/common/src/lua_processor.cpp @@ -27,10 +27,13 @@ LuaProcessor::LuaProcessor(Session& context) { /* Expose vfs_node struct members to Lua. */ _lua.new_usertype("vfs_node", - "name", &vfs_node::name, - "type", &vfs_node::type, - "children", &vfs_node::children, - "content", &vfs_node::content); + "name", &vfs_node::name, + "type", &vfs_node::type, + "children", &vfs_node::children, + "content", &vfs_node::content, + "owner_id", &vfs_node::owner_id, + "group_id", &vfs_node::group_id, + "permissions", &vfs_node::permissions); /* Expose CommandProcessor to Lua. DON'T ALLOW SCRIPTS TO CREATE IT THOUGH! */ _lua.new_usertype("Session", sol::no_constructor); diff --git a/common/src/machine_manager.cpp b/common/src/machine_manager.cpp index e7cc33b..2ba4ccb 100644 --- a/common/src/machine_manager.cpp +++ b/common/src/machine_manager.cpp @@ -15,7 +15,10 @@ vfs_node* copy_vfs_node(vfs_node* original, vfs_node* new_parent) { } /* Create the new node and copy its properties. */ - vfs_node* new_copy = new_node(original->name, original->type, new_parent); + vfs_node* new_copy = new_node(original->name, original->type, new_parent, + original->owner_id, original->group_id, + original->permissions); + new_copy->content = original->content; /* Recursively copy all children. */ @@ -30,7 +33,7 @@ MachineManager::MachineManager(DatabaseManager* db_manager) : _db_manager(db_manager) { /* Create template VFS that holds shared, read-only directories. */ _vfs_template_root = new_node("/", DIR_NODE, nullptr); - vfs_node* bin = new_node("bin", DIR_NODE, _vfs_template_root); + vfs_node* bin = new_node("bin", DIR_NODE, _vfs_template_root, 0, 0, 0755); /* System owned. */ _vfs_template_root->children["bin"] = bin; /* Load all scripts from assets/scripts/bin into the VFS. */ @@ -42,7 +45,7 @@ MachineManager::MachineManager(DatabaseManager* db_manager) : buffer << t.rdbuf(); std::string filename_with_ext = entry.path().filename().string(); std::string filename = filename_with_ext.substr(0, filename_with_ext.find_last_of('.')); - vfs_node* script_node = new_node(filename, EXEC_NODE, bin); + vfs_node* script_node = new_node(filename, EXEC_NODE, bin, 0, 0, 0755); /* System owned. */ script_node->content = util::xor_string(buffer.str()); bin->children[filename] = script_node; fprintf(stderr, "Loaded executable: /bin/%s\n", filename.c_str()); @@ -58,24 +61,28 @@ Machine* MachineManager::create_machine(uint32_t id, const std::string& hostname const std::string& system_type) { auto* new_machine = new Machine(id, hostname); - vfs_node* root = new_node("/", DIR_NODE, nullptr); + vfs_node* root = new_node("/", DIR_NODE, nullptr, 0, 0, 0755); /* Root owned by system (0). */ /* Create directories for this specific VFS. */ - vfs_node* home = new_node("home", DIR_NODE, root); + vfs_node* home = new_node("home", DIR_NODE, root, id, id, 0755); /* Owned by player. */ vfs_node* user = new_node("user", DIR_NODE, home); home->children["user"] = user; - vfs_node* readme = new_node("readme.txt", FILE_NODE, user); + vfs_node* readme = new_node("readme.txt", FILE_NODE, user, id, id, 0644); /* Owned by player. */ readme->content = "Welcome to your new virtual machine."; user->children["readme.txt"] = readme; /* Link to the shared directories from the template. */ root->children["bin"] = copy_vfs_node(_vfs_template_root->children["bin"], root); + /* Ensure copied bin directory has the correct owner/group/permissions. */ + root->children["bin"]->owner_id = 0; /* System owned. */ + root->children["bin"]->group_id = 0; + root->children["bin"]->permissions = 0755; /* Assign the VFS to the new machine. */ new_machine->vfs_root = root; if(system_type == "npc") { - vfs_node* npc_file = new_node("npc_system.txt", FILE_NODE, root); + vfs_node* npc_file = new_node("npc_system.txt", FILE_NODE, root, 0, 0, 0644); npc_file->content = "This guy sucks nuts!"; root->children["npc_system.txt"] = npc_file; diff --git a/common/src/vfs.cpp b/common/src/vfs.cpp index 90cd6e2..f4f152f 100644 --- a/common/src/vfs.cpp +++ b/common/src/vfs.cpp @@ -3,11 +3,15 @@ #include "vfs.h" /* Create a new node. */ -vfs_node* new_node(std::string name, vfs_node_type type, vfs_node* parent) { - vfs_node* node = new vfs_node(); - node->name = name; - node->type = type; - node->parent = parent; +vfs_node* new_node(std::string name, vfs_node_type type, vfs_node* parent, + uint32_t owner_id, uint32_t group_id, uint16_t permissions) { + vfs_node* node = new vfs_node(); + node->name = name; + node->type = type; + node->parent = parent; + node->owner_id = owner_id; + node->group_id = group_id; + node->permissions = permissions; return node; } diff --git a/common/src/vfs.h b/common/src/vfs.h index fcc9d86..756c56b 100644 --- a/common/src/vfs.h +++ b/common/src/vfs.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -20,6 +21,10 @@ struct vfs_node { std::string name; vfs_node_type type; + uint32_t owner_id; + uint32_t group_id; + uint16_t permissions; + /* Files. */ std::string content; @@ -28,7 +33,8 @@ struct vfs_node { vfs_node* parent; }; -vfs_node* new_node(std::string name, vfs_node_type type, vfs_node* parent); +vfs_node* new_node(std::string name, vfs_node_type type, vfs_node* parent, + uint32_t owner_id = 0, uint32_t group_id = 0, uint16_t permissions=0755); 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); diff --git a/server/src/network_manager.cpp b/server/src/network_manager.cpp index f3056b7..3f4612c 100644 --- a/server/src/network_manager.cpp +++ b/server/src/network_manager.cpp @@ -304,7 +304,8 @@ void NetworkManager::release_machine(long long machine_id) { void NetworkManager::_recursive_save_vfs(long long machine_id, vfs_node* node, long long* parent_id) { long long current_node_id = _db_manager->vfs().create_node(machine_id, parent_id, node->name, node->type, - node->content); + node->content, node->owner_id, + node->group_id, node->permissions); if(node->type == DIR_NODE) { for(auto const& [name, child] : node->children) { @@ -347,15 +348,19 @@ void NetworkManager::_seed_npc_machines(void) { long long machine_id = _db_manager->machines().create({}, def.hostname, def.ip); /* Create a basic VFS for the NPC machines. */ - long long root_id = _db_manager->vfs().create_node(machine_id, nullptr, "/", DIR_NODE); + long long root_id = _db_manager->vfs().create_node(machine_id, nullptr, "/", DIR_NODE, + "", 0, 0, 0755); /* System owned. */ - _db_manager->vfs().create_node(machine_id, &root_id, "etc", DIR_NODE); + _db_manager->vfs().create_node(machine_id, &root_id, "etc", DIR_NODE, + "", 0, 0, 0755); /*System owned. */ - long long bin_id = _db_manager->vfs().create_node(machine_id, &root_id, "bin", DIR_NODE); + long long bin_id = _db_manager->vfs().create_node(machine_id, &root_id, "bin", DIR_NODE, + "", 0, 0, 0755); /* System owned. */ vfs_node* template_bin = _machine_manager.get_vfs_template()->children["bin"]; for(auto const& [name, node] : template_bin->children) { - _db_manager->vfs().create_node(machine_id, &bin_id, name, node->type, node->content); + _db_manager->vfs().create_node(machine_id, &bin_id, name, node->type, node->content, + 0, 0, 0755); /* System owned. */ } for(const auto& service : def.services) {