refactored names; fixed return values for hard links service
This commit is contained in:
@@ -24,9 +24,9 @@ public static class ServicesDI
|
|||||||
.AddTransient<ContentBlocker>()
|
.AddTransient<ContentBlocker>()
|
||||||
.AddTransient<DownloadCleaner>()
|
.AddTransient<DownloadCleaner>()
|
||||||
.AddTransient<IFilenameEvaluator, FilenameEvaluator>()
|
.AddTransient<IFilenameEvaluator, FilenameEvaluator>()
|
||||||
.AddTransient<IHardlinkFileService, HardlinkFileService>()
|
.AddTransient<IHardLinkFileService, HardLinkFileService>()
|
||||||
.AddTransient<UnixHardlinkFileService>()
|
.AddTransient<UnixHardLinkFileService>()
|
||||||
.AddTransient<WindowsHardlinkFileService>()
|
.AddTransient<WindowsHardLinkFileService>()
|
||||||
.AddTransient<DummyDownloadService>()
|
.AddTransient<DummyDownloadService>()
|
||||||
.AddTransient<QBitService>()
|
.AddTransient<QBitService>()
|
||||||
.AddTransient<DelugeService>()
|
.AddTransient<DelugeService>()
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public class TestDownloadService : DownloadService
|
|||||||
public override Task<List<object>?> GetDownloadsToBeCleaned(List<CleanCategory> categories) => Task.FromResult<List<object>?>(null);
|
public override Task<List<object>?> GetDownloadsToBeCleaned(List<CleanCategory> categories) => Task.FromResult<List<object>?>(null);
|
||||||
public override Task<List<object>?> GetDownloadsToChangeCategory(List<string> categories) => Task.FromResult<List<object>?>(null);
|
public override Task<List<object>?> GetDownloadsToChangeCategory(List<string> categories) => Task.FromResult<List<object>?>(null);
|
||||||
public override Task CleanDownloads(List<object> downloads, List<CleanCategory> categoriesToClean, HashSet<string> excludedHashes) => Task.CompletedTask;
|
public override Task CleanDownloads(List<object> downloads, List<CleanCategory> categoriesToClean, HashSet<string> excludedHashes) => Task.CompletedTask;
|
||||||
public override Task ChangeCategoryForNoHardlinksAsync(List<object> downloads, HashSet<string> excludedHashes) => Task.CompletedTask;
|
public override Task ChangeCategoryForNoHardLinksAsync(List<object> downloads, HashSet<string> excludedHashes) => Task.CompletedTask;
|
||||||
|
|
||||||
// Expose protected methods for testing
|
// Expose protected methods for testing
|
||||||
public new void ResetStrikesOnProgress(string hash, long downloaded) => base.ResetStrikesOnProgress(hash, downloaded);
|
public new void ResetStrikesOnProgress(string hash, long downloaded) => base.ResetStrikesOnProgress(hash, downloaded);
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ public sealed class DownloadCleaner : GenericHandler
|
|||||||
|
|
||||||
if (hasDownloadsToChange)
|
if (hasDownloadsToChange)
|
||||||
{
|
{
|
||||||
await _downloadService.ChangeCategoryForNoHardlinksAsync(downloadsToChangeCategory, _excludedHashes);
|
await _downloadService.ChangeCategoryForNoHardLinksAsync(downloadsToChangeCategory, _excludedHashes);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ public class DelugeService : DownloadService, IDelugeService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task ChangeCategoryForNoHardlinksAsync(List<object> downloads, HashSet<string> excludedHashes)
|
public override Task ChangeCategoryForNoHardLinksAsync(List<object> downloads, HashSet<string> excludedHashes)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ public abstract class DownloadService : IDownloadService
|
|||||||
public abstract Task CleanDownloads(List<object> downloads, List<CleanCategory> categoriesToClean, HashSet<string> excludedHashes);
|
public abstract Task CleanDownloads(List<object> downloads, List<CleanCategory> categoriesToClean, HashSet<string> excludedHashes);
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public abstract Task ChangeCategoryForNoHardlinksAsync(List<object> downloads, HashSet<string> excludedHashes);
|
public abstract Task ChangeCategoryForNoHardLinksAsync(List<object> downloads, HashSet<string> excludedHashes);
|
||||||
|
|
||||||
protected void ResetStrikesOnProgress(string hash, long downloaded)
|
protected void ResetStrikesOnProgress(string hash, long downloaded)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ public class DummyDownloadService : DownloadService
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task ChangeCategoryForNoHardlinksAsync(List<object> downloads, HashSet<string> excludedHashes)
|
public override Task ChangeCategoryForNoHardLinksAsync(List<object> downloads, HashSet<string> excludedHashes)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ public interface IDownloadService : IDisposable
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="downloads">The downloads to change.</param>
|
/// <param name="downloads">The downloads to change.</param>
|
||||||
/// <param name="excludedHashes"></param>
|
/// <param name="excludedHashes"></param>
|
||||||
Task ChangeCategoryForNoHardlinksAsync(List<object> downloads, HashSet<string> excludedHashes);
|
Task ChangeCategoryForNoHardLinksAsync(List<object> downloads, HashSet<string> excludedHashes);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deletes a download item.
|
/// Deletes a download item.
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using Common.Configuration.ContentBlocker;
|
|||||||
using Common.Configuration.DownloadCleaner;
|
using Common.Configuration.DownloadCleaner;
|
||||||
using Common.Configuration.DownloadClient;
|
using Common.Configuration.DownloadClient;
|
||||||
using Common.Configuration.QueueCleaner;
|
using Common.Configuration.QueueCleaner;
|
||||||
|
using Common.Exceptions;
|
||||||
using Common.Helpers;
|
using Common.Helpers;
|
||||||
using Domain.Enums;
|
using Domain.Enums;
|
||||||
using Infrastructure.Interceptors;
|
using Infrastructure.Interceptors;
|
||||||
@@ -298,20 +299,26 @@ public class QBitService : DownloadService, IQBitService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async Task ChangeCategoryForNoHardlinksAsync(List<object> downloads, HashSet<string> excludedHashes)
|
public override async Task ChangeCategoryForNoHardLinksAsync(List<object> downloads, HashSet<string> excludedHashes)
|
||||||
{
|
{
|
||||||
if (_downloadCleanerConfig.IgnoreRootDir)
|
if (_downloadCleanerConfig.IgnoreRootDir)
|
||||||
{
|
{
|
||||||
// TODO call this only if Unix
|
|
||||||
downloads
|
downloads
|
||||||
.Cast<TorrentInfo>()
|
.Cast<TorrentInfo>()
|
||||||
.GroupBy(x => x.SavePath)
|
.GroupBy(x => Path.GetPathRoot(x.SavePath))
|
||||||
.Select(x => x.Key)
|
.Select(x => x.Key)
|
||||||
.ToList()
|
.ToList()
|
||||||
.ForEach(x => _hardlinkFileService.PopulateInodeCounts(x));
|
.ForEach(x =>
|
||||||
|
{
|
||||||
|
if (!Directory.Exists(x))
|
||||||
|
{
|
||||||
|
throw new ValidationException($"directory \"{x}\" does not exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
_hardLinkFileService.PopulateFileCounts(x);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO account for cross-seed
|
|
||||||
foreach (TorrentInfo download in downloads)
|
foreach (TorrentInfo download in downloads)
|
||||||
{
|
{
|
||||||
IReadOnlyList<TorrentContent>? files = await _client.GetTorrentContentsAsync(download.Hash);
|
IReadOnlyList<TorrentContent>? files = await _client.GetTorrentContentsAsync(download.Hash);
|
||||||
@@ -343,7 +350,7 @@ public class QBitService : DownloadService, IQBitService
|
|||||||
: download.SavePath, file.Name
|
: download.SavePath, file.Name
|
||||||
);
|
);
|
||||||
|
|
||||||
long hardlinkCount = _hardlinkFileService.GetHardLinkCount(filePath, _downloadCleanerConfig.IgnoreRootDir);
|
long hardlinkCount = _hardLinkFileService.GetHardLinkCount(filePath, _downloadCleanerConfig.IgnoreRootDir);
|
||||||
|
|
||||||
if (hardlinkCount < 0)
|
if (hardlinkCount < 0)
|
||||||
{
|
{
|
||||||
@@ -364,7 +371,7 @@ public class QBitService : DownloadService, IQBitService
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation("no hardlinks found | changing category for {name}", download.Name);
|
_logger.LogInformation("changing category for {name}", download.Name);
|
||||||
|
|
||||||
await ((QBitService)Proxy).ChangeCategory(download.Hash, _downloadCleanerConfig.NoHardlinksCategory);
|
await ((QBitService)Proxy).ChangeCategory(download.Hash, _downloadCleanerConfig.NoHardlinksCategory);
|
||||||
await _notifier.NotifyCategoryChanged(download.Category, _downloadCleanerConfig.NoHardlinksCategory);
|
await _notifier.NotifyCategoryChanged(download.Category, _downloadCleanerConfig.NoHardlinksCategory);
|
||||||
|
|||||||
@@ -289,7 +289,7 @@ public class TransmissionService : DownloadService, ITransmissionService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Task ChangeCategoryForNoHardlinksAsync(List<object> downloads, HashSet<string> excludedHashes)
|
public override Task ChangeCategoryForNoHardLinksAsync(List<object> downloads, HashSet<string> excludedHashes)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Infrastructure.Verticals.Files;
|
||||||
|
|
||||||
|
public class HardLinkFileService : IHardLinkFileService
|
||||||
|
{
|
||||||
|
private readonly ILogger<HardLinkFileService> _logger;
|
||||||
|
private readonly UnixHardLinkFileService _unixHardLinkFileService;
|
||||||
|
private readonly WindowsHardLinkFileService _windowsHardLinkFileService;
|
||||||
|
|
||||||
|
public HardLinkFileService(
|
||||||
|
ILogger<HardLinkFileService> logger,
|
||||||
|
UnixHardLinkFileService unixHardLinkFileService,
|
||||||
|
WindowsHardLinkFileService windowsHardLinkFileService
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_unixHardLinkFileService = unixHardLinkFileService;
|
||||||
|
_windowsHardLinkFileService = windowsHardLinkFileService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PopulateFileCounts(string directoryPath)
|
||||||
|
{
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
_windowsHardLinkFileService.PopulateFileCounts(directoryPath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_unixHardLinkFileService.PopulateFileCounts(directoryPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long GetHardLinkCount(string filePath, bool ignoreRootDir)
|
||||||
|
{
|
||||||
|
if (!File.Exists(filePath))
|
||||||
|
{
|
||||||
|
_logger.LogDebug("file {file} does not exist", filePath);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
{
|
||||||
|
return _windowsHardLinkFileService.GetHardLinkCount(filePath, ignoreRootDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _unixHardLinkFileService.GetHardLinkCount(filePath, ignoreRootDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
using System.Runtime.InteropServices;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace Infrastructure.Verticals.Files;
|
|
||||||
|
|
||||||
public class HardlinkFileService : IHardlinkFileService
|
|
||||||
{
|
|
||||||
private readonly ILogger<HardlinkFileService> _logger;
|
|
||||||
private readonly UnixHardlinkFileService _unixHardlinkFileService;
|
|
||||||
private readonly WindowsHardlinkFileService _windowsHardlinkFileService;
|
|
||||||
|
|
||||||
public HardlinkFileService(
|
|
||||||
ILogger<HardlinkFileService> logger,
|
|
||||||
UnixHardlinkFileService unixHardlinkFileService,
|
|
||||||
WindowsHardlinkFileService windowsHardlinkFileService
|
|
||||||
)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
_unixHardlinkFileService = unixHardlinkFileService;
|
|
||||||
_windowsHardlinkFileService = windowsHardlinkFileService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void PopulateInodeCounts(string directoryPath)
|
|
||||||
{
|
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
||||||
{
|
|
||||||
_windowsHardlinkFileService.PopulateFileIndexCounts(directoryPath);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_unixHardlinkFileService.PopulateInodeCounts(directoryPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long GetHardLinkCount(string filePath, bool ignoreRootDir)
|
|
||||||
{
|
|
||||||
if (!File.Exists(filePath))
|
|
||||||
{
|
|
||||||
_logger.LogDebug("file {file} does not exist", filePath);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
||||||
{
|
|
||||||
return _windowsHardlinkFileService.GetWindowsHardLinkCount(filePath, ignoreRootDir);
|
|
||||||
}
|
|
||||||
|
|
||||||
return _unixHardlinkFileService.GetHardlinkCount(filePath, ignoreRootDir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
namespace Infrastructure.Verticals.Files;
|
||||||
|
|
||||||
|
public interface IHardLinkFileService
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Populates the inode counts for Unix and the file index counts for Windows.
|
||||||
|
/// Needs to be called before <see cref="GetHardLinkCount"/> to populate the inode counts.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="directoryPath">The root directory where to search for hardlinks.</param>
|
||||||
|
void PopulateFileCounts(string directoryPath);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the hardlink count of a file.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filePath">File path.</param>
|
||||||
|
/// <param name="ignoreRootDir">Whether to ignore hardlinks found in the same root dir.</param>
|
||||||
|
/// <returns>-1 on error, 0 if there are no hardlinks and 1 otherwise.</returns>
|
||||||
|
long GetHardLinkCount(string filePath, bool ignoreRootDir);
|
||||||
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace Infrastructure.Verticals.Files;
|
|
||||||
|
|
||||||
public interface IHardlinkFileService
|
|
||||||
{
|
|
||||||
void PopulateInodeCounts(string directoryPath);
|
|
||||||
long GetHardLinkCount(string filePath, bool ignoreRootDir);
|
|
||||||
}
|
|
||||||
+14
-14
@@ -4,18 +4,18 @@ using Mono.Unix.Native;
|
|||||||
|
|
||||||
namespace Infrastructure.Verticals.Files;
|
namespace Infrastructure.Verticals.Files;
|
||||||
|
|
||||||
public class UnixHardlinkFileService
|
public class UnixHardLinkFileService : IHardLinkFileService
|
||||||
{
|
{
|
||||||
private readonly ILogger<UnixHardlinkFileService> _logger;
|
private readonly ILogger<UnixHardLinkFileService> _logger;
|
||||||
// Track inode counts in the ignored directory (e.g., root directory)
|
|
||||||
private readonly ConcurrentDictionary<ulong, int> _inodeCounts = new();
|
private readonly ConcurrentDictionary<ulong, int> _inodeCounts = new();
|
||||||
|
|
||||||
public UnixHardlinkFileService(ILogger<UnixHardlinkFileService> logger)
|
public UnixHardLinkFileService(ILogger<UnixHardLinkFileService> logger)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long GetHardlinkCount(string filePath, bool ignoreRootDir)
|
/// <inheritdoc/>
|
||||||
|
public long GetHardLinkCount(string filePath, bool ignoreRootDir)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -27,15 +27,14 @@ public class UnixHardlinkFileService
|
|||||||
|
|
||||||
if (!ignoreRootDir)
|
if (!ignoreRootDir)
|
||||||
{
|
{
|
||||||
// Simple case: Just check if >1 hardlink exists
|
|
||||||
_logger.LogDebug("stat file | hardlinks: {nlink} | {file}", stat.st_nlink, filePath);
|
_logger.LogDebug("stat file | hardlinks: {nlink} | {file}", stat.st_nlink, filePath);
|
||||||
return (long)stat.st_nlink;
|
return (long)stat.st_nlink == 1 ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjusted case: Subtract links from the ignored directory
|
// subtract the number of hardlinks in the same root directory
|
||||||
int linksInIgnoredDir = _inodeCounts.TryGetValue(stat.st_ino, out int count)
|
int linksInIgnoredDir = _inodeCounts.TryGetValue(stat.st_ino, out int count)
|
||||||
? count
|
? count
|
||||||
: 1; // Default to 1 if not found
|
: 1; // default to 1 if not found
|
||||||
|
|
||||||
_logger.LogDebug("stat file | hardlinks: {nlink} | ignored: {ignored} | {file}", stat.st_nlink, linksInIgnoredDir, filePath);
|
_logger.LogDebug("stat file | hardlinks: {nlink} | ignored: {ignored} | {file}", stat.st_nlink, linksInIgnoredDir, filePath);
|
||||||
return (long)stat.st_nlink - linksInIgnoredDir;
|
return (long)stat.st_nlink - linksInIgnoredDir;
|
||||||
@@ -47,12 +46,11 @@ public class UnixHardlinkFileService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call this first to populate inode counts from the directory you want to ignore
|
/// <inheritdoc/>
|
||||||
public void PopulateInodeCounts(string directoryPath)
|
public void PopulateFileCounts(string directoryPath)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Traverse all files and directories in the ignored path
|
|
||||||
foreach (var file in Directory.EnumerateFiles(directoryPath, "*", SearchOption.AllDirectories))
|
foreach (var file in Directory.EnumerateFiles(directoryPath, "*", SearchOption.AllDirectories))
|
||||||
{
|
{
|
||||||
AddInodeToCount(file);
|
AddInodeToCount(file);
|
||||||
@@ -65,7 +63,8 @@ public class UnixHardlinkFileService
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Failed to populate inode counts from {dir}", directoryPath);
|
_logger.LogError(ex, "failed to populate inode counts from {dir}", directoryPath);
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,7 +79,8 @@ public class UnixHardlinkFileService
|
|||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogWarning(ex, "Couldn't stat {path} during inode counting", path);
|
_logger.LogWarning(ex, "could not stat {path} during inode counting", path);
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+8
-7
@@ -5,18 +5,18 @@ using Microsoft.Win32.SafeHandles;
|
|||||||
|
|
||||||
namespace Infrastructure.Verticals.Files;
|
namespace Infrastructure.Verticals.Files;
|
||||||
|
|
||||||
public class WindowsHardlinkFileService
|
public class WindowsHardLinkFileService : IHardLinkFileService
|
||||||
{
|
{
|
||||||
private readonly ILogger<WindowsHardlinkFileService> _logger;
|
private readonly ILogger<WindowsHardLinkFileService> _logger;
|
||||||
// Track file indices in the ignored directory (e.g., root directory)
|
|
||||||
private readonly ConcurrentDictionary<ulong, int> _fileIndexCounts = new();
|
private readonly ConcurrentDictionary<ulong, int> _fileIndexCounts = new();
|
||||||
|
|
||||||
public WindowsHardlinkFileService(ILogger<WindowsHardlinkFileService> logger)
|
public WindowsHardLinkFileService(ILogger<WindowsHardLinkFileService> logger)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long GetWindowsHardLinkCount(string filePath, bool ignoreRootDir)
|
/// <inheritdoc/>
|
||||||
|
public long GetHardLinkCount(string filePath, bool ignoreRootDir)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -31,7 +31,7 @@ public class WindowsHardlinkFileService
|
|||||||
if (!ignoreRootDir)
|
if (!ignoreRootDir)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("stat file | hardlinks: {nlink} | {file}", file.NumberOfLinks, filePath);
|
_logger.LogDebug("stat file | hardlinks: {nlink} | {file}", file.NumberOfLinks, filePath);
|
||||||
return file.NumberOfLinks;
|
return file.NumberOfLinks == 1 ? 0 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get unique file ID (combination of high and low indices)
|
// Get unique file ID (combination of high and low indices)
|
||||||
@@ -52,7 +52,8 @@ public class WindowsHardlinkFileService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PopulateFileIndexCounts(string directoryPath)
|
/// <inheritdoc/>
|
||||||
|
public void PopulateFileCounts(string directoryPath)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Reference in New Issue
Block a user