Skip to content

Commit

Permalink
Change to use a sandbox root element for opening io files in lua
Browse files Browse the repository at this point in the history
Add annotations for FileSystem.lua
  • Loading branch information
JonBooth78 committed Oct 8, 2023
1 parent 172185e commit 22f070b
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 38 deletions.
27 changes: 27 additions & 0 deletions data/libs/FileSystem.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---@class FileSystem : FileSystemBase
local FileSystem = package.core["FileSystem"]

--- Wrapper for our patched io.open that ensures files are opened inside the sandbox.
--- Prefer using this to io.open
---
--- Files in the user folder can be read or written to
--- Files in the data folder are read only.
---
---
---
--- Example:
--- > f = FileSystem.Open( "USER", "my_file.txt", "w" )
--- > f:write( "file contents" )
--- > f:close()
---
---@param root string A FileSystemRoot constant. Can be either "DATA" or "USER"
---@param filename string The name of the file to open, relative to the root
---@param mode string|nil The mode to open the file in, defaults to read only
---@return file A lua io file
function FileSystem.Open( root, filename, mode )
if not mode then mode = "r" end

return io.open( filename, mode, root )
end

return FileSystem
31 changes: 31 additions & 0 deletions data/meta/FileSystemBase.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
-- Copyright © 2008-2023 Pioneer Developers. See AUTHORS.txt for details
-- Licensed under the terms of the GPL v3. See licenses/GPL-3.txt

-- This file implements type information about C++ classes for Lua static analysis
-- This is used in FileSyestem.lua whcih then extends that.

---@meta

---@class FileSystemBase
local FileSystemBase = {}

---@param root string A FileSystemRoot constant. Can be either "DATA" or "USER"
---@return string[] files A list of files as full paths from the root
---@return string[] dirs A list of dirs as full paths from the root
---
--- Example:
--- > local files, dirs = FileSystem.ReadDirectory(root, path)
function FileSystemBase.ReadDirectory(root, path) end

--- Join the passed arguments into a path, correctly handling separators and .
--- and .. special dirs.
---
---@param arg string[] A list of path elements to be joined
---@return string The joined path elements
function FileSystemBase.JoinPath( ... ) end

---@param dir_name string The name of the folder to create in the user directory
---@return boolean Success
function FileSystemBase.MakeUserDataDirectory( dir_name ) end

return FileSystemBase
89 changes: 61 additions & 28 deletions src/lua/LuaFileSystem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,59 @@
* will get a Lua error.
*/

FileSystem::FileSource* get_filesytem_for_root(LuaFileSystem::Root root)
{
FileSystem::FileSource* fs = nullptr;
switch (root) {
case LuaFileSystem::ROOT_USER:
fs = &FileSystem::userFiles;
break;

case LuaFileSystem::ROOT_DATA:
fs = &FileSystem::gameDataFiles;
break;

default:
assert(0); // can't happen
}
return fs;
}

std::string LuaFileSystem::lua_path_to_fs_path(lua_State* l, const char* root_name, const char* path, const char* access)
{
const LuaFileSystem::Root root = static_cast<LuaFileSystem::Root>(LuaConstants::GetConstant(l, "FileSystemRoot", root_name));

if (root == LuaFileSystem::ROOT_DATA)
{
// check the acces mode is allowed:

// default mode is read only
if (access[0] != 0)
{
if (access[0] != 'r' || access[1] != 0)
{
// we are requesting an access mode not allowed for the user folder
// as you can't write there.
luaL_error(l, "'%s' is not valid for opening a file in root '%s'", access, root_name);
return "";
}
}
}

FileSystem::FileSource* fs = get_filesytem_for_root(root);
assert(fs);

try
{
return std::move(fs->Lookup(path).GetAbsolutePath());
}
catch (std::invalid_argument e)
{
luaL_error(l, "'%s' is not valid for opening a file in root '%s' - Is the file location within the root?", path, root_name);
return "";
}
}

