             try body(srcPath, dstPath, fileType)
         }
     }
-
-    open func copyItem(atPath srcPath: String, toPath dstPath: String) throws {
+    
+    private func shouldProceedAfterError(_ error: Error, copyingItemAtPath path: String, toPath: String, isURL: Bool) -> Bool {
+        guard let delegate = self.delegate else { return false }
+        if isURL {
+            return delegate.fileManager(self, shouldProceedAfterError: error, copyingItemAt: URL(fileURLWithPath: path), to: URL(fileURLWithPath: toPath))
+        } else {
+            return delegate.fileManager(self, shouldProceedAfterError: error, copyingItemAtPath: path, toPath: toPath)
+        }
+    }
+    
+    private func shouldCopyItemAtPath(_ path: String, toPath: String, isURL: Bool) -> Bool {
+        guard let delegate = self.delegate else { return true }
+        if isURL {
+            return delegate.fileManager(self, shouldCopyItemAt: URL(fileURLWithPath: path), to: URL(fileURLWithPath: toPath))
+        } else {
+            return delegate.fileManager(self, shouldCopyItemAtPath: path, toPath: toPath)
+        }
+    }
+    
+    fileprivate func _copyItem(atPath srcPath: String, toPath dstPath: String, isURL: Bool) throws {
         try _copyOrLinkDirectoryHelper(atPath: srcPath, toPath: dstPath) { (srcPath, dstPath, fileType) in
-            if fileType == .typeSymbolicLink {
-                try _copySymlink(atPath: srcPath, toPath: dstPath)
-            } else if fileType == .typeRegular {
-                try _copyRegularFile(atPath: srcPath, toPath: dstPath)
-            }
-        }
-    }
-    
-    open func moveItem(atPath srcPath: String, toPath dstPath: String) throws {
+            guard shouldCopyItemAtPath(srcPath, toPath: dstPath, isURL: isURL) else {
+                return
+            }
+            
+            do {
+                switch fileType {
+                case .typeRegular:
+                    try _copyRegularFile(atPath: srcPath, toPath: dstPath)
+                case .typeSymbolicLink:
+                    try _copySymlink(atPath: srcPath, toPath: dstPath)
+                default:
+                    break
+                }
+            } catch {
+                if !shouldProceedAfterError(error, copyingItemAtPath: srcPath, toPath: dstPath, isURL: isURL) {
+                    throw error
+                }
+            }
+        }
+    }
+    
+    private func shouldProceedAfterError(_ error: Error, movingItemAtPath path: String, toPath: String, isURL: Bool) -> Bool {
+        guard let delegate = self.delegate else { return false }
+        if isURL {
+            return delegate.fileManager(self, shouldProceedAfterError: error, movingItemAt: URL(fileURLWithPath: path), to: URL(fileURLWithPath: toPath))
+        } else {
+            return delegate.fileManager(self, shouldProceedAfterError: error, movingItemAtPath: path, toPath: toPath)
+        }
+    }
+    
+    private func shouldMoveItemAtPath(_ path: String, toPath: String, isURL: Bool) -> Bool {
+        guard let delegate = self.delegate else { return true }
+        if isURL {
+            return delegate.fileManager(self, shouldMoveItemAt: URL(fileURLWithPath: path), to: URL(fileURLWithPath: toPath))
+        } else {
+            return delegate.fileManager(self, shouldMoveItemAtPath: path, toPath: toPath)
+        }
+    }
+    
+    private func _moveItem(atPath srcPath: String, toPath dstPath: String, isURL: Bool) throws {
+        guard shouldMoveItemAtPath(srcPath, toPath: dstPath, isURL: isURL) else {
+            return
+        }
+        
         guard !self.fileExists(atPath: dstPath) else {
             throw NSError(domain: NSCocoaErrorDomain, code: CocoaError.fileWriteFileExists.rawValue, userInfo: [NSFilePathErrorKey : NSString(dstPath)])
         }
         try _fileSystemRepresentation(withPath: srcPath, andPath: dstPath, {
             if rename($0, $1) != 0 {
                 if errno == EXDEV {
-                    // TODO: Copy and delete.
-                    NSUnimplemented("Cross-device moves not yet implemented")
+                    try _copyOrLinkDirectoryHelper(atPath: srcPath, toPath: dstPath, variant: "Move") { (srcPath, dstPath, fileType) in
+                        do {
+                            switch fileType {
+                            case .typeRegular:
+                                try _copyRegularFile(atPath: srcPath, toPath: dstPath, variant: "Move")
+                            case .typeSymbolicLink:
+                                try _copySymlink(atPath: srcPath, toPath: dstPath, variant: "Move")
+                            default:
+                                break
+                            }
+                        } catch {
+                            if !shouldProceedAfterError(error, movingItemAtPath: srcPath, toPath: dstPath, isURL: isURL) {
+                                throw error
+                            }
+                        }
+                    }
+                    
+                    // Remove source directory/file after successful moving
+                    try _removeItem(atPath: srcPath, isURL: isURL, alreadyConfirmed: true)
                 } else {
-                    throw _NSErrorWithErrno(errno, reading: false, path: srcPath)
+                    throw _NSErrorWithErrno(errno, reading: false, path: srcPath,
+                                            extraUserInfo: extraErrorInfo(srcPath: srcPath, dstPath: dstPath, userVariant: "Move"))
                 }
             }
         })
     }
     
