2026-02-05 11:14:01 +01:00
|
|
|
// Copyright (c) 2026 Duplicati Inc. All rights reserved.
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Runtime.CompilerServices;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
using Duplicati.Library.Interface;
|
|
|
|
|
using Duplicati.Proprietary.DiskImage.Filesystem;
|
|
|
|
|
|
|
|
|
|
namespace Duplicati.Proprietary.DiskImage.SourceItems;
|
|
|
|
|
|
2026-02-15 12:45:59 +01:00
|
|
|
/// <summary>
|
|
|
|
|
/// Represents a file within a filesystem as a source entry for backup operations.
|
|
|
|
|
/// </summary>
|
2026-02-05 11:14:01 +01:00
|
|
|
internal class FileSourceEntry(string parentPath, IFilesystem filesystem, IFile file)
|
2026-03-13 11:39:58 +01:00
|
|
|
: DiskImageEntryBase(System.IO.Path.Combine(parentPath, file.Address?.ToString("X016") ?? file.Path ?? "unknown"))
|
2026-02-05 11:14:01 +01:00
|
|
|
{
|
2026-02-15 12:45:59 +01:00
|
|
|
/// <inheritdoc />
|
2026-02-05 11:14:01 +01:00
|
|
|
public override bool IsFolder => file.IsDirectory;
|
2026-02-15 12:45:59 +01:00
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
2026-02-05 11:14:01 +01:00
|
|
|
public override long Size => file.Size;
|
|
|
|
|
|
2026-03-13 13:50:38 +00:00
|
|
|
/// If the file doesn't report a last modified time, use the current time as a fallback to always back it up.
|
2026-03-12 15:06:05 +01:00
|
|
|
/// <inheritdoc />
|
2026-03-13 13:50:38 +00:00
|
|
|
public override DateTime LastModificationUtc => file.LastModified ?? DateTime.UtcNow;
|
2026-03-12 15:06:05 +01:00
|
|
|
|
2026-02-15 12:45:59 +01:00
|
|
|
/// <inheritdoc />
|
2026-02-05 11:14:01 +01:00
|
|
|
public override Task<Stream> OpenRead(CancellationToken cancellationToken)
|
|
|
|
|
{
|
2026-02-10 12:30:43 +01:00
|
|
|
return filesystem.OpenReadStreamAsync(file, cancellationToken);
|
2026-02-05 11:14:01 +01:00
|
|
|
}
|
|
|
|
|
|
2026-02-15 12:45:59 +01:00
|
|
|
/// <inheritdoc />
|
2026-02-05 11:14:01 +01:00
|
|
|
public override async IAsyncEnumerable<ISourceProviderEntry> Enumerate([EnumeratorCancellation] CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
if (IsFolder)
|
|
|
|
|
{
|
|
|
|
|
await foreach (var child in filesystem.ListFilesAsync(file, cancellationToken))
|
|
|
|
|
{
|
|
|
|
|
yield return new FileSourceEntry(this.Path, filesystem, child);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-15 12:45:59 +01:00
|
|
|
/// <inheritdoc />
|
2026-02-06 06:12:55 +01:00
|
|
|
public override async Task<Dictionary<string, string?>> GetMinorMetadata(CancellationToken cancellationToken)
|
|
|
|
|
{
|
|
|
|
|
var metadata = await base.GetMinorMetadata(cancellationToken);
|
|
|
|
|
|
|
|
|
|
// Add file/block-level metadata
|
|
|
|
|
metadata["diskimage:Type"] = "block";
|
|
|
|
|
metadata["file:Path"] = file.Path;
|
|
|
|
|
metadata["file:Size"] = file.Size.ToString();
|
|
|
|
|
metadata["file:IsDirectory"] = file.IsDirectory.ToString();
|
|
|
|
|
metadata["filesystem:Type"] = filesystem.Type.ToString();
|
|
|
|
|
|
|
|
|
|
if (file.Address.HasValue)
|
|
|
|
|
metadata["block:Address"] = file.Address.Value.ToString();
|
|
|
|
|
|
|
|
|
|
return metadata;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-05 11:14:01 +01:00
|
|
|
}
|