Skip to content

Commit 4b5355f

Browse files
Merge branch 'main' into feat/nioFoundationCompatWASICompilation
2 parents 6934864 + d963335 commit 4b5355f

File tree

5 files changed

+123
-36
lines changed

5 files changed

+123
-36
lines changed

Sources/NIOFS/FileSystem.swift

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -232,12 +232,27 @@ public struct FileSystem: Sendable, FileSystemProtocol {
232232
at path: NIOFilePath,
233233
withIntermediateDirectories createIntermediateDirectories: Bool,
234234
permissions: FilePermissions?
235+
) async throws {
236+
try await self.createDirectory(
237+
at: path,
238+
withIntermediateDirectories: createIntermediateDirectories,
239+
permissions: permissions,
240+
idempotent: true
241+
)
242+
}
243+
244+
private func createDirectory(
245+
at path: NIOFilePath,
246+
withIntermediateDirectories createIntermediateDirectories: Bool,
247+
permissions: FilePermissions?,
248+
idempotent: Bool
235249
) async throws {
236250
try await self.threadPool.runIfActive {
237251
try self._createDirectory(
238252
at: path.underlying,
239253
withIntermediateDirectories: createIntermediateDirectories,
240-
permissions: permissions ?? .defaultsForDirectory
254+
permissions: permissions ?? .defaultsForDirectory,
255+
idempotent: idempotent
241256
).get()
242257
}
243258
}
@@ -795,7 +810,8 @@ extension FileSystem {
795810
private func _createDirectory(
796811
at fullPath: FilePath,
797812
withIntermediateDirectories createIntermediateDirectories: Bool,
798-
permissions: FilePermissions
813+
permissions: FilePermissions,
814+
idempotent: Bool = true
799815
) -> Result<Void, FileSystemError> {
800816
// We assume that we will be creating intermediate directories:
801817
// - Try creating the directory. If it fails with ENOENT (no such file or directory), then
@@ -826,16 +842,20 @@ extension FileSystem {
826842

827843
case let .failure(errno):
828844
if errno == .fileExists {
829-
switch self._info(forFileAt: path, infoAboutSymbolicLink: false) {
830-
case let .success(maybeInfo):
831-
if let info = maybeInfo, info.type == .directory {
832-
break loop
833-
} else {
834-
// A file exists at this path.
845+
if idempotent {
846+
switch self._info(forFileAt: path, infoAboutSymbolicLink: false) {
847+
case let .success(maybeInfo):
848+
if let info = maybeInfo, info.type == .directory {
849+
break loop
850+
} else {
851+
// A file exists at this path.
852+
return .failure(.mkdir(errno: errno, path: path, location: .here()))
853+
}
854+
case .failure:
855+
// Unable to determine what exists at this path.
835856
return .failure(.mkdir(errno: errno, path: path, location: .here()))
836857
}
837-
case .failure:
838-
// Unable to determine what exists at this path.
858+
} else {
839859
return .failure(.mkdir(errno: errno, path: path, location: .here()))
840860
}
841861
}
@@ -930,7 +950,8 @@ extension FileSystem {
930950
try await self.createDirectory(
931951
at: NIOFilePath(destinationPath),
932952
withIntermediateDirectories: false,
933-
permissions: info.permissions
953+
permissions: info.permissions,
954+
idempotent: false // Fail if the destination dir already exists.
934955
)
935956

936957
#if !os(Android)
@@ -1165,13 +1186,19 @@ extension FileSystem {
11651186
}
11661187

11671188
case .directory:
1168-
let addToQueue = try await self.prepareDirectoryForRecusiveCopy(
1169-
from: from.path.underlying,
1170-
to: to,
1171-
shouldProceedAfterError: shouldProceedAfterError,
1172-
shouldCopyItem: shouldCopyItem
1173-
)
1174-
yield(addToQueue)
1189+
do {
1190+
let addToQueue = try await self.prepareDirectoryForRecusiveCopy(
1191+
from: from.path.underlying,
1192+
to: to,
1193+
shouldProceedAfterError: shouldProceedAfterError,
1194+
shouldCopyItem: shouldCopyItem
1195+
)
1196+
yield(addToQueue)
1197+
} catch {
1198+
// The caller expects an end-of-dir regardless of whether there was an error or not.
1199+
yield([.endOfDir])
1200+
try await shouldProceedAfterError(from, error)
1201+
}
11751202

11761203
default:
11771204
let error = FileSystemError(

Sources/NIOFS/Internal/ParallelDirCopy.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ extension FileSystem {
126126
// the latter case we choose to propagate the cancellation clearly. This makes
127127
// testing for it more reliable.
128128
try Task.checkCancellation()
129+
130+
// If any child tasks failed throw up an error.
131+
try await taskGroup.waitForAll()
132+
129133
return
130134
}
131135
}

Sources/_NIOFileSystem/FileSystem.swift

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -232,12 +232,27 @@ public struct FileSystem: Sendable, FileSystemProtocol {
232232
at path: FilePath,
233233
withIntermediateDirectories createIntermediateDirectories: Bool,
234234
permissions: FilePermissions?
235+
) async throws {
236+
try await self.createDirectory(
237+
at: path,
238+
withIntermediateDirectories: createIntermediateDirectories,
239+
permissions: permissions,
240+
idempotent: true
241+
)
242+
}
243+
244+
private func createDirectory(
245+
at path: FilePath,
246+
withIntermediateDirectories createIntermediateDirectories: Bool,
247+
permissions: FilePermissions?,
248+
idempotent: Bool
235249
) async throws {
236250
try await self.threadPool.runIfActive {
237251
try self._createDirectory(
238252
at: path,
239253
withIntermediateDirectories: createIntermediateDirectories,
240-
permissions: permissions ?? .defaultsForDirectory
254+
permissions: permissions ?? .defaultsForDirectory,
255+
idempotent: idempotent
241256
).get()
242257
}
243258
}
@@ -809,7 +824,8 @@ extension FileSystem {
809824
private func _createDirectory(
810825
at fullPath: FilePath,
811826
withIntermediateDirectories createIntermediateDirectories: Bool,
812-
permissions: FilePermissions
827+
permissions: FilePermissions,
828+
idempotent: Bool = true
813829
) -> Result<Void, FileSystemError> {
814830
// We assume that we will be creating intermediate directories:
815831
// - Try creating the directory. If it fails with ENOENT (no such file or directory), then
@@ -840,16 +856,20 @@ extension FileSystem {
840856

841857
case let .failure(errno):
842858
if errno == .fileExists {
843-
switch self._info(forFileAt: path, infoAboutSymbolicLink: false) {
844-
case let .success(maybeInfo):
845-
if let info = maybeInfo, info.type == .directory {
846-
break loop
847-
} else {
848-
// A file exists at this path.
859+
if idempotent {
860+
switch self._info(forFileAt: path, infoAboutSymbolicLink: false) {
861+
case let .success(maybeInfo):
862+
if let info = maybeInfo, info.type == .directory {
863+
break loop
864+
} else {
865+
// A file exists at this path.
866+
return .failure(.mkdir(errno: errno, path: path, location: .here()))
867+
}
868+
case .failure:
869+
// Unable to determine what exists at this path.
849870
return .failure(.mkdir(errno: errno, path: path, location: .here()))
850871
}
851-
case .failure:
852-
// Unable to determine what exists at this path.
872+
} else {
853873
return .failure(.mkdir(errno: errno, path: path, location: .here()))
854874
}
855875
}
@@ -944,7 +964,8 @@ extension FileSystem {
944964
try await self.createDirectory(
945965
at: destinationPath,
946966
withIntermediateDirectories: false,
947-
permissions: info.permissions
967+
permissions: info.permissions,
968+
idempotent: false // Fail if the destination dir already exists.
948969
)
949970

950971
#if !os(Android)
@@ -1179,13 +1200,19 @@ extension FileSystem {
11791200
}
11801201

11811202
case .directory:
1182-
let addToQueue = try await self.prepareDirectoryForRecusiveCopy(
1183-
from: from.path,
1184-
to: to,
1185-
shouldProceedAfterError: shouldProceedAfterError,
1186-
shouldCopyItem: shouldCopyItem
1187-
)
1188-
yield(addToQueue)
1203+
do {
1204+
let addToQueue = try await self.prepareDirectoryForRecusiveCopy(
1205+
from: from.path,
1206+
to: to,
1207+
shouldProceedAfterError: shouldProceedAfterError,
1208+
shouldCopyItem: shouldCopyItem
1209+
)
1210+
yield(addToQueue)
1211+
} catch {
1212+
// The caller expects an end-of-dir regardless of whether there was an error or not.
1213+
yield([.endOfDir])
1214+
try await shouldProceedAfterError(from, error)
1215+
}
11891216

11901217
default:
11911218
let error = FileSystemError(

Sources/_NIOFileSystem/Internal/ParallelDirCopy.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,10 @@ extension FileSystem {
126126
// the latter case we choose to propagate the cancellation clearly. This makes
127127
// testing for it more reliable.
128128
try Task.checkCancellation()
129+
130+
// If any child tasks failed throw up an error.
131+
try await taskGroup.waitForAll()
132+
129133
return
130134
}
131135
}

Tests/NIOFSIntegrationTests/FileSystemTests.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,31 @@ final class FileSystemTests: XCTestCase {
717717
try await self.checkDirectoriesMatch(path, copy)
718718
}
719719

720+
func testCopyDirectoryToExistingDestinationSequential() async throws {
721+
try await self.testCopyDirectoryToExistingDestination(.sequential)
722+
}
723+
724+
func testCopyDirectoryToExistingDestinationParallelMinimal() async throws {
725+
try await self.testCopyDirectoryToExistingDestination(Self.minimalParallel)
726+
}
727+
728+
func testCopyDirectoryToExistingDestinationParallelDefault() async throws {
729+
try await self.testCopyDirectoryToExistingDestination(.platformDefault)
730+
}
731+
732+
private func testCopyDirectoryToExistingDestination(
733+
_ strategy: CopyStrategy
734+
) async throws {
735+
let path1 = try await self.fs.temporaryFilePath()
736+
let path2 = try await self.fs.temporaryFilePath()
737+
try await self.fs.createDirectory(at: path1, withIntermediateDirectories: false)
738+
try await self.fs.createDirectory(at: path2, withIntermediateDirectories: false)
739+
740+
await XCTAssertThrowsErrorAsync {
741+
try await self.fs.copyItem(at: path1, to: path2, strategy: strategy)
742+
}
743+
}
744+
720745
func testCopyOnGeneratedTreeStructureSequential() async throws {
721746
try await testAnyCopyStrategyOnGeneratedTreeStructure(.sequential)
722747
}

0 commit comments

Comments
 (0)