Skip to content

Commit 83c5cf1

Browse files
Thomas Fuchsseveneleven
authored andcommitted
Add MoryxFileSystem to kernel
1 parent 6504503 commit 83c5cf1

File tree

5 files changed

+200
-1
lines changed

5 files changed

+200
-1
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
using Microsoft.Extensions.Logging;
2+
using Moryx.Logging;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.IO;
6+
using System.Security.Cryptography;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
10+
namespace Moryx.Runtime.Kernel.FileSystem
11+
{
12+
internal class HashPath
13+
{
14+
public string Hash { get; private set; }
15+
16+
public string DirectoryName { get; private set; }
17+
18+
public string FileName { get; private set; }
19+
20+
private HashPath()
21+
{
22+
}
23+
24+
public static HashPath FromStream(Stream stream) =>
25+
BuildPath(HashFromStream(stream));
26+
27+
public static HashPath FromHash(string hash) =>
28+
BuildPath(hash);
29+
30+
public string FilePath(string storagePath) =>
31+
Path.Combine(storagePath, DirectoryName, FileName);
32+
33+
public string DirectoryPath(string storagePath) =>
34+
Path.Combine(storagePath, DirectoryName);
35+
36+
private static HashPath BuildPath(string hash)
37+
{
38+
return new HashPath
39+
{
40+
Hash = hash,
41+
DirectoryName = hash.Substring(0, 2),
42+
FileName = hash.Substring(2)
43+
};
44+
}
45+
46+
private static string HashFromStream(Stream stream)
47+
{
48+
string name;
49+
using (var hashing = SHA256.Create())
50+
{
51+
stream.Position = 0;
52+
53+
var hash = hashing.ComputeHash(stream);
54+
var nameBuilder = new StringBuilder(hash.Length * 2);
55+
foreach (var hashByte in hash)
56+
{
57+
nameBuilder.AppendFormat("{0:X2}", hashByte);
58+
}
59+
name = nameBuilder.ToString();
60+
61+
stream.Position = 0;
62+
}
63+
64+
return name;
65+
}
66+
}
67+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
using Microsoft.Extensions.Logging;
2+
using Moryx.FileSystem;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.IO;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
9+
namespace Moryx.Runtime.Kernel.FileSystem
10+
{
11+
internal class MoryxFileSystem : IMoryxFileSystem
12+
{
13+
private string _fsDirectory;
14+
private readonly ILogger _logger;
15+
16+
public MoryxFileSystem(ILoggerFactory loggerFactory)
17+
{
18+
_logger = loggerFactory.CreateLogger(nameof(MoryxFileSystem));
19+
}
20+
21+
public void SetBasePath(string basePath = "fs")
22+
{
23+
_fsDirectory = Path.Combine(Directory.GetCurrentDirectory(), basePath);
24+
}
25+
26+
public Stream ReadFile(string hash)
27+
{
28+
var path = HashPath.FromHash(hash).FilePath(_fsDirectory);
29+
return File.Exists(path) ? new FileStream(path, FileMode.Open, FileAccess.Read) : null;
30+
}
31+
32+
public async Task<string> WriteFile(Stream stream)
33+
{
34+
var hashPath = HashPath.FromStream(stream);
35+
36+
// Create directory if necessary
37+
var targetPath = hashPath.DirectoryPath(_fsDirectory);
38+
try
39+
{
40+
if (!Directory.Exists(targetPath))
41+
Directory.CreateDirectory(targetPath);
42+
}
43+
catch (Exception e)
44+
{
45+
throw LoggedException(e, _logger, _fsDirectory);
46+
}
47+
48+
var fileName = hashPath.FilePath(_fsDirectory);
49+
if (File.Exists(fileName))
50+
return hashPath.Hash;
51+
52+
// Write file
53+
try
54+
{
55+
using var fileStream = new FileStream(fileName, FileMode.Create);
56+
await stream.CopyToAsync(fileStream);
57+
fileStream.Flush();
58+
stream.Position = 0;
59+
}
60+
catch (Exception e)
61+
{
62+
throw LoggedException(e, _logger, fileName);
63+
}
64+
65+
return hashPath.Hash;
66+
}
67+
68+
private Exception LoggedException(Exception e, ILogger logger, string cause)
69+
{
70+
switch (e)
71+
{
72+
case UnauthorizedAccessException unauthorizedAccessException:
73+
logger.LogError("Error: {0}. You do not have the required permission to manipulate the file {1}.", e.Message, cause); // ToDo
74+
return unauthorizedAccessException;
75+
case ArgumentException argumentException:
76+
logger.LogError("Error: {0}. The path {1} contains invalid characters such as \", <, >, or |.", e.Message, cause);
77+
return argumentException;
78+
case IOException iOException:
79+
logger.LogError("Error: {0}. An I/O error occurred while opening the file {1}.", e.Message, cause);
80+
return iOException;
81+
default:
82+
logger.LogError("Unspecified error on file system access: {0}", e.Message);
83+
return e;
84+
}
85+
}
86+
}
87+
}

src/Moryx.Runtime.Kernel/KernelServiceCollectionExtensions.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
using Microsoft.Extensions.DependencyInjection;
55
using Moryx.Configuration;
66
using Moryx.Container;
7+
using Moryx.FileSystem;
8+
using Moryx.Runtime.Kernel.FileSystem;
79
using Moryx.Runtime.Modules;
810
using Moryx.Threading;
911
using System;
@@ -30,6 +32,10 @@ public static void AddMoryxKernel(this IServiceCollection serviceCollection)
3032
serviceCollection.AddSingleton<ModuleManager>();
3133
serviceCollection.AddSingleton<IModuleManager>(x => x.GetRequiredService<ModuleManager>());
3234

35+
// Register module manager
36+
serviceCollection.AddSingleton<MoryxFileSystem>();
37+
serviceCollection.AddSingleton<IMoryxFileSystem>(x => x.GetRequiredService<MoryxFileSystem>());
38+
3339
// Register parallel operations
3440
serviceCollection.AddTransient<IParallelOperations, ParallelOperations>();
3541

@@ -87,6 +93,19 @@ public static IConfigManager UseMoryxConfigurations(this IServiceProvider servic
8793
return configManager;
8894
}
8995

96+
/// <summary>
97+
/// Use moryx file system and configure base directory
98+
/// </summary>
99+
/// <returns></returns>
100+
public static IMoryxFileSystem UseMoryxFileSystem(this IServiceProvider serviceProvider, string path)
101+
{
102+
var fileSystem = serviceProvider.GetRequiredService<MoryxFileSystem>();
103+
if (!Directory.Exists(path))
104+
Directory.CreateDirectory(path);
105+
fileSystem.SetBasePath(path);
106+
return fileSystem;
107+
}
108+
90109
private static IModuleManager _moduleManager;
91110
/// <summary>
92111
/// Boot system and start all modules
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
7+
namespace Moryx.FileSystem
8+
{
9+
/// <summary>
10+
/// Common file system interface across MORYX modules and components.
11+
/// </summary>
12+
public interface IMoryxFileSystem
13+
{
14+
/// <summary>
15+
/// Write a file to the file system and receive the hash to access it later
16+
/// </summary>
17+
Task<string> WriteFile(Stream fileStream);
18+
19+
/// <summary>
20+
/// Read the file by passing the file system hash
21+
/// </summary>
22+
/// <param name="hash"></param>
23+
/// <returns></returns>
24+
Stream ReadFile(string hash);
25+
}
26+
}

src/Moryx/Moryx.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
<ItemGroup>
1717
<PackageReference Include="Newtonsoft.Json" />
18-
<PackageReference Include="Microsoft.Extensions.Logging"/>
18+
<PackageReference Include="Microsoft.Extensions.Logging" />
1919
</ItemGroup>
2020

2121
<ItemGroup Condition="'$(TargetFramework)'=='netstandard2.0'">

0 commit comments

Comments
 (0)