merge 2.0.0 into migration

This commit is contained in:
cake
2025-11-29 17:27:24 +01:00
60 changed files with 2604 additions and 1939 deletions

View File

@@ -471,8 +471,55 @@ public sealed class CacheMonitor : DisposableMediatorSubscriberBase
FileCacheSize = total;
var maxCacheBytes = (long)(_configService.Current.MaxLocalCacheInGiB * 1024d * 1024d * 1024d);
if (FileCacheSize < maxCacheBytes) return;
if (Directory.Exists(_configService.Current.CacheFolder + "/downscaled"))
{
var filesDownscaled = Directory.EnumerateFiles(_configService.Current.CacheFolder + "/downscaled").Select(f => new FileInfo(f)).OrderBy(f => f.LastAccessTime).ToList();
long totalSizeDownscaled = 0;
foreach (var f in filesDownscaled)
{
token.ThrowIfCancellationRequested();
try
{
long size = 0;
if (!isWine)
{
try
{
size = _fileCompactor.GetFileSizeOnDisk(f);
}
catch (Exception ex)
{
Logger.LogTrace(ex, "GetFileSizeOnDisk failed for {file}, using fallback length", f.FullName);
size = f.Length;
}
}
else
{
size = f.Length;
}
totalSizeDownscaled += size;
}
catch (Exception ex)
{
Logger.LogTrace(ex, "Error getting size for {file}", f.FullName);
}
}
FileCacheSize = (totalSize + totalSizeDownscaled);
}
else
{
FileCacheSize = totalSize;
}
var maxCacheInBytes = (long)(_configService.Current.MaxLocalCacheInGiB * 1024d * 1024d * 1024d);
if (FileCacheSize < maxCacheInBytes)
return;
var buffer = (long)(maxCacheBytes * 0.05d);
var target = maxCacheBytes - buffer;

View File

@@ -4,6 +4,7 @@ using LightlessSync.Services.Compactor;
using Microsoft.Extensions.Logging;
using Microsoft.Win32.SafeHandles;
using System.Collections.Concurrent;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading.Channels;
@@ -16,6 +17,7 @@ public sealed partial class FileCompactor : IDisposable
public const uint FSCTL_DELETE_EXTERNAL_BACKING = 0x90314U;
public const ulong WOF_PROVIDER_FILE = 2UL;
public const int _maxRetries = 3;
private readonly bool _isWindows;
private readonly ConcurrentDictionary<string, byte> _pendingCompactions;
private readonly ILogger<FileCompactor> _logger;
@@ -263,24 +265,12 @@ public sealed partial class FileCompactor : IDisposable
{
try
{
bool isWine = _dalamudUtilService?.IsWine ?? false;
var (_, linuxPath) = ResolvePathsForBtrfs(fileInfo.FullName);
string linuxPath = isWine ? ToLinuxPathIfWine(fileInfo.FullName, isWine)
: fileInfo.FullName;
(bool ok, string so, string se, int code) res;
res = isWine
? RunProcessShell($"stat -c %b -- {QuoteSingle(linuxPath)}", timeoutMs: 10000)
: RunProcessDirect("stat", ["-c", "%b", "--", linuxPath], "/", 10000);
var outTrim = res.so?.Trim() ?? "";
if (res.ok && long.TryParse(outTrim, out long blocks) && blocks >= 0)
{
// st_blocks are 512-byte units
return (flowControl: false, value: blocks * 512L);
}
var (ok, output, err, code) =
_isWindows
? RunProcessShell($"stat -c='%b' {QuoteSingle(linuxPath)}", workingDir: null, 10000)
: RunProcessDirect("stat", ["-c='%b'", linuxPath], workingDir: null, 10000);
if (_logger.IsEnabled(LogLevel.Debug))
_logger.LogDebug("Btrfs size probe failed for {linux} (exit {code}). stdout='{so}' stderr='{se}'. Falling back to Length.", linuxPath, res.code, outTrim, (res.se ?? "").Trim());
@@ -305,17 +295,28 @@ public sealed partial class FileCompactor : IDisposable
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);
if (blockSize <= 0)
throw new InvalidOperationException($"Invalid block size {blockSize} for {fileInfo.FullName}");
uint lo = GetCompressedFileSizeW(fileInfo.FullName, out uint hi);
if (lo == 0xFFFFFFFF)
{
int err = Marshal.GetLastWin32Error();
if (err != 0)
throw new Win32Exception(err);
}
long size = ((long)hi << 32) | lo;
long rounded = ((size + blockSize - 1) / blockSize) * blockSize;
return (flowControl: false, value: rounded);
}
catch (Exception ex)
{
if (_logger.IsEnabled(LogLevel.Debug))
_logger.LogDebug(ex, "Failed stat size for {file}, fallback to Length", fileInfo.FullName);
_logger.LogDebug(ex, "Failed stat size for {file}, fallback to Length", fileInfo.FullName);
return (flowControl: true, value: default);
}
return (flowControl: true, value: default);
}
/// <summary>
@@ -1149,18 +1150,16 @@ public sealed partial class FileCompactor : IDisposable
{
try
{
var pathToOpen = _isWindows ? winePath : linuxPath;
if (string.IsNullOrEmpty(pathToOpen) || !File.Exists(pathToOpen))
return false;
using var _ = new FileStream(pathToOpen, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
if (_isWindows)
{
using var _ = new FileStream(winePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
}
else
{
using var _ = new FileStream(linuxPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
}
return true;
}
catch
{
return false;
}
}
/// <summary>