diff --git a/LightlessSync/FileCache/FileCompactor.cs b/LightlessSync/FileCache/FileCompactor.cs
index b9c2118..4722b1f 100644
--- a/LightlessSync/FileCache/FileCompactor.cs
+++ b/LightlessSync/FileCache/FileCompactor.cs
@@ -4,7 +4,6 @@ using Microsoft.Extensions.Logging;
using Microsoft.Win32.SafeHandles;
using System.Collections.Concurrent;
using System.Diagnostics;
-using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Channels;
using static LightlessSync.Utils.FileSystemHelper;
@@ -140,42 +139,82 @@ public sealed class FileCompactor : IDisposable
if (fsType == FilesystemType.NTFS && !_dalamudUtilService.IsWine)
{
- var blockSize = GetBlockSizeForPath(fileInfo.FullName, _logger, _dalamudUtilService.IsWine);
- var losize = GetCompressedFileSizeW(fileInfo.FullName, out uint hosize);
- var size = (long)hosize << 32 | losize;
- return ((size + blockSize - 1) / blockSize) * blockSize;
+ (bool flowControl, long value) = GetFileSizeNTFS(fileInfo);
+ if (!flowControl)
+ {
+ return value;
+ }
}
if (fsType == FilesystemType.Btrfs)
{
- try
+ (bool flowControl, long value) = GetFileSizeBtrfs(fileInfo);
+ if (!flowControl)
{
- bool isWine = _dalamudUtilService?.IsWine ?? false;
- string realPath = isWine ? ToLinuxPathIfWine(fileInfo.FullName, isWine) : fileInfo.FullName;
-
- var fileName = "stat";
- var arguments = $"-c %b \"{realPath}\"";
-
- (bool processControl, bool success) = StartProcessInfo(realPath, fileName, arguments, out Process? proc, out string stdout);
-
- if (!processControl && !success)
- throw new InvalidOperationException($"stat failed: {proc}");
-
- if (!long.TryParse(stdout.Trim(), out var blocks))
- throw new InvalidOperationException($"invalid stat output: {stdout}");
-
- // st_blocks are always 512-byte on Linux enviroment.
- return blocks * 512L;
- }
- catch (Exception ex)
- {
- _logger.LogDebug(ex, "Failed stat size for {file}, fallback to Length", fileInfo.FullName);
+ return value;
}
}
return fileInfo.Length;
}
+ ///
+ /// Get File Size in an Btrfs file system (Linux/Wine).
+ ///
+ /// File that you want the size from.
+ /// Succesful check and value of the filesize.
+ /// Fails on the Process in StartProcessInfo
+ private (bool flowControl, long value) GetFileSizeBtrfs(FileInfo fileInfo)
+ {
+ try
+ {
+ bool isWine = _dalamudUtilService?.IsWine ?? false;
+ string realPath = isWine ? ToLinuxPathIfWine(fileInfo.FullName, isWine) : fileInfo.FullName;
+
+ var fileName = "stat";
+ var arguments = $"-c %b \"{realPath}\"";
+
+ (bool processControl, bool success) = StartProcessInfo(realPath, fileName, arguments, out Process? proc, out string stdout);
+
+ if (!processControl && !success)
+ throw new InvalidOperationException($"stat failed: {proc}");
+
+ if (!long.TryParse(stdout.Trim(), out var blocks))
+ throw new InvalidOperationException($"invalid stat output: {stdout}");
+
+ // st_blocks are always 512-byte on Linux enviroment.
+ return (flowControl: false, value: blocks * 512L);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogDebug(ex, "Failed stat size for {file}, fallback to Length", fileInfo.FullName);
+ }
+
+ return (flowControl: true, value: default);
+ }
+
+ ///
+ /// Get File Size in an NTFS file system (Windows).
+ ///
+ /// File that you want the size from.
+ /// Succesful check and value of the filesize.
+ private (bool flowControl, long value) GetFileSizeNTFS(FileInfo fileInfo)
+ {
+ try
+ {
+ var blockSize = GetBlockSizeForPath(fileInfo.FullName, _logger, _dalamudUtilService.IsWine);
+ var losize = GetCompressedFileSizeW(fileInfo.FullName, out uint hosize);
+ var size = (long)hosize << 32 | losize;
+ return (flowControl: false, value: ((size + blockSize - 1) / blockSize) * blockSize);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogDebug(ex, "Failed stat size for {file}, fallback to Length", fileInfo.FullName);
+ }
+
+ return (flowControl: true, value: default);
+ }
+
///
/// Compressing the given path with BTRFS or NTFS file system.
///
@@ -293,7 +332,7 @@ public sealed class FileCompactor : IDisposable
/// Decompress an BTRFS File
///
/// Path of the compressed file
- /// Decompessing state
+ /// Decompressing state
private bool DecompressBtrfsFile(string path)
{
var fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
@@ -353,7 +392,7 @@ public sealed class FileCompactor : IDisposable
/// Decompress an NTFS File
///
/// Path of the compressed file
- /// Decompessing state
+ /// Decompressing state
private bool DecompressWOFFile(string path, out FileStream fs)
{
fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
@@ -571,14 +610,6 @@ public sealed class FileCompactor : IDisposable
return false;
}
- //Skipping small files to make compression a bit faster, its not that effective on small files.
- int blockSize = GetBlockSizeForPath(realPath, _logger, isWine);
- if (fi.Length < Math.Max(blockSize * 2, 128 * 1024))
- {
- _logger.LogTrace("Skipping Btrfs compression for small file {file} ({size} bytes)", realPath, fi.Length);
- return true;
- }
-
if (IsBtrfsCompressedFile(realPath))
{
_logger.LogTrace("File {file} already compressed (Btrfs), skipping file", realPath);
@@ -695,21 +726,52 @@ public sealed class FileCompactor : IDisposable
private void EnqueueCompaction(string filePath)
{
+ // Safe-checks
+ if (string.IsNullOrWhiteSpace(filePath))
+ return;
+
+ if (!_lightlessConfigService.Current.UseCompactor)
+ return;
+
+ if (!File.Exists(filePath))
+ return;
+
if (!_pendingCompactions.TryAdd(filePath, 0))
return;
- var fsType = GetFilesystemType(filePath, _dalamudUtilService.IsWine);
- if (fsType != FilesystemType.NTFS && fsType != FilesystemType.Btrfs)
+ bool enqueued = false;
+ try
{
- _logger.LogTrace("Skip enqueue (unsupported fs) {fs} {file}", fsType, filePath);
- _pendingCompactions.TryRemove(filePath, out _);
- return;
- }
+ bool isWine = _dalamudUtilService?.IsWine ?? false;
+ var fsType = GetFilesystemType(filePath, isWine);
- if (!_compactionQueue.Writer.TryWrite(filePath))
+ // If under Wine, we should skip NTFS because its not Windows but might return NTFS.
+ if (fsType == FilesystemType.NTFS && isWine)
+ {
+ _logger.LogTrace("Skip enqueue (NTFS under Wine) {file}", filePath);
+ return;
+ }
+
+ // Unknown file system should be skipped.
+ if (fsType != FilesystemType.NTFS && fsType != FilesystemType.Btrfs)
+ {
+ _logger.LogTrace("Skip enqueue (unsupported fs) {fs} {file}", fsType, filePath);
+ return;
+ }
+
+ if (!_compactionQueue.Writer.TryWrite(filePath))
+ {
+ _logger.LogTrace("Skip enqueue: compaction channel is closed {file}", filePath);
+ return;
+ }
+
+ enqueued = true;
+ _logger.LogTrace("Queued compaction for {file} (fs={fs})", filePath, fsType);
+ }
+ finally
{
- _pendingCompactions.TryRemove(filePath, out _);
- _logger.LogDebug("Failed to enqueue compaction {file}", filePath);
+ if (!enqueued)
+ _pendingCompactions.TryRemove(filePath, out _);
}
}