Signal-iOS/SignalServiceKit/src/Util/OWSFileSystem.swift
2022-03-02 09:26:58 +01:00

147 lines
5.3 KiB
Swift

//
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
//
import Foundation
@objc
public extension OWSFileSystem {
class func fileOrFolderExists(atPath filePath: String) -> Bool {
FileManager.default.fileExists(atPath: filePath)
}
class func fileOrFolderExists(url: URL) -> Bool {
fileOrFolderExists(atPath: url.path)
}
@discardableResult
class func deleteFile(_ filePath: String) -> Bool {
deleteFile(filePath, ignoreIfMissing: false)
}
@discardableResult
class func deleteFileIfExists(_ filePath: String) -> Bool {
return deleteFile(filePath, ignoreIfMissing: true)
}
class func deleteFile(url: URL) throws {
try FileManager.default.removeItem(at: url)
}
class func deleteFileIfExists(url: URL) throws {
guard FileManager.default.fileExists(atPath: url.path) else {
return
}
try deleteFile(url: url)
}
class func moveFile(from fromUrl: URL, to toUrl: URL) throws {
guard FileManager.default.fileExists(atPath: fromUrl.path) else {
throw OWSAssertionError("Source file does not exist.")
}
guard !FileManager.default.fileExists(atPath: toUrl.path) else {
throw OWSAssertionError("Destination file already exists.")
}
try FileManager.default.moveItem(at: fromUrl, to: toUrl)
// Ensure all files moved have the proper data protection class.
// On large directories this can take a while, so we dispatch async
// since we're in the launch path.
DispatchQueue.global().async {
self.protectRecursiveContents(atPath: toUrl.path)
}
#if TESTABLE_BUILD
guard !FileManager.default.fileExists(atPath: fromUrl.path) else {
throw OWSAssertionError("Source file does not exist.")
}
guard FileManager.default.fileExists(atPath: toUrl.path) else {
throw OWSAssertionError("Destination file already exists.")
}
#endif
}
class func recursiveFilesInDirectory(_ dirPath: String) throws -> [String] {
owsAssertDebug(dirPath.count > 0)
do {
return try FileManager.default.subpathsOfDirectory(atPath: dirPath)
.map { (dirPath as NSString).appendingPathComponent($0) }
.filter {
var isDirectory: ObjCBool = false
FileManager.default.fileExists(atPath: $0, isDirectory: &isDirectory)
return !isDirectory.boolValue
}
} catch {
let nsError = error as NSError
let isCocoaNoSuchFileError = (nsError.domain == NSCocoaErrorDomain && nsError.code == NSFileReadNoSuchFileError)
if isCocoaNoSuchFileError {
return []
} else {
throw error
}
}
}
// MARK: - Temporary Files
class func temporaryFileUrl(fileExtension: String? = nil,
isAvailableWhileDeviceLocked: Bool = false) -> URL {
return URL(fileURLWithPath: temporaryFilePath(fileExtension: fileExtension,
isAvailableWhileDeviceLocked: isAvailableWhileDeviceLocked))
}
class func temporaryFilePath(fileExtension: String? = nil) -> String {
temporaryFilePath(fileExtension: fileExtension, isAvailableWhileDeviceLocked: false)
}
class func temporaryFilePath(fileExtension: String? = nil,
isAvailableWhileDeviceLocked: Bool = false) -> String {
let tempDirPath = (isAvailableWhileDeviceLocked
? OWSTemporaryDirectoryAccessibleAfterFirstAuth()
: OWSTemporaryDirectory())
var fileName = UUID().uuidString
if let fileExtension = fileExtension,
!fileExtension.isEmpty {
fileName = String(format: "\(fileName).\(fileExtension)")
}
let filePath = (tempDirPath as NSString).appendingPathComponent(fileName)
return filePath
}
}
// MARK: -
public extension OWSFileSystem {
@objc
class func deleteFile(_ filePath: String, ignoreIfMissing: Bool = false) -> Bool {
do {
try FileManager.default.removeItem(atPath: filePath)
return true
} catch {
let nsError = error as NSError
let isPosixNoSuchFileError = (nsError.domain == NSPOSIXErrorDomain && nsError.code == ENOENT)
let isCocoaNoSuchFileError = (nsError.domain == NSCocoaErrorDomain && nsError.code == NSFileNoSuchFileError)
if ignoreIfMissing,
isPosixNoSuchFileError || isCocoaNoSuchFileError {
// Ignore "No such file or directory" error.
return true
} else if nsError.hasDomain(NSCocoaErrorDomain, code: NSFileWriteNoPermissionError) {
let attemptedUrl = URL(fileURLWithPath: filePath)
let knownNoWritePermissionUrls = [
OWSFileSystem.appSharedDataDirectoryURL().appendingPathComponent(".com.apple.mobile_container_manager.metadata.plist")
]
owsAssertDebug(knownNoWritePermissionUrls.contains(attemptedUrl))
return false
} else {
owsFailDebug("Error: \(error)")
}
return false
}
}
}