fix: use NAS directory filenames for already-safe matching when manifest is sparse

When Kisani's manifest has 0–1 entries but the NAS directory contains thousands of
files uploaded by other tools, reconcile previously showed Already Safe = 1 because
countSafe only matched against the tiny manifest.

The validity check in buildManifestIndex now:
- Stores the filtered NAS filenames in NASManifestCache (directoryFilenames field)
- Returns a ManifestIndex(nasListing:) for filename-based matching against all NAS files

The fast-path reconcile now:
- Detects a sparse manifest (entries ≤ 1, dirCount > 1)
- Rebuilds ManifestIndex(nasFilenames:) from the cached filenames for O(1) filename matching
- Falls back to full NAS reconcile if cached filenames aren't available yet

Adds ManifestIndex.init(nasFilenames:) for fast-path reconstruction from cached strings.

Co-Authored-By: Kutesir <kutesir@provoc.ug>
Co-Authored-By: Sentry <sentry@provoc.ug>
This commit is contained in:
Robin Kutesa
2026-05-18 21:07:00 +03:00
parent 809929e0ed
commit 2384a74517
4 changed files with 68 additions and 15 deletions

View File

@@ -122,7 +122,7 @@
90F628AC04DC05FDCBAE52D9 /* SyncView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncView.swift; sourceTree = "<group>"; };
966927456571CE45600BEF75 /* BackupManifest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupManifest.swift; sourceTree = "<group>"; };
9EF7C3E36ACFD6096DE0677A /* ConnectViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectViewModel.swift; sourceTree = "<group>"; };
A1DAA26CE0A4DFFDD90370F2 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
A1DAA26CE0A4DFFDD90370F2 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
B3306187119851CE3E989408 /* MainTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTabView.swift; sourceTree = "<group>"; };
B70F1C01E131F5F2798200E2 /* HistoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryView.swift; sourceTree = "<group>"; };
BBBDD83322981C31F4DE00FF /* SavedConnections.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavedConnections.swift; sourceTree = "<group>"; };
@@ -139,7 +139,7 @@
E317DD286568B85367A66AD3 /* NASManifestCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NASManifestCache.swift; sourceTree = "<group>"; };
E956B8562EDB9259034CF8FF /* AppMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppMenuView.swift; sourceTree = "<group>"; };
E987653F4F7129568656EFDE /* BackupResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupResult.swift; sourceTree = "<group>"; };
ED5F358675A200A5C0FF2289 /* NASBackup.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = NASBackup.app; sourceTree = BUILT_PRODUCTS_DIR; };
ED5F358675A200A5C0FF2289 /* NASBackup.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NASBackup.app; sourceTree = BUILT_PRODUCTS_DIR; };
EF029B56DE72F2D5D7977D95 /* SFTPService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SFTPService.swift; sourceTree = "<group>"; };
EF4EDDA4B02ABF0F17BE86D5 /* BackupQueueItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BackupQueueItem.swift; sourceTree = "<group>"; };
F052EE8B0A757532E4BCBCCD /* LoginView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginView.swift; sourceTree = "<group>"; };
@@ -463,7 +463,6 @@
LastUpgradeCheck = 1500;
TargetAttributes = {
009689A0ADEB4878A288991E = {
DevelopmentTeam = "";
ProvisioningStyle = Automatic;
};
3362521ADE965E6BA3383045 = {
@@ -600,6 +599,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = NASBackup.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
DEVELOPMENT_TEAM = K8BLMMR883;
INFOPLIST_FILE = App/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
@@ -779,6 +779,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_ENTITLEMENTS = NASBackup.entitlements;
CODE_SIGN_IDENTITY = "iPhone Developer";
DEVELOPMENT_TEAM = K8BLMMR883;
INFOPLIST_FILE = App/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",