static void push_date_time(lua_State *l, const Time::DateTime &dt)
{
int year, month, day, hour, minute, second;
Expand Down Expand Up @@ -65,20 +118,7 @@ static int l_filesystem_read_dir(lua_State *l)
if (lua_gettop(l) > 1)
path = luaL_checkstring(l, 2);

FileSystem::FileSource *fs = nullptr;
switch (root) {
case LuaFileSystem::ROOT_USER:
fs = &FileSystem::userFiles;
break;

case LuaFileSystem::ROOT_DATA:
fs = &FileSystem::gameDataFiles;
break;

default:
assert(0); // can't happen
return 0;
}
FileSystem::FileSource* fs = get_filesytem_for_root(root);

assert(fs);

Expand Down Expand Up @@ -157,8 +197,8 @@ static int l_filesystem_join_path(lua_State *l)
*
* > local path = FileSystem.MakeUserDataDirectory( dir_name )
*
* Creating the given directory if it's missing, returning the name
* full name of the directory
* Creating the given directory if it's missing, returning a boolean
* indicating success
*
* Availability:
*
Expand All @@ -177,20 +217,13 @@ static int l_filesystem_make_user_directory(lua_State* l)

FileSystem::FileInfo f = FileSystem::userFiles.Lookup(dir);

if (f.IsDir())
{
std::string fullDirName = f.GetAbsolutePath();
lua_pushlstring(l, fullDirName.c_str(), fullDirName.size());
return 1;
} else
{
lua_pushlstring(l, "", 1 );
return 0;
}
lua_pushboolean(l, f.IsDir());
return 1;
}
catch (const std::invalid_argument&) {
luaL_error(l, "unable to create directory");
return 0;
luaL_error(l, "unable to create directory the argument is invalid");
lua_pushboolean(l, 0);
return 1;
}
}

Expand Down
8 changes: 8 additions & 0 deletions src/lua/LuaFileSystem.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,21 @@
#ifndef _LUAFILESYSTEM_H
#define _LUAFILESYSTEM_H

#include <string>

struct lua_State;

namespace LuaFileSystem {
void Register();

enum Root { // <enum scope='LuaFileSystem' name=FileSystemRoot prefix=ROOT_ public>
ROOT_USER,
ROOT_DATA
};

// will throw lua errors if not allowed
// and return an empty string
std::string lua_path_to_fs_path(lua_State* l, const char* root, const char* path, const char* access);
} // namespace LuaFileSystem

#endif
31 changes: 21 additions & 10 deletions src/lua/core/Sandbox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "core/Log.h"
#include "utils.h"
#include "FileSystem.h"
#include "LuaFileSystem.h"

static int l_d_null_userdata(lua_State *L)
{
Expand Down Expand Up @@ -109,19 +110,29 @@ static lua_CFunction l_original_io_open = nullptr;

static int l_patched_io_open(lua_State* L)
{
std::string path = lua_tostring(L, 1);
path = FileSystem::NormalisePath(path);
if (lua_gettop(L) != 3)
{
luaL_error(L, "Wrong number of arguments for io.open(). You should be using FileSystem.Open() instead");
return 0;
}
const char* path_arg = lua_tostring(L, 1);
const char* access_arg = lua_tostring(L, 2);
const char* root_arg = lua_tostring(L, 3);

std::string userDir = FileSystem::GetUserDir();
// TODO: should we add a file separator here?

if (path.rfind(userDir, 0) == 0)
std::string path = LuaFileSystem::lua_path_to_fs_path(L, root_arg, path_arg, access_arg);

if (path.length() == 0)
{
// path starts with the userDir, we're good to go
return l_original_io_open(L);
luaL_error(L, "attempt to access filesystem in an invalid user folder location");
return 0;
}
luaL_error(L, "attempt to access filesystem outside of the user data folder");
return 0;

lua_pushstring(L, path.c_str());
lua_replace(L, 1);

const int rv = l_original_io_open(L);

return rv;
}

static const luaL_Reg STANDARD_LIBS[] = {
Expand Down

0 comments on commit 22f070b

Please sign in to comment.