diff --git a/src/AppCommon/FileTime.App.ContainerSizeScanner/ContainerSizeSizeScanProvider.cs b/src/AppCommon/FileTime.App.ContainerSizeScanner/ContainerSizeSizeScanProvider.cs index 4e700d0..9b6c4cb 100644 --- a/src/AppCommon/FileTime.App.ContainerSizeScanner/ContainerSizeSizeScanProvider.cs +++ b/src/AppCommon/FileTime.App.ContainerSizeScanner/ContainerSizeSizeScanProvider.cs @@ -100,6 +100,9 @@ public class ContainerSizeSizeScanProvider : ContentProviderBase, IContainerSize public override VolumeSizeInfo? GetVolumeSizeInfo(FullName path) => null; + public override ValueTask GetSupportedPathPart(NativePath nativePath) + => ValueTask.FromResult(nativePath); + public ISizeScanTask StartSizeScan(IContainer scanSizeOf) { var searchTask = _serviceProvider diff --git a/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/NavigationUserCommandHandlerService.cs b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/NavigationUserCommandHandlerService.cs index 9fadd1c..9b41600 100644 --- a/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/NavigationUserCommandHandlerService.cs +++ b/src/AppCommon/FileTime.App.Core/Services/UserCommandHandler/NavigationUserCommandHandlerService.cs @@ -22,6 +22,7 @@ namespace FileTime.App.Core.Services.UserCommandHandler; public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase { private const int PageSize = 8; + private readonly IAppState _appState; private readonly IServiceProvider _serviceProvider; private readonly ILocalContentProvider _localContentProvider; @@ -33,6 +34,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase private readonly IContentProviderRegistry _contentProviderRegistry; private readonly ILogger _logger; private readonly ApplicationConfiguration _applicationConfiguration; + private ITabViewModel? _selectedTab; private IDeclarativeProperty? _currentLocation; private IDeclarativeProperty? _currentSelectedItem; @@ -147,9 +149,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase } private async Task GoByFrequency() - { - await _frequencyNavigationService.OpenNavigationWindow(); - } + => await _frequencyNavigationService.OpenNavigationWindow(); private async Task GoToPath() { @@ -166,6 +166,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase } catch { + // ignored } if (resolvedPath is IContainer container) @@ -173,10 +174,10 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase await _userCommandHandlerService.HandleCommandAsync( new OpenContainerCommand(new AbsolutePath(_timelessContentProvider, container))); } - else if (resolvedPath is IElement element) + else if (resolvedPath is IElement {Parent: { } parent}) { await _userCommandHandlerService.HandleCommandAsync( - new OpenContainerCommand(element.Parent!)); + new OpenContainerCommand(parent)); } else { @@ -492,7 +493,10 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase private Task CloseTab() { - if ((!_applicationConfiguration.AllowCloseLastTab && _appState.Tabs.Count < 2) || _selectedTab == null) return Task.CompletedTask; + if ((!_applicationConfiguration.AllowCloseLastTab && _appState.Tabs.Count < 2) || _selectedTab == null) + { + return Task.CompletedTask; + } var tabToRemove = _selectedTab; _appState.RemoveTab(tabToRemove!); @@ -503,6 +507,7 @@ public class NavigationUserCommandHandlerService : UserCommandHandlerServiceBase } catch { + // ignored } return Task.CompletedTask; diff --git a/src/AppCommon/FileTime.App.Search/SearchContentProvider.cs b/src/AppCommon/FileTime.App.Search/SearchContentProvider.cs index 45575d1..cb9e89b 100644 --- a/src/AppCommon/FileTime.App.Search/SearchContentProvider.cs +++ b/src/AppCommon/FileTime.App.Search/SearchContentProvider.cs @@ -80,6 +80,7 @@ public class SearchContentProvider : ContentProviderBase, ISearchContentProvider public override Task CanHandlePathAsync(NativePath path) => Task.FromResult(path.Path.StartsWith(ContentProviderName)); public override VolumeSizeInfo? GetVolumeSizeInfo(FullName path) => null; + public override ValueTask GetSupportedPathPart(NativePath nativePath) => throw new NotImplementedException(); public async Task StartSearchAsync(ISearchMatcher matcher, IContainer searchIn) { diff --git a/src/Core/FileTime.Core.Abstraction/ContentAccess/IContentProvider.cs b/src/Core/FileTime.Core.Abstraction/ContentAccess/IContentProvider.cs index 76fd3d4..49b6623 100644 --- a/src/Core/FileTime.Core.Abstraction/ContentAccess/IContentProvider.cs +++ b/src/Core/FileTime.Core.Abstraction/ContentAccess/IContentProvider.cs @@ -29,4 +29,5 @@ public interface IContentProvider : IContainer, IOnContainerEnter Task CanHandlePathAsync(NativePath path); Task CanHandlePathAsync(FullName path); VolumeSizeInfo? GetVolumeSizeInfo(FullName path); + ValueTask GetSupportedPathPart(NativePath nativePath); } \ No newline at end of file diff --git a/src/Core/FileTime.Core.ContentAccess/ContentProviderBase.cs b/src/Core/FileTime.Core.ContentAccess/ContentProviderBase.cs index fb7b41a..9f8aefb 100644 --- a/src/Core/FileTime.Core.ContentAccess/ContentProviderBase.cs +++ b/src/Core/FileTime.Core.ContentAccess/ContentProviderBase.cs @@ -95,5 +95,6 @@ public abstract class ContentProviderBase : IContentProvider public abstract VolumeSizeInfo? GetVolumeSizeInfo(FullName path); - public IItem WithParent(AbsolutePath parent) => this; + public IItem WithParent(AbsolutePath parent) => this; + public abstract ValueTask GetSupportedPathPart(NativePath nativePath); } \ No newline at end of file diff --git a/src/Core/FileTime.Core.ContentAccess/RootContentProvider.cs b/src/Core/FileTime.Core.ContentAccess/RootContentProvider.cs index 094be4c..0a2d211 100644 --- a/src/Core/FileTime.Core.ContentAccess/RootContentProvider.cs +++ b/src/Core/FileTime.Core.ContentAccess/RootContentProvider.cs @@ -86,6 +86,7 @@ public class RootContentProvider : IRootContentProvider public Task CanHandlePathAsync(FullName path) => throw new NotImplementedException(); public VolumeSizeInfo? GetVolumeSizeInfo(FullName path) => null; + public ValueTask GetSupportedPathPart(NativePath nativePath) => throw new NotImplementedException(); public IItem WithParent(AbsolutePath parent) => this; } \ No newline at end of file diff --git a/src/Core/FileTime.Core.ContentAccess/SubContentProviderBase.cs b/src/Core/FileTime.Core.ContentAccess/SubContentProviderBase.cs index 01b9a57..cf5fe3b 100644 --- a/src/Core/FileTime.Core.ContentAccess/SubContentProviderBase.cs +++ b/src/Core/FileTime.Core.ContentAccess/SubContentProviderBase.cs @@ -6,14 +6,14 @@ namespace FileTime.Core.ContentAccess; public abstract class SubContentProviderBase : ContentProviderBase { - private readonly IContentProvider _parentContentProvider; + public IContentProvider ParentContentProvider { get; } protected SubContentProviderBase( IContentProvider parentContentProvider, string name, ITimelessContentProvider timelessContentProvider) : base(name, timelessContentProvider) { - _parentContentProvider = parentContentProvider; + ParentContentProvider = parentContentProvider; } public override async Task GetItemByNativePathAsync( @@ -22,7 +22,7 @@ public abstract class SubContentProviderBase : ContentProviderBase bool forceResolve = false, AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown, ItemInitializationSettings itemInitializationSettings = default) - => await _parentContentProvider.GetItemByNativePathAsync( + => await ParentContentProvider.GetItemByNativePathAsync( nativePath, pointInTime, forceResolve, @@ -30,17 +30,14 @@ public abstract class SubContentProviderBase : ContentProviderBase itemInitializationSettings); public override async ValueTask GetNativePathAsync(FullName fullName) - => await _parentContentProvider.GetNativePathAsync(fullName); + => await ParentContentProvider.GetNativePathAsync(fullName); public override FullName GetFullName(NativePath nativePath) - => _parentContentProvider.GetFullName(nativePath); - - public override async Task GetContentAsync(IElement element, int? maxLength = null, CancellationToken cancellationToken = default) - => await _parentContentProvider.GetContentAsync(element, maxLength, cancellationToken); + => ParentContentProvider.GetFullName(nativePath); public override async Task CanHandlePathAsync(NativePath path) - => await _parentContentProvider.CanHandlePathAsync(path); + => await ParentContentProvider.CanHandlePathAsync(path); - public override VolumeSizeInfo? GetVolumeSizeInfo(FullName path) - => _parentContentProvider.GetVolumeSizeInfo(path); + public override async ValueTask GetSupportedPathPart(NativePath nativePath) + => await ParentContentProvider.GetSupportedPathPart(nativePath); } \ No newline at end of file diff --git a/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs b/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs index bfa9284..5803300 100644 --- a/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs +++ b/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs @@ -164,6 +164,22 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo }; } + public override ValueTask GetSupportedPathPart(NativePath nativePath) + { + var path = nativePath.Path; + var pathParts = path.Split(Path.DirectorySeparatorChar).SelectMany(p => p.Split(Constants.SeparatorChar)).ToArray(); + + for (var i = pathParts.Length - 1; i > 0; i--) + { + var possiblePath = string.Join(Path.DirectorySeparatorChar, pathParts.Take(i)); + if (!File.Exists(possiblePath) && !Directory.Exists(possiblePath)) continue; + + return ValueTask.FromResult(new NativePath(possiblePath)); + } + + return ValueTask.FromResult(null); + } + private Container CreateEmptyContainer(NativePath nativePath, PointInTime pointInTime, IEnumerable? initialExceptions = null) diff --git a/src/Providers/FileTime.Providers.Local/LocalContentReader.cs b/src/Providers/FileTime.Providers.Local/LocalContentReader.cs index a14cda1..fa2dfe7 100644 --- a/src/Providers/FileTime.Providers.Local/LocalContentReader.cs +++ b/src/Providers/FileTime.Providers.Local/LocalContentReader.cs @@ -40,10 +40,7 @@ public class LocalContentReader : IContentReader } } - public void SetPosition(long position) - { - Position = position; - } + public void SetPosition(long position) => Position = position; public Stream AsStream() => _binaryReader.BaseStream; diff --git a/src/Providers/FileTime.Providers.Remote/RemoteContentProvider.cs b/src/Providers/FileTime.Providers.Remote/RemoteContentProvider.cs index 9e70510..23d39db 100644 --- a/src/Providers/FileTime.Providers.Remote/RemoteContentProvider.cs +++ b/src/Providers/FileTime.Providers.Remote/RemoteContentProvider.cs @@ -104,6 +104,7 @@ public sealed class RemoteContentProvider : ContentProviderBase, IRemoteContentP public override Task CanHandlePathAsync(NativePath path) => throw new NotImplementedException(); public override VolumeSizeInfo? GetVolumeSizeInfo(FullName path) => throw new NotImplementedException(); + public override ValueTask GetSupportedPathPart(NativePath nativePath) => throw new NotImplementedException(); private string ConvertLocalFullNameToRemote(FullName fullName) { diff --git a/src/Tools/FileTime.Tools.VirtualDiskSources.Abstractions/FileTime.Tools.VirtualDiskSources.Abstractions.csproj b/src/Tools/FileTime.Tools.VirtualDiskSources.Abstractions/FileTime.Tools.VirtualDiskSources.Abstractions.csproj index 6a5c619..ac2440c 100644 --- a/src/Tools/FileTime.Tools.VirtualDiskSources.Abstractions/FileTime.Tools.VirtualDiskSources.Abstractions.csproj +++ b/src/Tools/FileTime.Tools.VirtualDiskSources.Abstractions/FileTime.Tools.VirtualDiskSources.Abstractions.csproj @@ -4,6 +4,7 @@ net7.0 enable enable + FileTime.Tools.VirtualDiskSources diff --git a/src/Tools/FileTime.Tools.VirtualDiskSources.Abstractions/IVirtualDiskContentProvider.cs b/src/Tools/FileTime.Tools.VirtualDiskSources.Abstractions/IVirtualDiskContentProvider.cs new file mode 100644 index 0000000..c94acd9 --- /dev/null +++ b/src/Tools/FileTime.Tools.VirtualDiskSources.Abstractions/IVirtualDiskContentProvider.cs @@ -0,0 +1,8 @@ +using FileTime.Core.ContentAccess; + +namespace FileTime.Tools.VirtualDiskSources; + +public interface IVirtualDiskContentProvider : IContentProvider +{ + +} \ No newline at end of file diff --git a/src/Tools/FileTime.Tools.VirtualDiskSources.Abstractions/IVirtualDiskContentProviderFactory.cs b/src/Tools/FileTime.Tools.VirtualDiskSources.Abstractions/IVirtualDiskContentProviderFactory.cs new file mode 100644 index 0000000..614f716 --- /dev/null +++ b/src/Tools/FileTime.Tools.VirtualDiskSources.Abstractions/IVirtualDiskContentProviderFactory.cs @@ -0,0 +1,8 @@ +using FileTime.Core.ContentAccess; + +namespace FileTime.Tools.VirtualDiskSources; + +public interface IVirtualDiskContentProviderFactory +{ + IVirtualDiskContentProvider Create(IContentProvider parentContentProvider); +} \ No newline at end of file diff --git a/src/Tools/FileTime.Tools.VirtualDiskSources.Abstractions/IVirtualDiskSubContentProvider.cs b/src/Tools/FileTime.Tools.VirtualDiskSources.Abstractions/IVirtualDiskSubContentProvider.cs index 32a6453..c7f3564 100644 --- a/src/Tools/FileTime.Tools.VirtualDiskSources.Abstractions/IVirtualDiskSubContentProvider.cs +++ b/src/Tools/FileTime.Tools.VirtualDiskSources.Abstractions/IVirtualDiskSubContentProvider.cs @@ -1,6 +1,6 @@ using FileTime.Core.ContentAccess; -namespace FileTime.Tools.VirtualDiskSources.Abstractions; +namespace FileTime.Tools.VirtualDiskSources; public interface IVirtualDiskSubContentProvider : ISubContentProvider { diff --git a/src/Tools/FileTime.Tools.VirtualDiskSources/Startup.cs b/src/Tools/FileTime.Tools.VirtualDiskSources/Startup.cs index 2ee099e..41fcd8e 100644 --- a/src/Tools/FileTime.Tools.VirtualDiskSources/Startup.cs +++ b/src/Tools/FileTime.Tools.VirtualDiskSources/Startup.cs @@ -1,6 +1,5 @@ using FileTime.App.Core.Services; using FileTime.Core.ContentAccess; -using FileTime.Tools.VirtualDiskSources.Abstractions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -13,6 +12,8 @@ public static class Startup services.TryAddSingleton(); services.AddSingleton(sp => sp.GetRequiredService()); services.AddSingleton(); + services.TryAddSingleton(); + services.TryAddSingleton, VirtualDiskContentReaderFactory>(); return services; } } \ No newline at end of file diff --git a/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskContentProvider.cs b/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskContentProvider.cs index b892a0e..3b7adeb 100644 --- a/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskContentProvider.cs +++ b/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskContentProvider.cs @@ -1,14 +1,50 @@ -using FileTime.Core.ContentAccess; +using DiscUtils.Udf; +using FileTime.Core.ContentAccess; +using FileTime.Core.Models; using FileTime.Core.Timeline; namespace FileTime.Tools.VirtualDiskSources; -public class VirtualDiskContentProvider : SubContentProviderBase +public class VirtualDiskContentProvider : SubContentProviderBase, IVirtualDiskContentProvider { + private readonly IContentAccessorFactory _contentAccessorFactory; + public VirtualDiskContentProvider( - IContentProvider parentContentProvider, - ITimelessContentProvider timelessContentProvider) + IContentProvider parentContentProvider, + ITimelessContentProvider timelessContentProvider, + IContentAccessorFactory contentAccessorFactory) : base(parentContentProvider, "virtual-disk", timelessContentProvider) { + _contentAccessorFactory = contentAccessorFactory; } + + public override async Task GetContentAsync(IElement element, int? maxLength = null, CancellationToken cancellationToken = default) + { + var elementNativePath = element.NativePath!; + + var supportedPath = await ParentContentProvider.GetSupportedPathPart(elementNativePath); + if (supportedPath is null) return null; + + var parentItem = await ParentContentProvider.GetItemByNativePathAsync(supportedPath, element.PointInTime); + if (parentItem is not IElement parentElement) return null; + + + var contentReaderFactory = _contentAccessorFactory.GetContentReaderFactory(parentElement.Provider); + var reader = await contentReaderFactory.CreateContentReaderAsync(parentElement); + + await using var readerStream = reader.AsStream(); + using var discReader = new UdfReader(readerStream); + + var subPath = elementNativePath.Path.Substring(supportedPath.Path.Length + 2 + Constants.SubContentProviderRootContainer.Length); + var fileInfo = discReader.GetFileInfo(subPath); + + await using var contentReader = fileInfo.Open(FileMode.Open, FileAccess.Read); + var data = new byte[1024 * 1024]; + var readAsync = await contentReader.ReadAsync(data, cancellationToken); + + return data[0..readAsync].ToArray(); + } + + public override VolumeSizeInfo? GetVolumeSizeInfo(FullName path) + => ParentContentProvider.GetVolumeSizeInfo(path); } \ No newline at end of file diff --git a/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskContentProviderFactory.cs b/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskContentProviderFactory.cs new file mode 100644 index 0000000..6a334b9 --- /dev/null +++ b/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskContentProviderFactory.cs @@ -0,0 +1,21 @@ +using FileTime.Core.ContentAccess; +using FileTime.Core.Timeline; + +namespace FileTime.Tools.VirtualDiskSources; + +public class VirtualDiskContentProviderFactory : IVirtualDiskContentProviderFactory +{ + private readonly ITimelessContentProvider _timelessContentProvider; + private readonly IContentAccessorFactory _contentAccessorFactory; + + public VirtualDiskContentProviderFactory( + ITimelessContentProvider timelessContentProvider, + IContentAccessorFactory contentAccessorFactory) + { + _timelessContentProvider = timelessContentProvider; + _contentAccessorFactory = contentAccessorFactory; + } + + public IVirtualDiskContentProvider Create(IContentProvider parentContentProvider) + => new VirtualDiskContentProvider(parentContentProvider, _timelessContentProvider, _contentAccessorFactory); +} \ No newline at end of file diff --git a/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskContentReader.cs b/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskContentReader.cs new file mode 100644 index 0000000..d5bceb8 --- /dev/null +++ b/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskContentReader.cs @@ -0,0 +1,39 @@ +using FileTime.Core.ContentAccess; + +namespace FileTime.Tools.VirtualDiskSources; + +public class VirtualDiskContentReader : IContentReader +{ + private readonly Stream _stream; + private readonly ICollection _disposables; + public int PreferredBufferSize => 1024 * 1024; + public long? Position => _stream.Position; + + public VirtualDiskContentReader(Stream stream, ICollection disposables) + { + _stream = stream; + _disposables = disposables; + } + + public async Task ReadBytesAsync(int bufferSize, int? offset = null) + { + var data = new byte[bufferSize]; + var read = await _stream.ReadAsync(data, offset ?? 0, bufferSize); + + return data[..read].ToArray(); + } + + public void SetPosition(long position) => _stream.Seek(position, SeekOrigin.Begin); + + public Stream AsStream() => _stream; + + + public void Dispose() + { + _stream.Dispose(); + foreach (var disposable in _disposables) + { + disposable.Dispose(); + } + } +} \ No newline at end of file diff --git a/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskContentReaderFactory.cs b/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskContentReaderFactory.cs new file mode 100644 index 0000000..2a25c0f --- /dev/null +++ b/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskContentReaderFactory.cs @@ -0,0 +1,43 @@ +using DiscUtils.Udf; +using FileTime.Core.ContentAccess; +using FileTime.Core.Models; + +namespace FileTime.Tools.VirtualDiskSources; + +public class VirtualDiskContentReaderFactory : IContentReaderFactory +{ + private readonly IContentAccessorFactory _contentAccessorFactory; + + public VirtualDiskContentReaderFactory(IContentAccessorFactory contentAccessorFactory) + { + _contentAccessorFactory = contentAccessorFactory; + } + + public async Task CreateContentReaderAsync(IElement element) + { + if (element.Provider is not VirtualDiskContentProvider provider) + throw new ArgumentException( + "Provider must be VirtualDiskContentProvider, but it is " + element.Provider.GetType(), + nameof(element)); + + var elementNativePath = element.NativePath!; + + var supportedPath = (await provider.ParentContentProvider.GetSupportedPathPart(elementNativePath))!; + + var parentElement = (IElement) await provider.ParentContentProvider.GetItemByNativePathAsync(supportedPath, element.PointInTime); + + + var contentReaderFactory = _contentAccessorFactory.GetContentReaderFactory(parentElement.Provider); + var reader = await contentReaderFactory.CreateContentReaderAsync(parentElement); + + var readerStream = reader.AsStream(); + var discReader = new UdfReader(readerStream); + + var subPath = elementNativePath.Path.Substring(supportedPath.Path.Length + 2 + Constants.SubContentProviderRootContainer.Length); + var fileInfo = discReader.GetFileInfo(subPath); + + var contentReader = fileInfo.Open(FileMode.Open, FileAccess.Read); + + return new VirtualDiskContentReader(contentReader, new IDisposable[] {discReader, readerStream, contentReader}); + } +} \ No newline at end of file diff --git a/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskSubContentProvider.cs b/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskSubContentProvider.cs index ea2ca6d..17b5a51 100644 --- a/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskSubContentProvider.cs +++ b/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskSubContentProvider.cs @@ -1,12 +1,10 @@ using System.Collections.ObjectModel; using DiscUtils; -using DiscUtils.Iso9660; using DiscUtils.Udf; using FileTime.Core.ContentAccess; using FileTime.Core.Enums; using FileTime.Core.Models; using FileTime.Core.Timeline; -using FileTime.Tools.VirtualDiskSources.Abstractions; namespace FileTime.Tools.VirtualDiskSources; @@ -14,14 +12,17 @@ public class VirtualDiskSubContentProvider : IVirtualDiskSubContentProvider { private readonly IContentAccessorFactory _contentAccessorFactory; private readonly ITimelessContentProvider _timelessContentProvider; + private readonly IVirtualDiskContentProviderFactory _virtualDiskContentProviderFactory; public VirtualDiskSubContentProvider( IContentAccessorFactory contentAccessorFactory, - ITimelessContentProvider timelessContentProvider + ITimelessContentProvider timelessContentProvider, + IVirtualDiskContentProviderFactory virtualDiskContentProviderFactory ) { _contentAccessorFactory = contentAccessorFactory; _timelessContentProvider = timelessContentProvider; + _virtualDiskContentProviderFactory = virtualDiskContentProviderFactory; } public Task CanHandleAsync(IElement parentElement) @@ -36,7 +37,7 @@ public class VirtualDiskSubContentProvider : IVirtualDiskSubContentProvider { var contentReaderFactory = _contentAccessorFactory.GetContentReaderFactory(parentElement.Provider); var reader = await contentReaderFactory.CreateContentReaderAsync(parentElement); - + await using var readerStream = reader.AsStream(); var discReader = new UdfReader(readerStream); @@ -48,15 +49,18 @@ public class VirtualDiskSubContentProvider : IVirtualDiskSubContentProvider var rootFullName = new FullName(rootFullNameBase); var rootNativePath = new NativePath(rootNativePathBase); - return CreateContainer(discReader.Root, + var container = CreateContainer( + discReader, + discReader.Root, rootFullName, rootNativePath, parentElement.Provider, parentElement.Parent!, parentElement.PointInTime, itemInitializationSettings); + return container; } - + return ResolveNonRootChild(discReader, parentElement, itemPath, pointInTime, itemInitializationSettings); } @@ -74,7 +78,7 @@ public class VirtualDiskSubContentProvider : IVirtualDiskSubContentProvider var childFullName = new FullName(childFullNameBase); var childNativePath = new NativePath(childNativePathBase); - + var parent = new AbsolutePath(_timelessContentProvider, pointInTime, childFullName.GetParent()!, AbsolutePathType.Container); var container = discReader.Root; @@ -89,6 +93,7 @@ public class VirtualDiskSubContentProvider : IVirtualDiskSubContentProvider if (container.GetDirectories().FirstOrDefault(d => d.Name == pathParts[^1]) is { } childContainer) { return CreateContainer( + discReader, childContainer, childFullName, childNativePath, @@ -99,22 +104,26 @@ public class VirtualDiskSubContentProvider : IVirtualDiskSubContentProvider ); } - if (container.GetFiles().FirstOrDefault(d => d.Name == pathParts[^1]) is { } childElement) + if (container.GetFiles().FirstOrDefault(d => d.Name == pathParts[^1]) is not { } childElement) { - return CreateElement( - childElement, - childFullName, - childNativePath, - parentElement.Provider, - parent, - pointInTime - ); + return null; } - return null; + var element = CreateElement( + childElement, + childFullName, + childNativePath, + parentElement.Provider, + parent, + pointInTime + ); + + discReader.Dispose(); + return element; } private IContainer CreateContainer( + UdfReader discReader, DiscDirectoryInfo sourceContainer, FullName fullname, NativePath nativePath, @@ -138,7 +147,7 @@ public class VirtualDiskSubContentProvider : IVirtualDiskSubContentProvider SupportsDelete.False, false, FormatAttributes(sourceContainer.Attributes), - new VirtualDiskContentProvider(parentContentProvider, _timelessContentProvider), + _virtualDiskContentProviderFactory.Create(parentContentProvider), false, pointInTime, exceptions, @@ -148,7 +157,11 @@ public class VirtualDiskSubContentProvider : IVirtualDiskSubContentProvider if (!initializationSettings.SkipChildInitialization) { - ThreadPool.QueueUserWorkItem(_ => LoadChildren(container, sourceContainer, children, pointInTime, exceptions)); + ThreadPool.QueueUserWorkItem(_ => + { + LoadChildren(container, sourceContainer, children, pointInTime, exceptions); + discReader.Dispose(); + }); } return container; @@ -204,7 +217,7 @@ public class VirtualDiskSubContentProvider : IVirtualDiskSubContentProvider false, FormatAttributes(childElement.Attributes), childElement.Length, - new VirtualDiskContentProvider(parentContentProvider, _timelessContentProvider), + _virtualDiskContentProviderFactory.Create(parentContentProvider), pointInTime, new ObservableCollection(), new ReadOnlyExtensionCollection(new ExtensionCollection())