2017-04-14 19:59:01 +00:00
using System ;
using System.Runtime.InteropServices ;
using LuaInterface ;
// TODO - evaluate for re-entrancy problems
namespace BizHawk.Client.Common
{
public unsafe class LuaSandbox
{
2016-02-01 01:54:48 +00:00
protected static Action < string > Logger ;
static System . Runtime . CompilerServices . ConditionalWeakTable < Lua , LuaSandbox > SandboxForThread = new System . Runtime . CompilerServices . ConditionalWeakTable < Lua , LuaSandbox > ( ) ;
public static Action < string > DefaultLogger ;
2017-04-14 19:59:01 +00:00
public void SetLogger ( Action < string > logger )
{
Logger = logger ;
2016-01-31 08:40:48 +00:00
}
2016-02-01 01:54:48 +00:00
public void SetSandboxCurrentDirectory ( string dir )
2016-01-31 08:40:48 +00:00
{
CurrentDirectory = dir ;
}
2016-02-01 01:54:48 +00:00
string CurrentDirectory ;
#if WINDOWS
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool SetCurrentDirectoryW ( byte * lpPathName ) ;
[DllImport("kernel32.dll", SetLastError=true)]
static extern uint GetCurrentDirectoryW ( uint nBufferLength , byte * pBuffer ) ;
#endif
bool CoolSetCurrentDirectory ( string path , string currDirSpeedHack = null )
{
string target = CurrentDirectory + "\\" ;
2017-04-14 19:59:01 +00:00
// first we'll bypass it with a general hack: dont do any setting if the value's already there (even at the OS level, setting the directory can be slow)
// yeah I know, not the smoothest move to compare strings here, in case path normalization is happening at some point
// but you got any better ideas?
2016-02-01 01:54:48 +00:00
if ( currDirSpeedHack = = null )
currDirSpeedHack = CoolGetCurrentDirectory ( ) ;
if ( currDirSpeedHack = = path )
return true ;
2017-05-09 18:19:55 +00:00
// WARNING: setting the current directory is SLOW!!! security checks for some reason.
// so we're bypassing it with windows hacks
2016-02-01 01:54:48 +00:00
#if WINDOWS
fixed ( byte * pstr = & System . Text . Encoding . Unicode . GetBytes ( target + "\0" ) [ 0 ] )
2017-04-14 19:59:01 +00:00
return SetCurrentDirectoryW ( pstr ) ;
#else
2017-05-09 18:19:55 +00:00
if ( System . IO . Directory . Exists ( CurrentDirectory ) ) // race condition for great justice
2017-04-14 19:59:01 +00:00
{
2017-05-09 18:19:55 +00:00
Environment . CurrentDirectory = CurrentDirectory ; // thats right, you can't set a directory as current that doesnt exist because .net's got to do SENSELESS SLOW-ASS SECURITY CHECKS on it and it can't do that on a NONEXISTENT DIRECTORY
2017-04-14 19:59:01 +00:00
return true ;
}
2017-05-03 03:09:50 +00:00
else return false ;
2016-02-01 01:54:48 +00:00
#endif
}
string CoolGetCurrentDirectory ( )
{
2017-05-09 18:19:55 +00:00
// GUESS WHAT!
// .NET DOES A SECURITY CHECK ON THE DIRECTORY WE JUST RETRIEVED
// AS IF ASKING FOR THE CURRENT DIRECTORY IS EQUIVALENT TO TRYING TO ACCESS IT
// SCREW YOU
2016-02-01 01:54:48 +00:00
#if WINDOWS
2017-04-14 19:59:01 +00:00
var buf = new byte [ 32768 ] ;
fixed ( byte * pBuf = & buf [ 0 ] )
2016-02-01 01:54:48 +00:00
{
uint ret = GetCurrentDirectoryW ( 32767 , pBuf ) ;
return System . Text . Encoding . Unicode . GetString ( buf , 0 , ( int ) ret * 2 ) ;
}
2017-04-14 19:59:01 +00:00
#else
return Environment . CurrentDirectory ;
2016-02-01 01:54:48 +00:00
#endif
}
void Sandbox ( Action callback , Action exceptionCallback )
{
2017-04-14 19:59:01 +00:00
string savedEnvironmentCurrDir = null ;
try
{
2016-02-01 01:54:48 +00:00
savedEnvironmentCurrDir = Environment . CurrentDirectory ;
if ( CurrentDirectory ! = null )
2017-04-14 19:59:01 +00:00
CoolSetCurrentDirectory ( CurrentDirectory , savedEnvironmentCurrDir ) ;
EnvironmentSandbox . Sandbox ( callback ) ;
}
catch ( LuaException ex )
2016-03-31 19:11:20 +00:00
{
2017-04-14 19:59:01 +00:00
Console . WriteLine ( ex ) ;
Logger ( ex . ToString ( ) ) ;
2017-04-15 20:37:30 +00:00
exceptionCallback ? . Invoke ( ) ;
2017-04-14 19:59:01 +00:00
}
finally
2016-01-31 09:18:34 +00:00
{
2016-02-01 01:54:48 +00:00
if ( CurrentDirectory ! = null )
2017-04-14 19:59:01 +00:00
CoolSetCurrentDirectory ( savedEnvironmentCurrDir ) ;
}
2016-02-01 01:54:48 +00:00
}
public static LuaSandbox CreateSandbox ( Lua thread , string initialDirectory )
{
var sandbox = new LuaSandbox ( ) ;
SandboxForThread . Add ( thread , sandbox ) ;
sandbox . SetSandboxCurrentDirectory ( initialDirectory ) ;
sandbox . SetLogger ( DefaultLogger ) ;
return sandbox ;
}
public static LuaSandbox GetSandbox ( Lua thread )
{
2017-04-14 19:59:01 +00:00
// this is just placeholder.
// we shouldnt be calling a sandbox with no thread--construct a sandbox properly
2016-02-01 01:54:48 +00:00
if ( thread = = null )
{
return new LuaSandbox ( ) ;
}
lock ( SandboxForThread )
{
LuaSandbox sandbox ;
if ( SandboxForThread . TryGetValue ( thread , out sandbox ) )
return sandbox ;
else
{
2017-04-14 19:59:01 +00:00
// for now: throw exception (I want to manually creating them)
// return CreateSandbox(thread);
2016-02-01 01:54:48 +00:00
throw new InvalidOperationException ( "HOARY GORILLA HIJINX" ) ;
}
}
}
public static void Sandbox ( Lua thread , Action callback , Action exceptionCallback = null )
{
GetSandbox ( thread ) . Sandbox ( callback , exceptionCallback ) ;
2017-04-14 19:59:01 +00:00
}
}
}