mirror of
https://github.com/openbullet/openbullet.git
synced 2023-10-21 07:33:42 -05:00
Added FileLocker to avoid exceptions from cross thread IO operations
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using RuriLib.Functions.Conditions;
|
||||
using RuriLib.Functions.Conversions;
|
||||
using RuriLib.Functions.Files;
|
||||
using RuriLib.LS;
|
||||
using RuriLib.Models;
|
||||
using RuriLib.ViewModels;
|
||||
@@ -604,7 +605,7 @@ namespace RuriLib
|
||||
|
||||
case UtilityGroup.File:
|
||||
var file = ReplaceValues(filePath, data);
|
||||
ThrowIfNotInCWD(file);
|
||||
Files.ThrowIfNotInCWD(file);
|
||||
|
||||
switch (fileAction)
|
||||
{
|
||||
@@ -613,43 +614,55 @@ namespace RuriLib
|
||||
break;
|
||||
|
||||
case FileAction.Read:
|
||||
data.Variables.Set(new CVar(variableName, File.ReadAllText(file), isCapture));
|
||||
lock (FileLocker.GetLock(file))
|
||||
data.Variables.Set(new CVar(variableName, File.ReadAllText(file), isCapture));
|
||||
break;
|
||||
|
||||
case FileAction.ReadLines:
|
||||
data.Variables.Set(new CVar(variableName, File.ReadAllLines(file).ToList(), isCapture));
|
||||
lock (FileLocker.GetLock(file))
|
||||
data.Variables.Set(new CVar(variableName, File.ReadAllLines(file).ToList(), isCapture));
|
||||
break;
|
||||
|
||||
case FileAction.Write:
|
||||
CreatePath(file);
|
||||
File.WriteAllText(file, replacedInput.Unescape());
|
||||
Files.CreatePath(file);
|
||||
lock (FileLocker.GetLock(file))
|
||||
File.WriteAllText(file, replacedInput.Unescape());
|
||||
break;
|
||||
|
||||
case FileAction.WriteLines:
|
||||
CreatePath(file);
|
||||
File.WriteAllLines(file, ReplaceValuesRecursive(inputString, data).Select(i => i.Unescape()));
|
||||
Files.CreatePath(file);
|
||||
lock (FileLocker.GetLock(file))
|
||||
File.WriteAllLines(file, ReplaceValuesRecursive(inputString, data).Select(i => i.Unescape()));
|
||||
break;
|
||||
|
||||
case FileAction.Append:
|
||||
File.AppendAllText(file, replacedInput.Unescape());
|
||||
Files.CreatePath(file);
|
||||
lock (FileLocker.GetLock(file))
|
||||
File.AppendAllText(file, replacedInput.Unescape());
|
||||
break;
|
||||
|
||||
case FileAction.AppendLines:
|
||||
File.AppendAllLines(file, ReplaceValuesRecursive(inputString, data).Select(i => i.Unescape()));
|
||||
Files.CreatePath(file);
|
||||
lock (FileLocker.GetLock(file))
|
||||
File.AppendAllLines(file, ReplaceValuesRecursive(inputString, data).Select(i => i.Unescape()));
|
||||
break;
|
||||
|
||||
case FileAction.Copy:
|
||||
var fileCopyLocation = ReplaceValues(inputString, data);
|
||||
ThrowIfNotInCWD(fileCopyLocation);
|
||||
CreatePath(fileCopyLocation);
|
||||
File.Copy(file, fileCopyLocation);
|
||||
Files.ThrowIfNotInCWD(fileCopyLocation);
|
||||
Files.CreatePath(fileCopyLocation);
|
||||
lock (FileLocker.GetLock(file))
|
||||
lock (FileLocker.GetLock(fileCopyLocation))
|
||||
File.Copy(file, fileCopyLocation);
|
||||
break;
|
||||
|
||||
case FileAction.Move:
|
||||
var fileMoveLocation = ReplaceValues(inputString, data);
|
||||
ThrowIfNotInCWD(fileMoveLocation);
|
||||
CreatePath(fileMoveLocation);
|
||||
File.Move(file, fileMoveLocation);
|
||||
Files.ThrowIfNotInCWD(fileMoveLocation);
|
||||
Files.CreatePath(fileMoveLocation);
|
||||
lock (FileLocker.GetLock(file))
|
||||
lock (FileLocker.GetLock(fileMoveLocation))
|
||||
File.Move(file, fileMoveLocation);
|
||||
break;
|
||||
}
|
||||
data.Log(new LogEntry($"Executed action {fileAction} on file {file}", isCapture ? Colors.Tomato : Colors.Yellow));
|
||||
@@ -657,7 +670,7 @@ namespace RuriLib
|
||||
|
||||
case UtilityGroup.Folder:
|
||||
var folder = ReplaceValues(folderPath, data);
|
||||
ThrowIfNotInCWD(folder);
|
||||
Files.ThrowIfNotInCWD(folder);
|
||||
|
||||
switch (folderAction)
|
||||
{
|
||||
@@ -678,21 +691,5 @@ namespace RuriLib
|
||||
}
|
||||
catch(Exception ex) { data.Log(new LogEntry(ex.Message, Colors.Tomato)); }
|
||||
}
|
||||
|
||||
private void ThrowIfNotInCWD(string path)
|
||||
{
|
||||
if (!path.IsSubPathOf(Directory.GetCurrentDirectory()))
|
||||
{
|
||||
throw new UnauthorizedAccessException("For security reasons, you cannot interact with paths outside of the current working directory");
|
||||
}
|
||||
}
|
||||
|
||||
private void CreatePath(string file)
|
||||
{
|
||||
if (!Directory.Exists(Path.GetDirectoryName(file)))
|
||||
{
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(file));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace RuriLib
|
||||
{
|
||||
/// <summary>
|
||||
/// Singleton class that manages application-wide file locking to avoid cross thread IO operations on the same file.
|
||||
/// </summary>
|
||||
public static class FileLocker
|
||||
{
|
||||
/// <summary>
|
||||
/// Maps file names to lockable objects.
|
||||
/// </summary>
|
||||
public static Hashtable Hashtable = new Hashtable();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a lock by file name or creates one if it doesn't exist.
|
||||
/// </summary>
|
||||
/// <param name="fileName">The name of the file to access</param>
|
||||
/// <returns>An object that can be used in a lock statement.</returns>
|
||||
public static object GetLock(string fileName)
|
||||
{
|
||||
if (!Hashtable.ContainsKey(fileName))
|
||||
{
|
||||
Hashtable.Add(fileName, new object());
|
||||
}
|
||||
|
||||
return Hashtable[fileName];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -94,5 +94,31 @@ namespace RuriLib.Functions.Files
|
||||
|
||||
return Regex.Replace(name, invalidRegStr, underscore ? "_" : "");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Throws an UnauthorizedAccessException if the path is not part of the current working directory.
|
||||
/// </summary>
|
||||
/// <param name="path">The absolute or relative path.</param>
|
||||
public static void ThrowIfNotInCWD(string path)
|
||||
{
|
||||
if (!path.IsSubPathOf(Directory.GetCurrentDirectory()))
|
||||
{
|
||||
throw new UnauthorizedAccessException("For security reasons, you cannot interact with paths outside of the current working directory");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the folder structure that contains a certain files if it doesn't already exist.
|
||||
/// </summary>
|
||||
/// <param name="file">The absolute or relative path to the file.</param>
|
||||
public static void CreatePath(string file)
|
||||
{
|
||||
var dirName = Path.GetDirectoryName(file);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(dirName) && !Directory.Exists(dirName))
|
||||
{
|
||||
Directory.CreateDirectory(dirName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,6 +254,7 @@
|
||||
<Compile Include="CaptchaServices\RuCaptcha.cs" />
|
||||
<Compile Include="CaptchaServices\Service.cs" />
|
||||
<Compile Include="Extensions.cs" />
|
||||
<Compile Include="FileLocker.cs" />
|
||||
<Compile Include="Functions\Conversion\Conversion.cs" />
|
||||
<Compile Include="Functions\Crypto\Crypto.cs" />
|
||||
<Compile Include="Functions\Download\Download.cs" />
|
||||
|
||||
Reference in New Issue
Block a user