Skip to content

Commit 372e3ce

Browse files
authored
Merge pull request #66 from claucambra/bugfix/lock-files
Fix some bugs with file locking
2 parents 815c3ea + bbf1bf2 commit 372e3ce

11 files changed

Lines changed: 204 additions & 63 deletions

Sources/NextcloudFileProviderKit/Database/FilesDatabaseManager.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,9 +322,14 @@ public final class FilesDatabaseManager: Sendable {
322322
let database = ncDatabase()
323323

324324
do {
325+
// Find the metadatas that we previously knew to be on the server for this account
326+
// (we need to check if they were uploaded to prevent deleting ignored/lock files)
327+
//
328+
// - the ones that do exist remotely still are either the same or have been updated
329+
// - the ones that don't have been deleted
325330
let existingMetadatas = database
326331
.objects(RealmItemMetadata.self)
327-
.where { $0.account == account && $0.serverUrl == serverUrl }
332+
.where { $0.account == account && $0.serverUrl == serverUrl && $0.uploaded }
328333

329334
// NOTE: These metadatas are managed -- be careful!
330335
let metadatasToDelete = processItemMetadatasToDelete(

Sources/NextcloudFileProviderKit/Item/Item+CreateLockFile.swift

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ extension Item {
5454
Self.logger.info(
5555
"""
5656
Item to create:
57-
\(itemTemplate.filename)
57+
\(itemTemplate.filename, privacy: .public)
5858
is a lock file. Will handle by remotely locking the target file.
5959
"""
6060
)
@@ -70,8 +70,9 @@ extension Item {
7070
)
7171
return (nil, nil)
7272
}
73-
let (_, _, error) = await remoteInterface.setLockStateForFile(
74-
remotePath: parentItemRemotePath + "/" + targetFileName,
73+
let targetFileRemotePath = parentItemRemotePath + "/" + targetFileName
74+
let (_, _, error) = await remoteInterface.setLockStateForFile( // TODO: NOT WORKING
75+
remotePath: targetFileRemotePath,
7576
lock: true,
7677
account: account,
7778
options: .init(),
@@ -93,6 +94,8 @@ extension Item {
9394
received error: \(error.errorDescription)
9495
"""
9596
)
97+
} else {
98+
Self.logger.info("Locked file at: \(targetFileRemotePath, privacy: .public)")
9699
}
97100

98101
let metadata = SendableItemMetadata(
@@ -126,6 +129,10 @@ extension Item {
126129
dbManager.addItemMetadata(metadata)
127130

128131
progress.completedUnitCount = 1
132+
var returnError = error.fileProviderError
133+
if #available(macOS 13.0, *), error == .success {
134+
returnError = NSFileProviderError(.excludedFromSync)
135+
}
129136

130137
return (
131138
Item(
@@ -135,7 +142,7 @@ extension Item {
135142
remoteInterface: remoteInterface,
136143
dbManager: dbManager
137144
),
138-
error.fileProviderError
145+
returnError
139146
)
140147
}
141148
}

Sources/NextcloudFileProviderKit/Item/Item+DeleteLockFile.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ extension Item {
5656
return nil
5757
}
5858
let originalFileServerFileNameUrl = metadata.serverUrl + "/" + originalFileName
59-
let (_, _, error) = await remoteInterface.setLockStateForFile(
59+
let (_, _, error) = await remoteInterface.setLockStateForFile( // TODO: NOT WORKING
6060
remotePath: originalFileServerFileNameUrl,
6161
lock: false,
6262
account: account,

Sources/NextcloudFileProviderKit/Item/Item+Fetch.swift

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,12 +131,30 @@ public extension Item {
131131
dbManager: FilesDatabaseManager
132132
) async -> (URL?, Item?, Error?) {
133133
let ocId = itemIdentifier.rawValue
134+
guard metadata.classFile != "lock", !isLockFileName(filename) else {
135+
Self.logger.info(
136+
"""
137+
System requested fetch of lock file \(self.filename, privacy: .public)
138+
will just provide local contents URL if possible.
139+
"""
140+
)
141+
if let domain, let localUrl = await localUrlForContents(domain: domain) {
142+
return (localUrl, self, nil)
143+
} else if #available(macOS 13.0, *) {
144+
Self.logger.error("Could not get local contents URL for lock file, erroring")
145+
return (nil, self, NSFileProviderError(.excludedFromSync))
146+
} else {
147+
Self.logger.error("Could not get local contents URL for lock file, nilling")
148+
return (nil, self, nil)
149+
}
150+
}
151+
134152
let serverUrlFileName = metadata.serverUrl + "/" + metadata.fileName
135153

136154
Self.logger.debug(
137155
"""
138156
Fetching item with name \(self.metadata.fileName, privacy: .public)
139-
at URL: \(serverUrlFileName, privacy: .public)
157+
at URL: \(serverUrlFileName, privacy: .public)
140158
"""
141159
)
142160

Sources/NextcloudFileProviderKit/Item/Item+Modify.swift

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,22 @@ public extension Item {
827827
// refactors later on. Just use modifiedItem
828828
var modifiedItem = self
829829

830+
guard metadata.classFile != "lock", !isLockFileName(metadata.fileName) else {
831+
return await modifiedItem.modifyLockFile(
832+
itemTarget: itemTarget,
833+
baseVersion: baseVersion,
834+
changedFields: changedFields,
835+
contents: newContents,
836+
options: options,
837+
request: request,
838+
ignoredFiles: ignoredFiles,
839+
domain: domain,
840+
forcedChunkSize: forcedChunkSize,
841+
progress: progress,
842+
dbManager: dbManager
843+
)
844+
}
845+
830846
let relativePath = (
831847
metadata.serverUrl + "/" + metadata.fileName
832848
).replacingOccurrences(of: account.davFilesUrl, with: "")
@@ -871,22 +887,6 @@ public extension Item {
871887
return (nil, NSFileProviderError(.noSuchItem))
872888
}
873889

874-
guard metadata.classFile != "lock", !isLockFileName(metadata.fileName) else {
875-
return await modifiedItem.modifyLockFile(
876-
itemTarget: itemTarget,
877-
baseVersion: baseVersion,
878-
changedFields: changedFields,
879-
contents: newContents,
880-
options: options,
881-
request: request,
882-
ignoredFiles: ignoredFiles,
883-
domain: domain,
884-
forcedChunkSize: forcedChunkSize,
885-
progress: progress,
886-
dbManager: dbManager
887-
)
888-
}
889-
890890
let newParentItemIdentifier = itemTarget.parentItemIdentifier
891891
let isFolder = modifiedItem.contentType.conforms(to: .directory)
892892
let bundleOrPackage =

Sources/NextcloudFileProviderKit/Item/Item+ModifyLockFile.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,10 @@ extension Item {
103103
)
104104
}
105105

106-
return (modifiedItem, nil)
106+
var returnError: Error? = nil
107+
if #available(macOS 13.0, *) {
108+
returnError = NSFileProviderError(.excludedFromSync)
109+
}
110+
return (modifiedItem, returnError)
107111
}
108112
}

Sources/NextcloudFileProviderKit/Item/Item+ModifyUnuploaded.swift

Lines changed: 4 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -34,51 +34,18 @@ extension Item {
3434
let modifiedItem = self
3535
var contentsLocation = newContents
3636
if contentsLocation == nil {
37-
assert(domain != nil, "The domain should not be nil!")
38-
guard let manager = NSFileProviderManager(for: domain!),
39-
let fileUrl = try? await manager.getUserVisibleURL(
40-
for: modifiedItem.itemIdentifier
41-
)
42-
else {
37+
assert(domain != nil)
38+
guard let domain, let localUrl = await localUrlForContents(domain: domain) else {
4339
Self.logger.error(
4440
"""
4541
Unable to upload modified item that was previously ignored.
4642
filename: \(modifiedItem.filename, privacy: .public)
47-
Unable to get a file provider manager for the given domain, or item URL
43+
local url for contents could not be acquired.
4844
"""
4945
)
5046
return (nil, NSFileProviderError(.cannotSynchronize))
5147
}
52-
let fm = FileManager.default
53-
let tempLocation = fm.temporaryDirectory.appendingPathComponent(UUID().uuidString)
54-
let coordinator = NSFileCoordinator()
55-
var readData: Data?
56-
coordinator.coordinate(readingItemAt: fileUrl, options: [], error: nil) { readURL in
57-
readData = try? Data(contentsOf: readURL)
58-
}
59-
guard let readData else {
60-
Self.logger.error(
61-
"""
62-
Unable to upload modified item that was previously ignored.
63-
filename: \(modifiedItem.filename, privacy: .public)
64-
Unable to get ignored file item data from URL
65-
"""
66-
)
67-
return (nil, NSFileProviderError(.cannotSynchronize))
68-
}
69-
do {
70-
try readData.write(to: tempLocation)
71-
} catch let error {
72-
Self.logger.error(
73-
"""
74-
Unable to upload modified item that was previously ignored.
75-
filename: \(modifiedItem.filename, privacy: .public)
76-
Unable to write ignored file item contents to temp location.
77-
error: \(error.localizedDescription, privacy: .public)
78-
"""
79-
)
80-
}
81-
contentsLocation = tempLocation
48+
contentsLocation = localUrl
8249
}
8350
return await Self.create(
8451
basedOn: itemTarget,

Sources/NextcloudFileProviderKit/Item/Item.swift

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,4 +325,51 @@ public class Item: NSObject, NSFileProviderItem {
325325
dbManager: dbManager
326326
)
327327
}
328+
329+
public func localUrlForContents(domain: NSFileProviderDomain) async -> URL? {
330+
guard isDownloaded else {
331+
Self.logger.error(
332+
"""
333+
Unable to get local URL for item contents.
334+
filename: \(self.filename, privacy: .public)
335+
Item is not materialised.
336+
"""
337+
)
338+
return nil
339+
}
340+
341+
guard let manager = NSFileProviderManager(for: domain),
342+
let fileUrl = try? await manager.getUserVisibleURL(for: itemIdentifier)
343+
else {
344+
Self.logger.error(
345+
"""
346+
Unable to get manager or user visible url for item.
347+
filename: \(self.filename, privacy: .public)
348+
Cannot provide local URL for contents.
349+
"""
350+
)
351+
return nil
352+
}
353+
354+
let fm = FileManager.default
355+
let tempLocation = fm.temporaryDirectory.appendingPathComponent(UUID().uuidString)
356+
let coordinator = NSFileCoordinator()
357+
var readData: Data?
358+
coordinator.coordinate(readingItemAt: fileUrl, options: [], error: nil) { readURL in
359+
readData = try? Data(contentsOf: readURL)
360+
}
361+
guard let readData else { return nil }
362+
do {
363+
try readData.write(to: tempLocation)
364+
} catch let error {
365+
Self.logger.error(
366+
"""
367+
Unable to write file item contents \(self.filename, privacy: .public) to temp url.
368+
error: \(error.localizedDescription, privacy: .public)
369+
Cannot provide local URL for contents.
370+
"""
371+
)
372+
}
373+
return tempLocation
374+
}
328375
}

0 commit comments

Comments
 (0)