Added safe checks on enqueue.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get File Size in an Btrfs file system (Linux/Wine).
|
||||
/// </summary>
|
||||
/// <param name="fileInfo">File that you want the size from.</param>
|
||||
/// <returns>Succesful check and value of the filesize.</returns>
|
||||
/// <exception cref="InvalidOperationException">Fails on the Process in StartProcessInfo</exception>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get File Size in an NTFS file system (Windows).
|
||||
/// </summary>
|
||||
/// <param name="fileInfo">File that you want the size from.</param>
|
||||
/// <returns>Succesful check and value of the filesize.</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compressing the given path with BTRFS or NTFS file system.
|
||||
/// </summary>
|
||||
@@ -293,7 +332,7 @@ public sealed class FileCompactor : IDisposable
|
||||
/// Decompress an BTRFS File
|
||||
/// </summary>
|
||||
/// <param name="path">Path of the compressed file</param>
|
||||
/// <returns>Decompessing state</returns>
|
||||
/// <returns>Decompressing state</returns>
|
||||
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
|
||||
/// </summary>
|
||||
/// <param name="path">Path of the compressed file</param>
|
||||
/// <returns>Decompessing state</returns>
|
||||
/// <returns>Decompressing state</returns>
|
||||
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 _);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user