Skip to content

Commit 73fd6e1

Browse files
committed
fix(file-provider): detect lock changes during working set enumeration.
Nextcloud file locking does not update etags, so the previous optimisation that skipped child directories with unchanged etags silently hid lock state changes on nested files. Only skip directories that are both unchanged and have no materialised descendants. Also preserve lockToken and visitedDirectory in the target depth read path, matching the other two code paths. Signed-off-by: Camila Ayres <hello@camilasan.com>
1 parent 9ac8d75 commit 73fd6e1

2 files changed

Lines changed: 15 additions & 18 deletions

File tree

shell_integration/MacOSX/NextcloudFileProviderKit/Sources/NextcloudFileProviderKit/Enumeration/Enumerator+SyncEngine.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,8 @@ extension Enumerator {
207207
let updatedMetadatas = isNew ? [] : [metadata]
208208
let newMetadatas = isNew ? [metadata] : []
209209

210+
metadata.lockToken = existing?.lockToken
211+
metadata.visitedDirectory = existing?.visitedDirectory == true
210212
metadata.downloaded = existing?.downloaded == true
211213
metadata.keepDownloaded = existing?.keepDownloaded == true
212214
dbManager.addItemMetadata(metadata)

shell_integration/MacOSX/NextcloudFileProviderKit/Sources/NextcloudFileProviderKit/Enumeration/Enumerator.swift

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -483,30 +483,25 @@ public final class Enumerator: NSObject, NSFileProviderEnumerator, Sendable {
483483
examinedChildFilesAndDeletedItems.formUnion(metadatas[1...].filter { !$0.directory }.map(\.ocId))
484484
}
485485

486-
// If the target is not in the updated metadatas then neither it, nor any of its kids have changed. So skip examining all of them.
487-
if !allUpdatedMetadatas.contains(where: { $0.ocId == target.ocId }) {
488-
logger.debug("Target has not changed. Skipping children.", [.url: itemRemoteUrl])
489-
let materialisedChildren = materializedItems.filter { $0.serverUrl.hasPrefix(itemRemoteUrl) }.map(\.ocId)
490-
examinedChildFilesAndDeletedItems.formUnion(materialisedChildren)
491-
}
492-
493-
// OPTIMIZATION: For any child directories returned in this enumeration, if they haven't changed (etag matches database), mark them as examined so we don't enumerate them separately later.
486+
// Only skip unchanged child directories with no materialized descendants.
487+
// Lock changes don't propagate etags, so dirs with visible children must be enumerated.
494488
if metadatas.count > 1 {
495489
let childDirectories = metadatas[1...].filter(\.directory)
496490

497491
for childDir in childDirectories {
498-
// Check if this directory is in our materialized items list
499-
if let localItem = materializedItems.first(where: { $0.ocId == childDir.ocId }), localItem.etag == childDir.etag {
500-
// Directory hasn't changed, mark as examined to skip separate enumeration.
501-
logger.debug("Child directory etag unchanged, marking as examined.", [.name: childDir.fileName, .eTag: childDir.etag])
502-
examinedChildFilesAndDeletedItems.insert(childDir.ocId)
492+
guard let localItem = materializedItems.first(
493+
where: { $0.ocId == childDir.ocId }
494+
), localItem.isInSameDatabaseStoreableRemoteState(childDir) else {
495+
continue
496+
}
503497

504-
// Also mark any materialized children of this directory as examined.
505-
let grandChildren = materializedItems.filter {
506-
$0.serverUrl.hasPrefix(localItem.remotePath())
507-
}
498+
let hasMaterializedDescendants = materializedItems.contains {
499+
$0.ocId != localItem.ocId
500+
&& $0.serverUrl.hasPrefix(localItem.remotePath())
501+
}
508502

509-
examinedChildFilesAndDeletedItems.formUnion(grandChildren.map(\.ocId))
503+
if !hasMaterializedDescendants {
504+
examinedChildFilesAndDeletedItems.insert(childDir.ocId)
510505
}
511506
}
512507
}

0 commit comments

Comments
 (0)