-    open func linkItem(atPath srcPath: String, toPath dstPath: String) throws {
+    private func shouldProceedAfterError(_ error: Error, linkingItemAtPath path: String, toPath: String, isURL: Bool) -> Bool {
+        guard let delegate = self.delegate else { return false }
+        if isURL {
+            return delegate.fileManager(self, shouldProceedAfterError: error, linkingItemAt: URL(fileURLWithPath: path), to: URL(fileURLWithPath: toPath))
+        } else {
+            return delegate.fileManager(self, shouldProceedAfterError: error, linkingItemAtPath: path, toPath: toPath)
+        }
+    }
+    
+    private func shouldLinkItemAtPath(_ path: String, toPath: String, isURL: Bool) -> Bool {
+        guard let delegate = self.delegate else { return true }
+        if isURL {
+            return delegate.fileManager(self, shouldLinkItemAt: URL(fileURLWithPath: path), to: URL(fileURLWithPath: toPath))
+        } else {
+            return delegate.fileManager(self, shouldLinkItemAtPath: path, toPath: toPath)
+        }
+    }
+    
+    private func _linkItem(atPath srcPath: String, toPath dstPath: String, isURL: Bool) throws {
         try _copyOrLinkDirectoryHelper(atPath: srcPath, toPath: dstPath) { (srcPath, dstPath, fileType) in
-            if fileType == .typeSymbolicLink {
-                try _copySymlink(atPath: srcPath, toPath: dstPath)
-            } else if fileType == .typeRegular {
-                try _fileSystemRepresentation(withPath: srcPath, andPath: dstPath, {
-                    if link($0, $1) == -1 {
-                        throw _NSErrorWithErrno(errno, reading: false, path: srcPath)
-                    }
-                })
-            }
-        }
-    }
-
-    open func removeItem(atPath path: String) throws {
+            guard shouldLinkItemAtPath(srcPath, toPath: dstPath, isURL: isURL) else {
+                return
+            }
+            
+            do {
+                switch fileType {
+                case .typeRegular:
+                    try _fileSystemRepresentation(withPath: srcPath, andPath: dstPath, {
+                        if link($0, $1) == -1 {
+                            throw _NSErrorWithErrno(errno, reading: false, path: srcPath)
+                        }
+                    })
+                case .typeSymbolicLink:
+                    try _copySymlink(atPath: srcPath, toPath: dstPath)
+                default:
+                    break
+                }
+            } catch {
+                if !shouldProceedAfterError(error, linkingItemAtPath: srcPath, toPath: dstPath, isURL: isURL) {
+                    throw error
+                }
+            }
+        }
+    }
+    
+    private func shouldProceedAfterError(_ error: Error, removingItemAtPath path: String, isURL: Bool) -> Bool {
+        guard let delegate = self.delegate else { return false }
+        if isURL {
+            return delegate.fileManager(self, shouldProceedAfterError: error, removingItemAt: URL(fileURLWithPath: path))
+        } else {
+            return delegate.fileManager(self, shouldProceedAfterError: error, removingItemAtPath: path)
+        }
+    }
+    
+    private func shouldRemoveItemAtPath(_ path: String, isURL: Bool) -> Bool {
+        guard let delegate = self.delegate else { return true }
+        if isURL {
+            return delegate.fileManager(self, shouldRemoveItemAt: URL(fileURLWithPath: path))
+        } else {
+            return delegate.fileManager(self, shouldRemoveItemAtPath: path)
+        }
+    }
+    
+    private func _removeItem(atPath path: String, isURL: Bool, alreadyConfirmed: Bool = false) throws {
+        guard alreadyConfirmed || shouldRemoveItemAtPath(path, isURL: isURL) else {
+            return
+        }
+        
         if rmdir(path) == 0 {
             return
         } else if errno == ENOTEMPTY {
 
-            let fsRep = FileManager.default.fileSystemRepresentation(withPath: path)
-            let ps = UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>.allocate(capacity: 2)
-            ps.initialize(to: UnsafeMutablePointer(mutating: fsRep))
-            ps.advanced(by: 1).initialize(to: nil)
-            let stream = fts_open(ps, FTS_PHYSICAL | FTS_XDEV | FTS_NOCHDIR, nil)
-            ps.deinitialize(count: 2)
-            ps.deallocate()
-            fsRep.deallocate()
-
+            let stream = URL(fileURLWithPath: path).withUnsafeFileSystemRepresentation { (fsRep) -> UnsafeMutablePointer<FTS>? in
+                let ps = UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>.allocate(capacity: 2)
+                ps.initialize(to: UnsafeMutablePointer(mutating: fsRep))
+                ps.advanced(by: 1).initialize(to: nil)
+                defer {
+                    ps.deinitialize(count: 2)
+                    ps.deallocate()
+                }
+                return fts_open(ps, FTS_PHYSICAL | FTS_XDEV | FTS_NOCHDIR, nil)
+            }
+            
             if stream != nil {
                 defer {
                     fts_close(stream)
                 }
 
-                var current = fts_read(stream)
-                while current != nil {
-                    switch Int32(current!.pointee.fts_info) {
+                while let current = fts_read(stream)?.pointee {
+                    let itemPath = string(withFileSystemRepresentation: current.fts_path, length: Int(current.fts_pathlen))
+                    guard alreadyConfirmed || shouldRemoveItemAtPath(itemPath, isURL: isURL) else {
+                        continue
+                    }
+                    
+                    do {
+                        switch Int32(current.fts_info) {
                         case FTS_DEFAULT, FTS_F, FTS_NSOK, FTS_SL, FTS_SLNONE:
-                            if unlink(current!.pointee.fts_path) == -1 {
-                                let str = NSString(bytes: current!.pointee.fts_path, length: Int(strlen(current!.pointee.fts_path)), encoding: String.Encoding.utf8.rawValue)!._swiftObject
-                                throw _NSErrorWithErrno(errno, reading: false, path: str)
+                            if unlink(current.fts_path) == -1 {
+                                throw _NSErrorWithErrno(errno, reading: false, path: itemPath)
                             }
                         case FTS_DP:
-                            if rmdir(current!.pointee.fts_path) == -1 {
-                                let str = NSString(bytes: current!.pointee.fts_path, length: Int(strlen(current!.pointee.fts_path)), encoding: String.Encoding.utf8.rawValue)!._swiftObject
-                                throw _NSErrorWithErrno(errno, reading: false, path: str)
+                            if rmdir(current.fts_path) == -1 {
+                                throw _NSErrorWithErrno(errno, reading: false, path: itemPath)
                             }
+                        case FTS_DNR, FTS_ERR, FTS_NS:
+                            throw _NSErrorWithErrno(current.fts_errno, reading: false, path: itemPath)
                         default:
                             break
+                        }
+                    } catch {
+                        if !shouldProceedAfterError(error, removingItemAtPath: itemPath, isURL: isURL) {
+                            throw error
+                        }
                     }
-                    current = fts_read(stream)
                 }
             } else {
                 let _ = _NSErrorWithErrno(ENOTEMPTY, reading: false, path: path)
             }
-            // TODO: Error handling if fts_read fails.
-
         } else if errno != ENOTDIR {
             throw _NSErrorWithErrno(errno, reading: false, path: path)
         } else if _fileSystemRepresentation(withPath: path, { unlink($0) != 0 }) {

        }
    }

    private func _copySymlink(atPath srcPath: String, toPath dstPath: String) throws {
        let bufSize = Int(PATH_MAX) + 1
        var buf = [Int8](repeating: 0, count: bufSize)

        try _fileSystemRepresentation(withPath: srcPath) { srcFsRep in
            let len = readlink(srcFsRep, &buf, bufSize)
            if len < 0 {
                throw _NSErrorWithErrno(errno, reading: true, path: srcPath)
            }
            try _fileSystemRepresentation(withPath: dstPath) { dstFsRep in
                if symlink(buf, dstFsRep) == -1 {
                    throw _NSErrorWithErrno(errno, reading: false, path: dstPath)
                }
            }
        }
    }

    private func _copyOrLinkDirectoryHelper(atPath srcPath: String, toPath dstPath: String, _ body: (String, String, FileAttributeType) throws -> ()) throws {
        guard let stat = try? _lstatFile(atPath: srcPath) else {
                return
        }


extension FileManagerDelegate {
    func fileManager(_ fileManager: FileManager, shouldCopyItemAtPath srcPath: String, toPath dstPath: String) -> Bool { return true }
    func fileManager(_ fileManager: FileManager, shouldCopyItemAt srcURL: URL, to dstURL: URL) -> Bool { return true }

    func fileManager(_ fileManager: FileManager, shouldProceedAfterError error: Error, copyingItemAtPath srcPath: String, toPath dstPath: String) -> Bool { return false }
    func fileManager(_ fileManager: FileManager, shouldProceedAfterError error: Error, copyingItemAt srcURL: URL, to dstURL: URL) -> Bool { return false }

    func fileManager(_ fileManager: FileManager, shouldMoveItemAtPath srcPath: String, toPath dstPath: String) -> Bool { return true }
    func fileManager(_ fileManager: FileManager, shouldMoveItemAt srcURL: URL, to dstURL: URL) -> Bool { return true }

    func fileManager(_ fileManager: FileManager, shouldProceedAfterError error: Error, movingItemAtPath srcPath: String, toPath dstPath: String) -> Bool { return false }
    func fileManager(_ fileManager: FileManager, shouldProceedAfterError error: Error, movingItemAt srcURL: URL, to dstURL: URL) -> Bool { return false }

    func fileManager(_ fileManager: FileManager, shouldLinkItemAtPath srcPath: String, toPath dstPath: String) -> Bool { return true }
    func fileManager(_ fileManager: FileManager, shouldLinkItemAt srcURL: URL, to dstURL: URL) -> Bool { return true }

    func fileManager(_ fileManager: FileManager, shouldProceedAfterError error: Error, linkingItemAtPath srcPath: String, toPath dstPath: String) -> Bool { return false }
    func fileManager(_ fileManager: FileManager, shouldProceedAfterError error: Error, linkingItemAt srcURL: URL, to dstURL: URL) -> Bool { return false }

    func fileManager(_ fileManager: FileManager, shouldRemoveItemAtPath path: String) -> Bool { return true }
    func fileManager(_ fileManager: FileManager, shouldRemoveItemAt URL: URL) -> Bool { return true }

    func fileManager(_ fileManager: FileManager, shouldProceedAfterError error: Error, removingItemAtPath path: String) -> Bool { return false }
    func fileManager(_ fileManager: FileManager, shouldProceedAfterError error: Error, removingItemAt URL: URL) -> Bool { return false }
}

extension FileManager {
