2025-02-27 09:15:28 +01:00
using Logging ;
2025-02-26 16:17:20 +01:00
namespace AutoClient.Modes.FolderStore
{
public class FolderSaver
{
private const string FolderSaverFilename = "foldersaver.json" ;
private readonly App app ;
private readonly CodexWrapper instance ;
private readonly JsonFile < FolderStatus > statusFile ;
private readonly FolderStatus status ;
private int failureCount = 0 ;
public FolderSaver ( App app , CodexWrapper instance )
{
this . app = app ;
this . instance = instance ;
statusFile = new JsonFile < FolderStatus > ( app , Path . Combine ( app . Config . FolderToStore , FolderSaverFilename ) ) ;
status = statusFile . Load ( ) ;
}
public void Run ( CancellationTokenSource cts )
{
var folderFiles = Directory . GetFiles ( app . Config . FolderToStore ) ;
if ( ! folderFiles . Any ( ) ) throw new Exception ( "No files found in " + app . Config . FolderToStore ) ;
var counter = 0 ;
foreach ( var folderFile in folderFiles )
{
if ( cts . IsCancellationRequested ) return ;
if ( ! folderFile . ToLowerInvariant ( ) . EndsWith ( FolderSaverFilename ) )
{
2025-02-27 09:22:57 +01:00
if ( SaveFile ( folderFile ) )
{
counter + + ;
}
2025-02-26 16:17:20 +01:00
}
if ( failureCount > 9 )
{
app . Log . Error ( "Failure count reached threshold. Stopping..." ) ;
cts . Cancel ( ) ;
return ;
}
if ( counter > 5 )
{
counter = 0 ;
SaveFolderSaverJsonFile ( ) ;
}
2025-02-27 09:15:28 +01:00
statusFile . Save ( status ) ;
2025-02-27 10:18:35 +01:00
Thread . Sleep ( 100 ) ;
2025-02-26 16:17:20 +01:00
}
}
2025-02-27 09:22:57 +01:00
private bool SaveFile ( string folderFile )
2025-02-26 16:17:20 +01:00
{
var localFilename = Path . GetFileName ( folderFile ) ;
var entry = status . Files . SingleOrDefault ( f = > f . Filename = = localFilename ) ;
if ( entry = = null )
{
2025-02-26 16:24:11 +01:00
entry = new FileStatus
{
Filename = localFilename
} ;
2025-02-26 16:17:20 +01:00
status . Files . Add ( entry ) ;
}
2025-02-27 09:22:57 +01:00
return ProcessFileEntry ( folderFile , entry ) ;
2025-02-26 16:17:20 +01:00
}
2025-02-27 09:22:57 +01:00
private bool ProcessFileEntry ( string folderFile , FileStatus entry )
2025-02-26 16:17:20 +01:00
{
var fileSaver = CreateFileSaver ( folderFile , entry ) ;
fileSaver . Process ( ) ;
if ( fileSaver . HasFailed ) failureCount + + ;
2025-02-27 09:22:57 +01:00
return fileSaver . Changes ;
2025-02-26 16:17:20 +01:00
}
private void SaveFolderSaverJsonFile ( )
{
var entry = new FileStatus
{
Filename = FolderSaverFilename
} ;
var folderFile = Path . Combine ( app . Config . FolderToStore , FolderSaverFilename ) ;
2025-02-27 09:15:28 +01:00
ApplyPadding ( folderFile ) ;
2025-02-26 16:17:20 +01:00
var fileSaver = CreateFileSaver ( folderFile , entry ) ;
fileSaver . Process ( ) ;
if ( fileSaver . HasFailed ) failureCount + + ;
}
2025-02-27 09:15:28 +01:00
private const int MinCodexStorageFilesize = 262144 ;
private readonly Random random = new Random ( ) ;
private readonly string paddingMessage = $"Codex currently requires a minimum filesize of {MinCodexStorageFilesize} bytes for datasets used in storage contracts. " +
$"Anything smaller, and the erasure-coding algorithms used for data durability won't function. Therefore, we apply this padding field to make sure this " +
$"file is larger than the minimal size. The following is pseudo-random: " ;
private void ApplyPadding ( string folderFile )
{
var info = new FileInfo ( folderFile ) ;
var min = MinCodexStorageFilesize * 2 ;
if ( info . Length < min )
{
2025-02-27 09:22:57 +01:00
var required = Math . Max ( 1024 , min - info . Length ) ;
2025-02-27 09:15:28 +01:00
status . Padding = paddingMessage + GenerateRandomString ( required ) ;
2025-02-27 10:09:03 +01:00
statusFile . Save ( status ) ;
2025-02-27 09:15:28 +01:00
}
}
private string GenerateRandomString ( long required )
{
var result = "" ;
while ( result . Length < required )
{
2025-02-27 09:22:57 +01:00
var bytes = new byte [ 1024 ] ;
2025-02-27 09:15:28 +01:00
random . NextBytes ( bytes ) ;
result + = string . Join ( "" , bytes . Select ( b = > b . ToString ( ) ) ) ;
}
return result ;
}
2025-02-26 16:17:20 +01:00
private FileSaver CreateFileSaver ( string folderFile , FileStatus entry )
{
var fixedLength = entry . Filename . PadRight ( 35 ) ;
var prefix = $"[{fixedLength}] " ;
2025-02-27 09:15:28 +01:00
return new FileSaver ( new LogPrefixer ( app . Log , prefix ) , instance , status . Stats , folderFile , entry ) ;
2025-02-26 16:17:20 +01:00
}
}
}