diff --git a/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs b/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs
index 9a874b912f..2c047d57f9 100644
--- a/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs
+++ b/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs
@@ -137,9 +137,14 @@ namespace Tgstation.Server.Host.Components.StaticFiles
string ValidateConfigRelativePath(string configurationRelativePath)
{
- if (String.IsNullOrEmpty(configurationRelativePath))
+ var nullOrEmptyCheck = String.IsNullOrEmpty(configurationRelativePath);
+ if (nullOrEmptyCheck)
configurationRelativePath = ".";
- return ioManager.ResolvePath(configurationRelativePath);
+ var resolved = ioManager.ResolvePath(configurationRelativePath);
+ var local = !nullOrEmptyCheck ? ioManager.ResolvePath(".") : null;
+ if (!nullOrEmptyCheck && resolved.Length < local.Length) //.. fuccbois
+ throw new InvalidOperationException("Attempted to access file outside of configuration manager!");
+ return resolved;
}
///
@@ -152,17 +157,30 @@ namespace Tgstation.Server.Host.Components.StaticFiles
void ListImpl()
{
- var enumerator = synchronousIOManager.GetDirectories(configurationRelativePath, cancellationToken);
- result.AddRange(enumerator.Select(x => new ConfigurationFile
+ var enumerator = synchronousIOManager.GetDirectories(path, cancellationToken);
+ try
{
- IsDirectory = true,
- Path = ioManager.ConcatPath(configurationRelativePath, x),
- }));
- enumerator = synchronousIOManager.GetFiles(configurationRelativePath, cancellationToken);
+ result.AddRange(enumerator.Select(x => new ConfigurationFile
+ {
+ IsDirectory = true,
+ Path = ioManager.ConcatPath(path, x),
+ }));
+ }
+ catch (UnauthorizedAccessException)
+ {
+ result = null;
+ return;
+ }
+ catch (DirectoryNotFoundException)
+ {
+ result = null;
+ return;
+ }
+ enumerator = synchronousIOManager.GetFiles(path, cancellationToken);
result.AddRange(enumerator.Select(x => new ConfigurationFile
{
IsDirectory = false,
- Path = ioManager.ConcatPath(configurationRelativePath, x),
+ Path = ioManager.ConcatPath(path, x),
}));
}
@@ -201,15 +219,31 @@ namespace Tgstation.Server.Host.Components.StaticFiles
Path = configurationRelativePath
};
}
- catch (FileNotFoundException) { }
- catch (DirectoryNotFoundException) { }
+ catch (IOException e)
+ {
+ logger.LogWarning("IOException while reading {0}: {1}", path, e);
+ }
catch (UnauthorizedAccessException)
{
+ //this happens on windows, dunno about linux
+ bool isDirectory;
+ try
+ {
+ isDirectory = synchronousIOManager.IsDirectory(path);
+ }
+ catch
+ {
+ isDirectory = false;
+ }
+
result = new ConfigurationFile
{
- AccessDenied = true,
Path = configurationRelativePath
};
+ if (!isDirectory)
+ result.AccessDenied = true;
+ else
+ result.IsDirectory = true;
}
}
@@ -266,13 +300,15 @@ namespace Tgstation.Server.Host.Components.StaticFiles
var success = synchronousIOManager.WriteFileChecked(path, data, previousHash, cancellationToken);
if (!success)
return;
+ string sha1String = null;
if (data != null)
+ {
postWriteHandler.HandleWrite(path);
- string sha1String;
#pragma warning disable CA5350 // Do not use insecure cryptographic algorithm SHA1.
- using (var sha1 = new SHA1Managed())
+ using (var sha1 = new SHA1Managed())
#pragma warning restore CA5350 // Do not use insecure cryptographic algorithm SHA1.
- sha1String = String.Join("", sha1.ComputeHash(data).Select(b => b.ToString("x2", CultureInfo.InvariantCulture)));
+ sha1String = String.Join("", sha1.ComputeHash(data).Select(b => b.ToString("x2", CultureInfo.InvariantCulture)));
+ }
result = new ConfigurationFile
{
Content = data,
@@ -282,15 +318,31 @@ namespace Tgstation.Server.Host.Components.StaticFiles
Path = configurationRelativePath
};
}
- catch (FileNotFoundException) { }
- catch (DirectoryNotFoundException) { }
+ catch (IOException e)
+ {
+ logger.LogWarning("IOException while writing {0}: {1}", path, e);
+ }
catch (UnauthorizedAccessException)
{
+ //this happens on windows, dunno about linux
+ bool isDirectory;
+ try
+ {
+ isDirectory = synchronousIOManager.IsDirectory(path);
+ }
+ catch
+ {
+ isDirectory = false;
+ }
+
result = new ConfigurationFile
{
- AccessDenied = true,
Path = configurationRelativePath
};
+ if (!isDirectory)
+ result.AccessDenied = true;
+ else
+ result.IsDirectory = true;
}
}
diff --git a/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs b/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs
index ceac94794d..faf1c595cc 100644
--- a/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs
+++ b/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs
@@ -69,20 +69,20 @@ namespace Tgstation.Server.Host.Controllers
///
/// Get the contents of a file at a
///
- /// The path of the file to get
+ /// The path of the file to get
/// The for the operation
/// A resulting in the for the operation
- [HttpGet("/File/{path}")]
+ [HttpGet("File/{*filePath}")]
[TgsAuthorize(ConfigurationRights.Read)]
- public async Task File(string path, CancellationToken cancellationToken)
+ public async Task File(string filePath, CancellationToken cancellationToken)
{
if (ForbidDueToModeConflicts())
return Forbid();
try
{
- var result = await instanceManager.GetInstance(Instance).Configuration.Read(path, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false);
- if (result == null || result.IsDirectory.Value)
+ var result = await instanceManager.GetInstance(Instance).Configuration.Read(filePath, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false);
+ if (result == null)
return StatusCode((int)HttpStatusCode.Gone);
return Json(result);
@@ -96,19 +96,19 @@ namespace Tgstation.Server.Host.Controllers
///
/// Get the contents of a directory at a
///
- /// The path of the directory to get
+ /// The path of the directory to get
/// The for the operation
/// A resulting in the for the operation
- [HttpGet("/List/{path}")]
+ [HttpGet("List/{*directoryPath}")]
[TgsAuthorize(ConfigurationRights.List)]
- public async Task Directory(string path, CancellationToken cancellationToken)
+ public async Task Directory(string directoryPath, CancellationToken cancellationToken)
{
if (ForbidDueToModeConflicts())
return Forbid();
try
{
- var result = await instanceManager.GetInstance(Instance).Configuration.ListDirectory(path, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false);
+ var result = await instanceManager.GetInstance(Instance).Configuration.ListDirectory(directoryPath, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false);
if (result == null)
return StatusCode((int)HttpStatusCode.Gone);
@@ -123,5 +123,9 @@ namespace Tgstation.Server.Host.Controllers
return Forbid();
}
}
+
+ ///
+ [TgsAuthorize(ConfigurationRights.List)]
+ public override Task List(CancellationToken cancellationToken) => Directory(null, cancellationToken);
}
}
diff --git a/src/Tgstation.Server.Host/IO/ISynchronousIOManager.cs b/src/Tgstation.Server.Host/IO/ISynchronousIOManager.cs
index b0d61e969b..617bac069f 100644
--- a/src/Tgstation.Server.Host/IO/ISynchronousIOManager.cs
+++ b/src/Tgstation.Server.Host/IO/ISynchronousIOManager.cs
@@ -40,5 +40,12 @@ namespace Tgstation.Server.Host.IO
/// The for the operation
/// on success, if the operation failed due to not matching the file's contents
bool WriteFileChecked(string path, byte[] data, string previousSha1, CancellationToken cancellationToken);
+
+ ///
+ /// Checks if a given is a directory
+ ///
+ /// The path to check
+ /// if is a directory, otherwise
+ bool IsDirectory(string path);
}
}
diff --git a/src/Tgstation.Server.Host/IO/SynchronousIOManager.cs b/src/Tgstation.Server.Host/IO/SynchronousIOManager.cs
index cc00821fdd..ec85b710d0 100644
--- a/src/Tgstation.Server.Host/IO/SynchronousIOManager.cs
+++ b/src/Tgstation.Server.Host/IO/SynchronousIOManager.cs
@@ -14,7 +14,6 @@ namespace Tgstation.Server.Host.IO
///
public IEnumerable GetDirectories(string path, CancellationToken cancellationToken)
{
- cancellationToken.ThrowIfCancellationRequested();
foreach (var I in Directory.EnumerateDirectories(path))
{
yield return I;
@@ -25,7 +24,6 @@ namespace Tgstation.Server.Host.IO
///
public IEnumerable GetFiles(string path, CancellationToken cancellationToken)
{
- cancellationToken.ThrowIfCancellationRequested();
foreach (var I in Directory.EnumerateFiles(path))
{
yield return I;
@@ -34,29 +32,54 @@ namespace Tgstation.Server.Host.IO
}
///
- public byte[] ReadFile(string path) => File.ReadAllBytes(path);
+ public bool IsDirectory(string path)
+ {
+ if (path == null)
+ throw new ArgumentNullException(nameof(path));
+ return Directory.Exists(path);
+ }
+
+ ///
+ public byte[] ReadFile(string path)
+ {
+ if (path == null)
+ throw new ArgumentNullException(nameof(path));
+ return File.ReadAllBytes(path);
+ }
///
public bool WriteFileChecked(string path, byte[] data, string previousSha1, CancellationToken cancellationToken)
{
+ if (path == null)
+ throw new ArgumentNullException(nameof(path));
+ cancellationToken.ThrowIfCancellationRequested();
+ Directory.CreateDirectory(Path.GetDirectoryName(path));
cancellationToken.ThrowIfCancellationRequested();
using (var file = File.Open(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))
{
+ cancellationToken.ThrowIfCancellationRequested();
+
+ //as nice as it would be to not have to arrayify the memory stream, we have to
+ //because, oddly enough sha1(memorystream) != sha1(memorystream.ToArray())
+ // vOv
+ byte[] originalBytes;
using (var readMs = new MemoryStream())
{
- cancellationToken.ThrowIfCancellationRequested();
file.CopyTo(readMs);
- if (readMs.Length != 0 && previousSha1 == null)
- return false; //no sha1? no write
- //suppressed due to only using for consistency checks
+ originalBytes = readMs.ToArray();
+ }
+ if (originalBytes.Length != 0 && previousSha1 == null)
+ //no sha1? no write
+ return false;
+
+ //suppressed due to only using for consistency checks
#pragma warning disable CA5350 // Do not use insecure cryptographic algorithm SHA1.
- using (var sha1 = new SHA1Managed())
+ using (var sha1 = new SHA1Managed())
#pragma warning restore CA5350 // Do not use insecure cryptographic algorithm SHA1.
- {
- var sha1String = String.Join("", sha1.ComputeHash(readMs).Select(b => b.ToString("x2", CultureInfo.InvariantCulture)));
- if (sha1String != previousSha1)
- return false;
- }
+ {
+ var sha1String = originalBytes.Length != 0 ? String.Join("", sha1.ComputeHash(originalBytes).Select(b => b.ToString("x2", CultureInfo.InvariantCulture))) : null;
+ if (sha1String != previousSha1)
+ return false;
}
cancellationToken.ThrowIfCancellationRequested();
@@ -66,8 +89,6 @@ namespace Tgstation.Server.Host.IO
cancellationToken.ThrowIfCancellationRequested();
file.SetLength(data.Length);
-
- cancellationToken.ThrowIfCancellationRequested();
file.Write(data, 0, data.Length);
}
}