Skip to content

Commit 6bcddaf

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 487b43d commit 6bcddaf

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
@@ -225,6 +225,8 @@ extension Enumerator {
225225
let updatedMetadatas = isNew ? [] : [metadata]
226226
let newMetadatas = isNew ? [metadata] : []
227227

228+
metadata.lockToken = existing?.lockToken
229+
metadata.visitedDirectory = existing?.visitedDirectory == true
228230
metadata.downloaded = existing?.downloaded == true
229231
metadata.keepDownloaded = existing?.keepDownloaded == true
230232
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)