Skip to content

Commit 86d660b

Browse files
authored
(140535903) Avoid attaching removefile callbacks when delegate does not implement functions (#1070)
1 parent db10408 commit 86d660b

File tree

1 file changed

+72
-32
lines changed

1 file changed

+72
-32
lines changed

Sources/FoundationEssentials/FileManager/FileOperations.swift

Lines changed: 72 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,24 @@ extension FileManager {
152152
return delegateResponse ?? true
153153
}
154154

155+
fileprivate func _delegateImplementsRemoveItemConfirmation() -> Bool {
156+
guard let delegate = self.safeDelegate else { return false }
157+
#if FOUNDATION_FRAMEWORK
158+
return delegate.responds(to: #selector(FileManagerDelegate.fileManager(_:shouldRemoveItemAt:))) || delegate.responds(to: #selector(FileManagerDelegate.fileManager(_:shouldRemoveItemAtPath:)))
159+
#else
160+
return true
161+
#endif
162+
}
163+
164+
fileprivate func _delegateImplementsRemoveItemError() -> Bool {
165+
guard let delegate = self.safeDelegate else { return false }
166+
#if FOUNDATION_FRAMEWORK
167+
return delegate.responds(to: #selector(FileManagerDelegate.fileManager(_:shouldProceedAfterError:removingItemAt:))) || delegate.responds(to: #selector(FileManagerDelegate.fileManager(_:shouldProceedAfterError:removingItemAtPath:)))
168+
#else
169+
return true
170+
#endif
171+
}
172+
155173
fileprivate func _shouldProceedAfter(error: Error, copyingItemAtPath path: String, to dst: String) -> Bool {
156174
var delegateResponse: Bool?
157175

@@ -292,11 +310,15 @@ extension removefile_state_t {
292310
return num
293311
}
294312

295-
fileprivate func attachCallbacks(context: UnsafeRawPointer?, confirm: RemoveFileCallback, error: RemoveFileCallback) {
296-
removefile_state_set(self, UInt32(REMOVEFILE_STATE_CONFIRM_CONTEXT), context)
297-
removefile_state_set(self, UInt32(REMOVEFILE_STATE_CONFIRM_CALLBACK), unsafeBitCast(confirm, to: UnsafeRawPointer.self))
298-
removefile_state_set(self, UInt32(REMOVEFILE_STATE_ERROR_CONTEXT), context)
299-
removefile_state_set(self, UInt32(REMOVEFILE_STATE_ERROR_CALLBACK), unsafeBitCast(error, to: UnsafeRawPointer.self))
313+
fileprivate func attachCallbacks(context: UnsafeRawPointer?, confirm: RemoveFileCallback?, error: RemoveFileCallback?) {
314+
if let confirm {
315+
removefile_state_set(self, UInt32(REMOVEFILE_STATE_CONFIRM_CONTEXT), context)
316+
removefile_state_set(self, UInt32(REMOVEFILE_STATE_CONFIRM_CALLBACK), unsafeBitCast(confirm, to: UnsafeRawPointer.self))
317+
}
318+
if let error {
319+
removefile_state_set(self, UInt32(REMOVEFILE_STATE_ERROR_CONTEXT), context)
320+
removefile_state_set(self, UInt32(REMOVEFILE_STATE_ERROR_CALLBACK), unsafeBitCast(error, to: UnsafeRawPointer.self))
321+
}
300322
}
301323
}
302324
#endif
@@ -411,49 +433,67 @@ enum _FileOperations {
411433
#if canImport(Darwin)
412434
fileprivate class _FileRemoveContext {
413435
var error: CocoaError?
414-
var manager: FileManager?
436+
var manager: FileManager
415437

416-
init(_ manager: FileManager?) {
438+
init(_ manager: FileManager) {
417439
self.manager = manager
418440
}
419441
}
420442

443+
private static func _removeFile(_ pathPtr: UnsafePointer<CChar>, _ pathStr: String, state: removefile_state_t) throws {
444+
let err = removefile(pathPtr, state, removefile_flags_t(REMOVEFILE_RECURSIVE))
445+
if err < 0 {
446+
if errno != 0 {
447+
throw CocoaError.removeFileError(Int32(errno), pathStr)
448+
}
449+
throw CocoaError.removeFileError(state.errnum, pathStr)
450+
}
451+
}
452+
421453
private static func _removeFile(_ pathPtr: UnsafePointer<CChar>, _ pathStr: String, with fileManager: FileManager?) throws {
422454
let state = removefile_state_alloc()!
423455
defer { removefile_state_free(state) }
424456

457+
guard let fileManager else {
458+
// We have no file manager with a delegate, simply call removefile and return
459+
try Self._removeFile(pathPtr, pathStr, state: state)
460+
return
461+
}
462+
425463
let ctx = _FileRemoveContext(fileManager)
426464
try withExtendedLifetime(ctx) {
427465
let ctxPtr = Unmanaged.passUnretained(ctx).toOpaque()
428-
state.attachCallbacks(context: ctxPtr, confirm: { _, pathPtr, contextPtr in
429-
let context = Unmanaged<_FileOperations._FileRemoveContext>.fromOpaque(contextPtr).takeUnretainedValue()
430-
let path = String(cString: pathPtr)
431-
432-
// Proceed unless the delegate says to skip
433-
return (context.manager?._shouldRemoveItemAtPath(path) ?? true) ? REMOVEFILE_PROCEED : REMOVEFILE_SKIP
434-
}, error: { state, pathPtr, contextPtr in
435-
let context = Unmanaged<_FileOperations._FileRemoveContext>.fromOpaque(contextPtr).takeUnretainedValue()
436-
let path = String(cString: pathPtr)
437-
438-
let err = CocoaError.removeFileError(state.errnum, path)
439-
440-
// Proceed only if the delegate says so
441-
if context.manager?._shouldProceedAfter(error: err, removingItemAtPath: path) ?? false {
442-
return REMOVEFILE_PROCEED
443-
} else {
444-
context.error = err
445-
return REMOVEFILE_STOP
446-
}
447-
})
466+
var confirmCallback: RemoveFileCallback?
467+
var errorCallback: RemoveFileCallback?
448468

449-
let err = removefile(pathPtr, state, removefile_flags_t(REMOVEFILE_RECURSIVE))
450-
if err < 0 {
451-
if errno != 0 {
452-
throw CocoaError.removeFileError(Int32(errno), pathStr)
469+
if fileManager._delegateImplementsRemoveItemConfirmation() {
470+
confirmCallback = { _, pathPtr, contextPtr in
471+
let context = Unmanaged<_FileOperations._FileRemoveContext>.fromOpaque(contextPtr).takeUnretainedValue()
472+
let path = String(cString: pathPtr)
473+
474+
// Proceed unless the delegate says to skip
475+
return (context.manager._shouldRemoveItemAtPath(path)) ? REMOVEFILE_PROCEED : REMOVEFILE_SKIP
476+
}
477+
}
478+
if fileManager._delegateImplementsRemoveItemError() {
479+
errorCallback = { state, pathPtr, contextPtr in
480+
let context = Unmanaged<_FileOperations._FileRemoveContext>.fromOpaque(contextPtr).takeUnretainedValue()
481+
let path = String(cString: pathPtr)
482+
483+
let err = CocoaError.removeFileError(state.errnum, path)
484+
485+
// Proceed only if the delegate says so
486+
if context.manager._shouldProceedAfter(error: err, removingItemAtPath: path) {
487+
return REMOVEFILE_PROCEED
488+
} else {
489+
context.error = err
490+
return REMOVEFILE_STOP
491+
}
453492
}
454-
throw CocoaError.removeFileError(state.errnum, pathStr)
455493
}
456494

495+
state.attachCallbacks(context: ctxPtr, confirm: confirmCallback, error: errorCallback)
496+
try Self._removeFile(pathPtr, pathStr, state: state)
457497
if let error = ctx.error {
458498
throw error
459499
}

0 commit comments

Comments
 (0)