@@ -125,12 +125,18 @@ private func writeToFileDescriptorWithProgress(_ fd: Int32, buffer: UnsafeRawBuf
125
125
126
126
private func cleanupTemporaryDirectory( at inPath: String ? ) {
127
127
guard let inPath else { return }
128
+ #if canImport(Darwin) || os(Linux)
129
+ // Since we expect the directory to be empty at this point, try rmdir which is much faster than Darwin's removefile(3) for known empty directories
130
+ if inPath. withFileSystemRepresentation ( { $0. flatMap ( rmdir) } ) == 0 {
131
+ return
132
+ }
133
+ #endif
128
134
// Attempt to use FileManager, ignore error
129
135
try ? FileManager . default. removeItem ( atPath: inPath)
130
136
}
131
137
132
138
/// Caller is responsible for calling `close` on the `Int32` file descriptor.
133
- private func createTemporaryFile( at destinationPath: String , inPath: PathOrURL , prefix: String , options: Data . WritingOptions ) throws -> ( Int32 , String ) {
139
+ private func createTemporaryFile( at destinationPath: String , inPath: PathOrURL , prefix: String , options: Data . WritingOptions , variant : String ? = nil ) throws -> ( Int32 , String ) {
134
140
#if os(WASI)
135
141
// WASI does not have temp directories
136
142
throw CocoaError ( . featureUnsupported)
@@ -154,12 +160,12 @@ private func createTemporaryFile(at destinationPath: String, inPath: PathOrURL,
154
160
// Furthermore, we can't compatibly switch to mkstemp() until we have the ability to set fchmod correctly, which requires the ability to query the current umask, which we don't have. (22033100)
155
161
#if os(Windows)
156
162
guard _mktemp_s ( templateFileSystemRep, template. count + 1 ) == 0 else {
157
- throw CocoaError . errorWithFilePath ( inPath, errno: errno, reading: false )
163
+ throw CocoaError . errorWithFilePath ( inPath, errno: errno, reading: false , variant : variant )
158
164
}
159
165
let flags : CInt = _O_BINARY | _O_CREAT | _O_EXCL | _O_RDWR
160
166
#else
161
167
guard mktemp ( templateFileSystemRep) != nil else {
162
- throw CocoaError . errorWithFilePath ( inPath, errno: errno, reading: false )
168
+ throw CocoaError . errorWithFilePath ( inPath, errno: errno, reading: false , variant : variant )
163
169
}
164
170
let flags : CInt = O_CREAT | O_EXCL | O_RDWR
165
171
#endif
@@ -172,7 +178,7 @@ private func createTemporaryFile(at destinationPath: String, inPath: PathOrURL,
172
178
173
179
// If the file exists, we repeat. Otherwise throw the error.
174
180
if errno != EEXIST {
175
- throw CocoaError . errorWithFilePath ( inPath, errno: errno, reading: false )
181
+ throw CocoaError . errorWithFilePath ( inPath, errno: errno, reading: false , variant : variant )
176
182
}
177
183
178
184
// Try again
@@ -194,12 +200,25 @@ private func createTemporaryFile(at destinationPath: String, inPath: PathOrURL,
194
200
195
201
/// Returns `(file descriptor, temporary file path, temporary directory path)`
196
202
/// Caller is responsible for calling `close` on the `Int32` file descriptor and calling `cleanupTemporaryDirectory` on the temporary directory path. The temporary directory path may be nil, if it does not need to be cleaned up.
197
- private func createProtectedTemporaryFile( at destinationPath: String , inPath: PathOrURL , options: Data . WritingOptions ) throws -> ( Int32 , String , String ? ) {
203
+ private func createProtectedTemporaryFile( at destinationPath: String , inPath: PathOrURL , options: Data . WritingOptions , variant : String ? = nil ) throws -> ( Int32 , String , String ? ) {
198
204
#if FOUNDATION_FRAMEWORK
199
205
if _foundation_sandbox_check ( getpid ( ) , nil ) != 0 {
200
206
// Convert the path back into a string
201
207
let url = URL ( fileURLWithPath: destinationPath, isDirectory: false )
202
- let temporaryDirectoryPath = try FileManager . default. url ( for: . itemReplacementDirectory, in: . userDomainMask, appropriateFor: url, create: true ) . path ( percentEncoded: false )
208
+ var temporaryDirectoryPath : String
209
+ do {
210
+ temporaryDirectoryPath = try FileManager . default. url ( for: . itemReplacementDirectory, in: . userDomainMask, appropriateFor: url, create: true ) . path ( percentEncoded: false )
211
+ } catch {
212
+ if let variant, let cocoaError = error as? CocoaError {
213
+ let code = cocoaError. code
214
+ var userInfo = cocoaError. userInfo
215
+ userInfo [ NSUserStringVariantErrorKey] = variant
216
+
217
+ throw CocoaError ( code, userInfo: userInfo)
218
+ } else {
219
+ throw error
220
+ }
221
+ }
203
222
204
223
let auxFile = temporaryDirectoryPath. appendingPathComponent ( destinationPath. lastPathComponent)
205
224
return try auxFile. withFileSystemRepresentation { auxFileFileSystemRep in
@@ -212,14 +231,14 @@ private func createProtectedTemporaryFile(at destinationPath: String, inPath: Pa
212
231
} else {
213
232
let savedErrno = errno
214
233
cleanupTemporaryDirectory ( at: temporaryDirectoryPath)
215
- throw CocoaError . errorWithFilePath ( inPath, errno: savedErrno, reading: false )
234
+ throw CocoaError . errorWithFilePath ( inPath, errno: savedErrno, reading: false , variant : variant )
216
235
}
217
236
}
218
237
}
219
238
#endif
220
239
221
240
let temporaryDirectoryPath = destinationPath. deletingLastPathComponent ( )
222
- let ( fd, auxFile) = try createTemporaryFile ( at: temporaryDirectoryPath, inPath: inPath, prefix: " .dat.nosync " , options: options)
241
+ let ( fd, auxFile) = try createTemporaryFile ( at: temporaryDirectoryPath, inPath: inPath, prefix: " .dat.nosync " , options: options, variant : variant )
223
242
return ( fd, auxFile, nil )
224
243
}
225
244
@@ -310,25 +329,7 @@ private func writeToFileAux(path inPath: PathOrURL, buffer: UnsafeRawBufferPoint
310
329
311
330
#if os(Windows)
312
331
try inPath. path. withNTPathRepresentation { pwszPath in
313
- var fd : CInt
314
- var auxPath : String ?
315
- var temporaryDirectoryPath : String ?
316
-
317
- do {
318
- ( fd, auxPath, temporaryDirectoryPath) = try createProtectedTemporaryFile ( at: inPath. path, inPath: inPath, options: options)
319
- } catch {
320
- if let cocoaError = error as? CocoaError {
321
- // Extract code and userInfo, then re-create it with an additional userInfo key.
322
- let code = cocoaError. code
323
- var userInfo = cocoaError. userInfo
324
- userInfo [ NSUserStringVariantErrorKey] = " Folder "
325
-
326
- throw CocoaError ( code, userInfo: userInfo)
327
- } else {
328
- // These should all be CocoaErrors, but just in case we re-throw the original one here.
329
- throw error
330
- }
331
- }
332
+ var ( fd, auxPath, temporaryDirectoryPath) = try createProtectedTemporaryFile ( at: inPath. path, inPath: inPath, options: options, variant: " Folder " )
332
333
333
334
// Cleanup temporary directory
334
335
defer { cleanupTemporaryDirectory ( at: temporaryDirectoryPath) }
@@ -344,10 +345,8 @@ private func writeToFileAux(path inPath: PathOrURL, buffer: UnsafeRawBufferPoint
344
345
do {
345
346
try write ( buffer: buffer, toFileDescriptor: fd, path: inPath, parentProgress: callback)
346
347
} catch {
347
- if let auxPath {
348
- try auxPath. withNTPathRepresentation { pwszAuxPath in
349
- _ = DeleteFileW ( pwszAuxPath)
350
- }
348
+ try auxPath. withNTPathRepresentation { pwszAuxPath in
349
+ _ = DeleteFileW ( pwszAuxPath)
351
350
}
352
351
353
352
if callback? . isCancelled ?? false {
@@ -359,9 +358,6 @@ private func writeToFileAux(path inPath: PathOrURL, buffer: UnsafeRawBufferPoint
359
358
360
359
writeExtendedAttributes ( fd: fd, attributes: attributes)
361
360
362
- // We're done now
363
- guard let auxPath else { return }
364
-
365
361
_close ( fd)
366
362
fd = - 1
367
363
@@ -379,10 +375,7 @@ private func writeToFileAux(path inPath: PathOrURL, buffer: UnsafeRawBufferPoint
379
375
throw CocoaError ( . fileWriteInvalidFileName)
380
376
}
381
377
382
- let fd : Int32
383
378
var mode : mode_t ?
384
- var temporaryDirectoryPath : String ?
385
- var auxPath : String ?
386
379
387
380
#if FOUNDATION_FRAMEWORK
388
381
var newPath = inPath. path
@@ -410,21 +403,7 @@ private func writeToFileAux(path inPath: PathOrURL, buffer: UnsafeRawBufferPoint
410
403
let newPath = inPath. path
411
404
#endif
412
405
413
- do {
414
- ( fd, auxPath, temporaryDirectoryPath) = try createProtectedTemporaryFile ( at: newPath, inPath: inPath, options: options)
415
- } catch {
416
- if let cocoaError = error as? CocoaError {
417
- // Extract code and userInfo, then re-create it with an additional userInfo key.
418
- let code = cocoaError. code
419
- var userInfo = cocoaError. userInfo
420
- userInfo [ NSUserStringVariantErrorKey] = " Folder "
421
-
422
- throw CocoaError ( code, userInfo: userInfo)
423
- } else {
424
- // These should all be CocoaErrors, but just in case we re-throw the original one here.
425
- throw error
426
- }
427
- }
406
+ var ( fd, auxPath, temporaryDirectoryPath) = try createProtectedTemporaryFile ( at: newPath, inPath: inPath, options: options, variant: " Folder " )
428
407
429
408
guard fd >= 0 else {
430
409
let savedErrno = errno
@@ -442,11 +421,9 @@ private func writeToFileAux(path inPath: PathOrURL, buffer: UnsafeRawBufferPoint
442
421
} catch {
443
422
let savedError = errno
444
423
445
- if let auxPath {
446
- auxPath. withFileSystemRepresentation { pathFileSystemRep in
447
- guard let pathFileSystemRep else { return }
448
- unlink ( pathFileSystemRep)
449
- }
424
+ auxPath. withFileSystemRepresentation { pathFileSystemRep in
425
+ guard let pathFileSystemRep else { return }
426
+ unlink ( pathFileSystemRep)
450
427
}
451
428
cleanupTemporaryDirectory ( at: temporaryDirectoryPath)
452
429
@@ -458,11 +435,6 @@ private func writeToFileAux(path inPath: PathOrURL, buffer: UnsafeRawBufferPoint
458
435
}
459
436
460
437
writeExtendedAttributes ( fd: fd, attributes: attributes)
461
-
462
- guard let auxPath else {
463
- // We're done now
464
- return
465
- }
466
438
467
439
try auxPath. withFileSystemRepresentation { auxPathFileSystemRep in
468
440
guard let auxPathFileSystemRep else {
0 commit comments