11using Microsoft . Extensions . Logging ;
22using Moryx . FileSystem ;
3+ using Moryx . Logging ;
34using System ;
45using System . Collections . Generic ;
56using System . IO ;
7+ using System . Linq ;
68using System . Text ;
79using System . Threading . Tasks ;
810
@@ -11,6 +13,7 @@ namespace Moryx.Runtime.Kernel.FileSystem
1113 internal class MoryxFileSystem : IMoryxFileSystem
1214 {
1315 private string _fsDirectory ;
16+ private string _ownerFilesDirectory ;
1417 private readonly ILogger _logger ;
1518
1619 public MoryxFileSystem ( ILoggerFactory loggerFactory )
@@ -21,15 +24,10 @@ public MoryxFileSystem(ILoggerFactory loggerFactory)
2124 public void SetBasePath ( string basePath = "fs" )
2225 {
2326 _fsDirectory = Path . Combine ( Directory . GetCurrentDirectory ( ) , basePath ) ;
27+ _ownerFilesDirectory = Path . Combine ( _fsDirectory , "owners" ) ;
2428 }
2529
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 )
30+ public async Task < string > WriteBlob ( Stream stream )
3331 {
3432 var hashPath = HashPath . FromStream ( stream ) ;
3533
@@ -64,6 +62,139 @@ public async Task<string> WriteFile(Stream stream)
6462
6563 return hashPath . Hash ;
6664 }
65+ public Task < string > WriteTree ( IReadOnlyList < MoryxFileMetadata > metadata )
66+ {
67+ // Convert metadata to lines
68+ var lines = metadata . Select ( md => md . ToString ( ) ) . ToList ( ) ;
69+ var stream = new MemoryStream ( ) ;
70+ using ( var sw = new StreamWriter ( stream ) )
71+ {
72+ foreach ( var line in lines )
73+ sw . WriteLine ( line ) ;
74+ sw . Flush ( ) ;
75+ }
76+
77+ return WriteBlob ( stream ) ;
78+ }
79+
80+ public async Task < string > WriteBlob ( Stream fileStream , string ownerKey , MoryxFileMetadata metadata )
81+ {
82+ // Create file first
83+ var hash = await WriteBlob ( fileStream ) ;
84+ metadata . Hash = hash ;
85+
86+ // Read current owner tree
87+ var ownerFile = Path . Combine ( _ownerFilesDirectory , ownerKey ) ;
88+ var ownerTree = File . ReadAllText ( ownerFile ) ;
89+ var tree = ReadExtensibleTree ( ownerTree ) ;
90+
91+ // Add to owner tree and write new
92+ tree . Add ( metadata ) ;
93+ var treeHash = WriteTree ( tree ) ;
94+
95+ // Update owner file
96+ var ownerFilePath = Path . Combine ( _ownerFilesDirectory , ownerKey ) ;
97+ if ( File . Exists ( ownerFilePath ) )
98+ await File . WriteAllLinesAsync ( ownerFilePath , new [ ] { hash } ) ;
99+ else
100+ await File . AppendAllLinesAsync ( ownerFilePath , new [ ] { hash } ) ;
101+
102+ return hash ;
103+ }
104+
105+ public Stream ReadBlob ( string hash )
106+ {
107+ var path = HashPath . FromHash ( hash ) . FilePath ( _fsDirectory ) ;
108+ return File . Exists ( path ) ? new FileStream ( path , FileMode . Open , FileAccess . Read ) : null ;
109+ }
110+
111+ public IReadOnlyList < MoryxFileMetadata > ReadTree ( string hash ) => ReadExtensibleTree ( hash ) ;
112+
113+
114+ public IReadOnlyList < MoryxFileMetadata > ReadTreeByOwner ( string ownerKey )
115+ {
116+ // read hash from owner file
117+ var ownerFile = Path . Combine ( _ownerFilesDirectory , ownerKey ) ;
118+ var ownerTree = File . ReadAllText ( ownerFile ) ;
119+
120+ return ReadExtensibleTree ( ownerTree ) ;
121+ }
122+
123+ private List < MoryxFileMetadata > ReadExtensibleTree ( string hash )
124+ {
125+ // Read tree from hash
126+ var stream = ReadBlob ( hash ) ;
127+ var metadata = new List < MoryxFileMetadata > ( ) ;
128+ using ( var sr = new StreamReader ( stream ) )
129+ {
130+ var line = sr . ReadLine ( ) ;
131+ metadata . Add ( MoryxFileMetadata . FromLine ( line ) ) ;
132+ }
133+
134+ return metadata ;
135+ }
136+
137+ public bool RemoveFile ( string hash , string ownerKey )
138+ {
139+ if ( ! IsOwner ( hash , ownerKey ) )
140+ return false ;
141+
142+ // Delete file if found
143+ var hashPath = HashPath . FromHash ( hash ) ;
144+ var filePath = hashPath . FilePath ( _fsDirectory ) ;
145+ if ( ! File . Exists ( filePath ) )
146+ return false ;
147+ RemoveFile ( filePath , _logger ) ;
148+
149+ // Check if subdirectory is empty and remove
150+ var directory = hashPath . DirectoryPath ( _fsDirectory ) ;
151+ CleanUpDirectory ( directory , _logger ) ;
152+
153+ // TODO: Remove file from owner list
154+
155+ return true ;
156+ }
157+
158+ private bool IsOwner ( string hash , string ownerFile )
159+ {
160+ var ownerFilePath = Path . Combine ( _ownerFilesDirectory , ownerFile ) ;
161+ using ( var reader = new StreamReader ( ownerFilePath ) )
162+ {
163+ string line ;
164+ while ( ( line = reader . ReadLine ( ) ) != null )
165+ {
166+ if ( line . Contains ( searchLine ) )
167+ return true ;
168+ }
169+ }
170+ return false ;
171+ }
172+
173+ private void RemoveFile ( string filePath , ILogger logger )
174+ {
175+ try
176+ {
177+ File . Delete ( filePath ) ;
178+ }
179+ catch ( Exception e )
180+ {
181+ throw LoggedException ( e , logger , filePath ) ;
182+ }
183+ }
184+
185+ private void CleanUpDirectory ( string directoryPath , ILogger logger )
186+
187+ {
188+ try
189+ {
190+ if ( Directory . GetFiles ( directoryPath ) . Length == 0 )
191+ Directory . Delete ( directoryPath ) ;
192+ }
193+ catch ( Exception e )
194+ {
195+ throw LoggedException ( e , logger , directoryPath ) ;
196+ }
197+ }
67198
68199 private Exception LoggedException ( Exception e , ILogger logger , string cause )
69200 {
0 commit comments