Skip to content

Commit 071f0c6

Browse files
committed
Fixed threshold detection in RestoreRoutine
1 parent 240e023 commit 071f0c6

1 file changed

Lines changed: 30 additions & 3 deletions

File tree

src/Core/SecureFolderFS.Core/Routines/Operational/RestoreRoutine.cs

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Security.Cryptography;
4+
using System.Text;
45
using System.Threading;
56
using System.Threading.Tasks;
67
using OwlCore.Storage;
@@ -69,16 +70,37 @@ public async Task<IDisposable> FinalizeAsync(CancellationToken cancellationToken
6970
string? foundNameCrypt = null;
7071
string? foundEncoding = null;
7172
var noExtensions = 0;
72-
var shorteningThreshold = 0;
73+
var minSidecarContentLength = int.MaxValue;
74+
var hasShortenedNames = false;
7375

7476
var folderScanner = new DeepFolderScanner(contentFolder, StorableType.File);
7577
await foreach (var item in folderScanner.ScanFolderAsync(cancellationToken))
7678
{
7779
if (item is not IFile file)
7880
continue;
7981

80-
if (shorteningThreshold == 0 && item.Name.EndsWith(FileSystem.Constants.Names.SHORTENED_FILE_EXTENSION, StringComparison.OrdinalIgnoreCase))
81-
shorteningThreshold = 220; // Arbitrary threshold typical for shortened names
82+
// Read sidecar files to determine the shortening threshold from the actual ciphertext name length
83+
if (item.Name.EndsWith(FileSystem.Constants.Names.SIDECAR_FILE_EXTENSION, StringComparison.OrdinalIgnoreCase))
84+
{
85+
try
86+
{
87+
await using var sidecarStream = await file.OpenReadAsync(cancellationToken);
88+
var buffer = new byte[4097];
89+
var bytesRead = await sidecarStream.ReadAsync(buffer.AsMemory(0, buffer.Length), cancellationToken);
90+
if (bytesRead is > 0 and <= 4096)
91+
minSidecarContentLength = Math.Min(minSidecarContentLength, Encoding.UTF8.GetString(buffer, 0, bytesRead).Length);
92+
}
93+
catch { }
94+
95+
continue;
96+
}
97+
98+
// Shortened files are hashes that can't be decrypted directly
99+
if (item.Name.EndsWith(FileSystem.Constants.Names.SHORTENED_FILE_EXTENSION, StringComparison.OrdinalIgnoreCase))
100+
{
101+
hasShortenedNames = true;
102+
continue;
103+
}
82104

83105
if (!item.Name.EndsWith(FileSystem.Constants.Names.ENCRYPTED_FILE_EXTENSION, StringComparison.OrdinalIgnoreCase))
84106
noExtensions++;
@@ -102,6 +124,11 @@ public async Task<IDisposable> FinalizeAsync(CancellationToken cancellationToken
102124
if (foundNameCrypt is null || foundEncoding is null || foundContentCrypt is null)
103125
throw new InvalidOperationException("Could not find all required cryptographic components.");
104126

127+
// Determine shortening threshold from sidecar content, with fallback for missing sidecars
128+
var shorteningThreshold = minSidecarContentLength < int.MaxValue
129+
? minSidecarContentLength
130+
: hasShortenedNames ? 220 : 0;
131+
105132
// Regenerate config
106133
var configDataModel = new V4VaultConfigurationDataModel()
107134
{

0 commit comments

Comments
 (0)