From b85c19407e91fd6871a4f80f734da8ddd4b05906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81d=C3=A1m=20Kov=C3=A1cs?= Date: Tue, 5 Sep 2023 00:13:24 +0200 Subject: [PATCH] Browse compressed file content --- .../ContentAccess/ISubContentProvider.cs | 4 +- .../ICompressedContentProvider.cs | 8 + .../ICompressedContentProviderFactory.cs | 8 + .../ICompressedSubContentProvider.cs | 8 + .../FileTime.Tools.Compression.Core.csproj | 1 + .../{ => Compress}/CompressCommand.cs | 2 +- .../{ => Compress}/CompressCommandFactory.cs | 2 +- .../{ => Compress}/CompressUserCommand.cs | 2 +- .../CompressionUserCommandHandler.cs | 2 + .../CompressedContentProvider.cs | 22 ++ .../CompressedContentProviderFactory.cs | 19 ++ .../CompressedSubContentProvider.cs | 301 ++++++++++++++++++ .../{ => Decompress}/DecompressCommand.cs | 2 +- .../DecompressCommandFactory.cs | 2 +- .../{ => Decompress}/DecompressUserCommand.cs | 2 +- .../FileTime.Tools.Compression.csproj | 4 + .../FileTime.Tools.Compression/Startup.cs | 7 + .../VirtualDiskContentProvider.cs | 1 - .../VirtualDiskSubContentProvider.cs | 23 +- 19 files changed, 406 insertions(+), 14 deletions(-) create mode 100644 src/Tools/FileTime.Tools.Compression.Core/ContentProvider/ICompressedContentProvider.cs create mode 100644 src/Tools/FileTime.Tools.Compression.Core/ContentProvider/ICompressedContentProviderFactory.cs create mode 100644 src/Tools/FileTime.Tools.Compression.Core/ContentProvider/ICompressedSubContentProvider.cs rename src/Tools/FileTime.Tools.Compression/{ => Compress}/CompressCommand.cs (99%) rename src/Tools/FileTime.Tools.Compression/{ => Compress}/CompressCommandFactory.cs (96%) rename src/Tools/FileTime.Tools.Compression/{ => Compress}/CompressUserCommand.cs (88%) create mode 100644 src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedContentProvider.cs create mode 100644 src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedContentProviderFactory.cs create mode 100644 src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedSubContentProvider.cs rename src/Tools/FileTime.Tools.Compression/{ => Decompress}/DecompressCommand.cs (99%) rename src/Tools/FileTime.Tools.Compression/{ => Decompress}/DecompressCommandFactory.cs (95%) rename src/Tools/FileTime.Tools.Compression/{ => Decompress}/DecompressUserCommand.cs (88%) diff --git a/src/Core/FileTime.Core.Abstraction/ContentAccess/ISubContentProvider.cs b/src/Core/FileTime.Core.Abstraction/ContentAccess/ISubContentProvider.cs index 9bc60b4..128b947 100644 --- a/src/Core/FileTime.Core.Abstraction/ContentAccess/ISubContentProvider.cs +++ b/src/Core/FileTime.Core.Abstraction/ContentAccess/ISubContentProvider.cs @@ -6,12 +6,12 @@ namespace FileTime.Core.ContentAccess; public interface ISubContentProvider { + Task CanHandleAsync(IElement parentElement); + Task GetItemByFullNameAsync( IElement parentElement, FullName itemPath, PointInTime pointInTime, AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown, ItemInitializationSettings itemInitializationSettings = default); - - Task CanHandleAsync(IElement parentElement); } \ No newline at end of file diff --git a/src/Tools/FileTime.Tools.Compression.Core/ContentProvider/ICompressedContentProvider.cs b/src/Tools/FileTime.Tools.Compression.Core/ContentProvider/ICompressedContentProvider.cs new file mode 100644 index 0000000..523285d --- /dev/null +++ b/src/Tools/FileTime.Tools.Compression.Core/ContentProvider/ICompressedContentProvider.cs @@ -0,0 +1,8 @@ +using FileTime.Core.ContentAccess; + +namespace FileTime.Tools.Compression.ContentProvider; + +public interface ICompressedContentProvider : IContentProvider +{ + +} \ No newline at end of file diff --git a/src/Tools/FileTime.Tools.Compression.Core/ContentProvider/ICompressedContentProviderFactory.cs b/src/Tools/FileTime.Tools.Compression.Core/ContentProvider/ICompressedContentProviderFactory.cs new file mode 100644 index 0000000..cb92553 --- /dev/null +++ b/src/Tools/FileTime.Tools.Compression.Core/ContentProvider/ICompressedContentProviderFactory.cs @@ -0,0 +1,8 @@ +using FileTime.Core.ContentAccess; + +namespace FileTime.Tools.Compression.ContentProvider; + +public interface ICompressedContentProviderFactory +{ + ICompressedContentProvider Create(IContentProvider parentContentProvider); +} \ No newline at end of file diff --git a/src/Tools/FileTime.Tools.Compression.Core/ContentProvider/ICompressedSubContentProvider.cs b/src/Tools/FileTime.Tools.Compression.Core/ContentProvider/ICompressedSubContentProvider.cs new file mode 100644 index 0000000..a56bb4e --- /dev/null +++ b/src/Tools/FileTime.Tools.Compression.Core/ContentProvider/ICompressedSubContentProvider.cs @@ -0,0 +1,8 @@ +using FileTime.Core.ContentAccess; + +namespace FileTime.Tools.Compression.ContentProvider; + +public interface ICompressedSubContentProvider : ISubContentProvider +{ + +} \ No newline at end of file diff --git a/src/Tools/FileTime.Tools.Compression.Core/FileTime.Tools.Compression.Core.csproj b/src/Tools/FileTime.Tools.Compression.Core/FileTime.Tools.Compression.Core.csproj index a5ce8c8..4baf9de 100644 --- a/src/Tools/FileTime.Tools.Compression.Core/FileTime.Tools.Compression.Core.csproj +++ b/src/Tools/FileTime.Tools.Compression.Core/FileTime.Tools.Compression.Core.csproj @@ -15,4 +15,5 @@ + diff --git a/src/Tools/FileTime.Tools.Compression/CompressCommand.cs b/src/Tools/FileTime.Tools.Compression/Compress/CompressCommand.cs similarity index 99% rename from src/Tools/FileTime.Tools.Compression/CompressCommand.cs rename to src/Tools/FileTime.Tools.Compression/Compress/CompressCommand.cs index b00f1fd..58e6902 100644 --- a/src/Tools/FileTime.Tools.Compression/CompressCommand.cs +++ b/src/Tools/FileTime.Tools.Compression/Compress/CompressCommand.cs @@ -9,7 +9,7 @@ using SharpCompress.Archives; using SharpCompress.Common; using SharpCompressCompressionType = SharpCompress.Common.CompressionType; -namespace FileTime.Tools.Compression; +namespace FileTime.Tools.Compression.Compress; public class CompressCommand : CommandBase, IExecutableCommand, ITransportationCommand, IRequireInputCommand { diff --git a/src/Tools/FileTime.Tools.Compression/CompressCommandFactory.cs b/src/Tools/FileTime.Tools.Compression/Compress/CompressCommandFactory.cs similarity index 96% rename from src/Tools/FileTime.Tools.Compression/CompressCommandFactory.cs rename to src/Tools/FileTime.Tools.Compression/Compress/CompressCommandFactory.cs index 30cdfee..fccf170 100644 --- a/src/Tools/FileTime.Tools.Compression/CompressCommandFactory.cs +++ b/src/Tools/FileTime.Tools.Compression/Compress/CompressCommandFactory.cs @@ -4,7 +4,7 @@ using FileTime.Core.Interactions; using FileTime.Core.Models; using FileTime.Core.Timeline; -namespace FileTime.Tools.Compression; +namespace FileTime.Tools.Compression.Compress; public class CompressCommandFactory : ITransportationCommandFactory { diff --git a/src/Tools/FileTime.Tools.Compression/CompressUserCommand.cs b/src/Tools/FileTime.Tools.Compression/Compress/CompressUserCommand.cs similarity index 88% rename from src/Tools/FileTime.Tools.Compression/CompressUserCommand.cs rename to src/Tools/FileTime.Tools.Compression/Compress/CompressUserCommand.cs index 2c272ca..f484afa 100644 --- a/src/Tools/FileTime.Tools.Compression/CompressUserCommand.cs +++ b/src/Tools/FileTime.Tools.Compression/Compress/CompressUserCommand.cs @@ -1,6 +1,6 @@ using FileTime.App.Core.UserCommand; -namespace FileTime.Tools.Compression; +namespace FileTime.Tools.Compression.Compress; public class CompressUserCommand : IIdentifiableUserCommand { diff --git a/src/Tools/FileTime.Tools.Compression/CompressionUserCommandHandler.cs b/src/Tools/FileTime.Tools.Compression/CompressionUserCommandHandler.cs index a92de66..51b48f1 100644 --- a/src/Tools/FileTime.Tools.Compression/CompressionUserCommandHandler.cs +++ b/src/Tools/FileTime.Tools.Compression/CompressionUserCommandHandler.cs @@ -4,6 +4,8 @@ using FileTime.App.Core.Services; using FileTime.App.Core.Services.UserCommandHandler; using FileTime.App.Core.ViewModels; using FileTime.Core.Models; +using FileTime.Tools.Compression.Compress; +using FileTime.Tools.Compression.Decompress; namespace FileTime.Tools.Compression; diff --git a/src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedContentProvider.cs b/src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedContentProvider.cs new file mode 100644 index 0000000..74239fc --- /dev/null +++ b/src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedContentProvider.cs @@ -0,0 +1,22 @@ +using System.Text; +using FileTime.Core.ContentAccess; +using FileTime.Core.Models; +using FileTime.Core.Timeline; + +namespace FileTime.Tools.Compression.ContentProvider; + +public class CompressedContentProvider : SubContentProviderBase, ICompressedContentProvider +{ + public CompressedContentProvider( + IContentProvider parentContentProvider, + ITimelessContentProvider timelessContentProvider + ) + : base(parentContentProvider, "compression", timelessContentProvider) + { + } + + public override Task GetContentAsync(IElement element, int? maxLength = null, CancellationToken cancellationToken = default) + => Task.FromResult((byte[]?)"Not implemented..."u8.ToArray()); + + public override VolumeSizeInfo? GetVolumeSizeInfo(FullName path) => throw new NotImplementedException(); +} \ No newline at end of file diff --git a/src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedContentProviderFactory.cs b/src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedContentProviderFactory.cs new file mode 100644 index 0000000..5dc1c2d --- /dev/null +++ b/src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedContentProviderFactory.cs @@ -0,0 +1,19 @@ +using FileTime.Core.ContentAccess; +using FileTime.Core.Timeline; + +namespace FileTime.Tools.Compression.ContentProvider; + +public class CompressedContentProviderFactory : ICompressedContentProviderFactory +{ + private readonly ITimelessContentProvider _timelessContentProvider; + + public CompressedContentProviderFactory(ITimelessContentProvider timelessContentProvider) + { + _timelessContentProvider = timelessContentProvider; + } + + public ICompressedContentProvider Create(IContentProvider parentContentProvider) + { + return new CompressedContentProvider(parentContentProvider, _timelessContentProvider); + } +} \ No newline at end of file diff --git a/src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedSubContentProvider.cs b/src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedSubContentProvider.cs new file mode 100644 index 0000000..c314722 --- /dev/null +++ b/src/Tools/FileTime.Tools.Compression/ContentProvider/CompressedSubContentProvider.cs @@ -0,0 +1,301 @@ +using System.Collections.ObjectModel; +using FileTime.Core.ContentAccess; +using FileTime.Core.Enums; +using FileTime.Core.Models; +using FileTime.Core.Timeline; +using SharpCompress.Archives; +using IContainer = FileTime.Core.Models.IContainer; + +namespace FileTime.Tools.Compression.ContentProvider; + +public sealed class CompressedSubContentProvider : ICompressedSubContentProvider +{ + private static readonly string[] SupportedExtensions = {".zip", ".gz", ".7z"}; + + private readonly IContentAccessorFactory _contentAccessorFactory; + private readonly ICompressedContentProviderFactory _compressedContentProviderFactory; + private readonly ITimelessContentProvider _timelessContentProvider; + + public CompressedSubContentProvider( + IContentAccessorFactory contentAccessorFactory, + ICompressedContentProviderFactory compressedContentProviderFactory, + ITimelessContentProvider timelessContentProvider + ) + { + _contentAccessorFactory = contentAccessorFactory; + _compressedContentProviderFactory = compressedContentProviderFactory; + _timelessContentProvider = timelessContentProvider; + } + + public Task CanHandleAsync(IElement parentElement) + => Task.FromResult( + parentElement.NativePath?.Path is { } path + && SupportedExtensions.Any(e => path.EndsWith(e)) + ); + + public async Task GetItemByFullNameAsync( + IElement parentElement, + FullName itemPath, + PointInTime pointInTime, + AbsolutePathType forceResolvePathType = AbsolutePathType.Unknown, + ItemInitializationSettings itemInitializationSettings = default) + { + var parentContentReader = await _contentAccessorFactory.GetContentReaderFactory(parentElement.Provider).CreateContentReaderAsync(parentElement); + var parentContentReaderStream = parentContentReader.AsStream(); + var archive = ArchiveFactory.Open(parentContentReaderStream); + var disposables = new IDisposable[] {parentContentReader, parentContentReaderStream, archive}; + + if (itemPath.Path.Length == 0 || itemPath.Path == Constants.SubContentProviderRootContainer) + { + var rootFullNameBase = parentElement.FullName!.Path + Constants.SeparatorChar + Constants.SubContentProviderRootContainer; + var rootNativePathBase = parentElement.NativePath!.Path + Constants.SeparatorChar + Constants.SubContentProviderRootContainer; + + var rootFullName = new FullName(rootFullNameBase); + var rootNativePath = new NativePath(rootNativePathBase); + + var container = CreateContainer( + archive, + new FullName(":/"), + rootFullName, + rootNativePath, + parentElement, + parentElement.Parent!, + itemInitializationSettings, + disposables + ); + return container; + } + + return ResolveNonRootChild( + archive, + parentElement, + itemPath, + itemInitializationSettings, + disposables + ); + } + + private IItem ResolveNonRootChild( + IArchive archive, + IElement parentElement, + FullName itemPath, + ItemInitializationSettings itemInitializationSettings, + ICollection disposables) + { + var childFullNameBase = parentElement.FullName!.Path + Constants.SeparatorChar + itemPath.Path; + var childNativePathBase = parentElement.NativePath!.Path + Constants.SeparatorChar + itemPath.Path; + + var childFullName = new FullName(childFullNameBase); + var childNativePath = new NativePath(childNativePathBase); + + var isDirectory = false; + var path = itemPath.Path + .Substring(1 + Constants.SubContentProviderRootContainer.Length) + .Replace(Constants.SeparatorChar, '/'); + + var pathWithSlash = path + '/'; + var size = 0L; + foreach (var archiveEntry in archive.Entries) + { + if (archiveEntry.Key == path) + { + if (archiveEntry.IsDirectory) + { + isDirectory = true; + break; + } + + size = archiveEntry.Size; + break; + } + + if (archiveEntry.Key.StartsWith(pathWithSlash)) + { + isDirectory = true; + break; + } + } + + var parent = new AbsolutePath( + _timelessContentProvider, + parentElement.PointInTime, + childFullName.GetParent()!, + AbsolutePathType.Container + ); + + if (isDirectory) + { + return CreateContainer( + archive, + itemPath, + childFullName, + childNativePath, + parentElement, + parent, + itemInitializationSettings, + disposables + ); + } + else + { + var element = CreateElement( + itemPath, + childFullName, + childNativePath, + parentElement, + parent, + size); + + foreach (var disposable in disposables) + { + disposable.Dispose(); + } + + return element; + } + } + + private IContainer CreateContainer( + IArchive archive, + FullName itemPath, + FullName fullName, + NativePath nativePath, + IElement parentElement, + AbsolutePath parent, + ItemInitializationSettings initializationSettings, + ICollection disposables) + { + var name = itemPath.Path.Split(Constants.SeparatorChar).Last(); + + var children = new ObservableCollection(); + var exceptions = new ObservableCollection(); + + var container = new Container( + name, + name, + fullName, + nativePath, + parent, + true, + true, + parentElement.CreatedAt, + parentElement.ModifiedAt, + SupportsDelete.False, + false, + "", + _compressedContentProviderFactory.Create(parentElement.Provider), + false, + parentElement.PointInTime, + exceptions, + new ReadOnlyExtensionCollection(new ExtensionCollection()), + children + ); + + if (!initializationSettings.SkipChildInitialization) + { + LoadChildren(archive, container, itemPath, parentElement.PointInTime, children, exceptions); + ThreadPool.QueueUserWorkItem(_ => + { + try + { + container.StartLoading(); + //LoadChildren(archive, container, itemPath, parentElement.PointInTime, children, exceptions); + } + finally + { + container.StopLoading(); + foreach (var disposable in disposables) + { + disposable.Dispose(); + } + } + }); + } + + return container; + } + + private void LoadChildren( + IArchive archive, + Container container, + FullName itemPath, + PointInTime pointInTime, + ObservableCollection children, + ObservableCollection exceptions) + { + var containerPath = itemPath.Path + .Substring(1 + Constants.SubContentProviderRootContainer.Length); + var containerLevel = containerPath.Length != 0 + ? containerPath.Split(Constants.SeparatorChar).Length + : 0; + + var addedContainers = new List(); + foreach (var archiveEntry in archive.Entries) + { + if (!archiveEntry.Key.StartsWith(containerPath)) continue; + + var childPathParts = archiveEntry.Key.TrimEnd('/').Split('/'); + if (childPathParts.Length < containerLevel + 1) continue; + var itemName = childPathParts[containerLevel]; + + if ((archiveEntry.IsDirectory && childPathParts.Length == containerLevel + 1) + || (!archiveEntry.IsDirectory && childPathParts.Length > containerLevel + 1)) + { + //Container + if (addedContainers.Contains(itemName)) continue; + addedContainers.Add(itemName); + + children.Add(new AbsolutePath( + _timelessContentProvider, + pointInTime, + container.FullName.GetChild(itemName), + AbsolutePathType.Container) + ); + } + else if (!archiveEntry.IsDirectory && childPathParts.Length == containerLevel + 1) + { + //Element + children.Add(new AbsolutePath( + _timelessContentProvider, + pointInTime, + container.FullName.GetChild(itemName), + AbsolutePathType.Element) + ); + } + } + } + + private IItem CreateElement(FullName itemPath, + FullName fullName, + NativePath nativePath, + IElement parentElement, + AbsolutePath parent, + long size) + { + var name = itemPath.Path.Split(Constants.SeparatorChar).Last(); + + var exceptions = new ObservableCollection(); + + var element = new Element( + name, + name, + fullName, + nativePath, + parent, + true, + true, + parentElement.CreatedAt, + parentElement.ModifiedAt, + SupportsDelete.False, + false, + "", + size, + _compressedContentProviderFactory.Create(parentElement.Provider), + parentElement.PointInTime, + exceptions, + new ReadOnlyExtensionCollection(new ExtensionCollection()) + ); + + return element; + } +} \ No newline at end of file diff --git a/src/Tools/FileTime.Tools.Compression/DecompressCommand.cs b/src/Tools/FileTime.Tools.Compression/Decompress/DecompressCommand.cs similarity index 99% rename from src/Tools/FileTime.Tools.Compression/DecompressCommand.cs rename to src/Tools/FileTime.Tools.Compression/Decompress/DecompressCommand.cs index 1fc583c..7e5d975 100644 --- a/src/Tools/FileTime.Tools.Compression/DecompressCommand.cs +++ b/src/Tools/FileTime.Tools.Compression/Decompress/DecompressCommand.cs @@ -4,7 +4,7 @@ using FileTime.Core.Models; using FileTime.Core.Timeline; using SharpCompress.Archives; -namespace FileTime.Tools.Compression; +namespace FileTime.Tools.Compression.Decompress; public class DecompressCommand : CommandBase, IExecutableCommand, ITransportationCommand, IDisposable { diff --git a/src/Tools/FileTime.Tools.Compression/DecompressCommandFactory.cs b/src/Tools/FileTime.Tools.Compression/Decompress/DecompressCommandFactory.cs similarity index 95% rename from src/Tools/FileTime.Tools.Compression/DecompressCommandFactory.cs rename to src/Tools/FileTime.Tools.Compression/Decompress/DecompressCommandFactory.cs index 7ef92d8..3ed0a43 100644 --- a/src/Tools/FileTime.Tools.Compression/DecompressCommandFactory.cs +++ b/src/Tools/FileTime.Tools.Compression/Decompress/DecompressCommandFactory.cs @@ -3,7 +3,7 @@ using FileTime.Core.ContentAccess; using FileTime.Core.Models; using FileTime.Core.Timeline; -namespace FileTime.Tools.Compression; +namespace FileTime.Tools.Compression.Decompress; public class DecompressCommandFactory : ITransportationCommandFactory { diff --git a/src/Tools/FileTime.Tools.Compression/DecompressUserCommand.cs b/src/Tools/FileTime.Tools.Compression/Decompress/DecompressUserCommand.cs similarity index 88% rename from src/Tools/FileTime.Tools.Compression/DecompressUserCommand.cs rename to src/Tools/FileTime.Tools.Compression/Decompress/DecompressUserCommand.cs index 6c36700..fe54601 100644 --- a/src/Tools/FileTime.Tools.Compression/DecompressUserCommand.cs +++ b/src/Tools/FileTime.Tools.Compression/Decompress/DecompressUserCommand.cs @@ -1,6 +1,6 @@ using FileTime.App.Core.UserCommand; -namespace FileTime.Tools.Compression; +namespace FileTime.Tools.Compression.Decompress; public class DecompressUserCommand : IIdentifiableUserCommand { diff --git a/src/Tools/FileTime.Tools.Compression/FileTime.Tools.Compression.csproj b/src/Tools/FileTime.Tools.Compression/FileTime.Tools.Compression.csproj index 4077082..a6e1ffa 100644 --- a/src/Tools/FileTime.Tools.Compression/FileTime.Tools.Compression.csproj +++ b/src/Tools/FileTime.Tools.Compression/FileTime.Tools.Compression.csproj @@ -9,8 +9,12 @@ + + + + diff --git a/src/Tools/FileTime.Tools.Compression/Startup.cs b/src/Tools/FileTime.Tools.Compression/Startup.cs index 5abb312..0441331 100644 --- a/src/Tools/FileTime.Tools.Compression/Startup.cs +++ b/src/Tools/FileTime.Tools.Compression/Startup.cs @@ -1,5 +1,10 @@ using FileTime.App.Core.Services; +using FileTime.Core.ContentAccess; +using FileTime.Tools.Compression.Compress; +using FileTime.Tools.Compression.ContentProvider; +using FileTime.Tools.Compression.Decompress; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; namespace FileTime.Tools.Compression; @@ -21,6 +26,8 @@ public static class Startup services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); + services.TryAddSingleton(); + services.AddSingleton(); 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 3b7adeb..9facfe9 100644 --- a/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskContentProvider.cs +++ b/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskContentProvider.cs @@ -28,7 +28,6 @@ public class VirtualDiskContentProvider : SubContentProviderBase, IVirtualDiskCo 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); diff --git a/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskSubContentProvider.cs b/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskSubContentProvider.cs index 17b5a51..1b55106 100644 --- a/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskSubContentProvider.cs +++ b/src/Tools/FileTime.Tools.VirtualDiskSources/VirtualDiskSubContentProvider.cs @@ -79,7 +79,12 @@ 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 parent = new AbsolutePath( + _timelessContentProvider, + pointInTime, + childFullName.GetParent()!, + AbsolutePathType.Container + ); var container = discReader.Root; for (var i = 1; i < pathParts.Length - 1; i++) @@ -125,7 +130,7 @@ public class VirtualDiskSubContentProvider : IVirtualDiskSubContentProvider private IContainer CreateContainer( UdfReader discReader, DiscDirectoryInfo sourceContainer, - FullName fullname, + FullName fullName, NativePath nativePath, IContentProvider parentContentProvider, AbsolutePath parent, @@ -137,7 +142,7 @@ public class VirtualDiskSubContentProvider : IVirtualDiskSubContentProvider var container = new Container( sourceContainer.Name, sourceContainer.Name, - fullname, + fullName, nativePath, parent, true, @@ -159,8 +164,16 @@ public class VirtualDiskSubContentProvider : IVirtualDiskSubContentProvider { ThreadPool.QueueUserWorkItem(_ => { - LoadChildren(container, sourceContainer, children, pointInTime, exceptions); - discReader.Dispose(); + try + { + container.StartLoading(); + LoadChildren(container, sourceContainer, children, pointInTime, exceptions); + } + finally + { + discReader.Dispose(); + container.StopLoading(); + } }); }