diff --git a/.dockerignore b/.dockerignore index e43b0f9..79b5594 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1 +1 @@ -.DS_Store +**/.DS_Store diff --git a/Sources/SMBClient/FileWriter.swift b/Sources/SMBClient/FileWriter.swift index 572885d..1d025f9 100644 --- a/Sources/SMBClient/FileWriter.swift +++ b/Sources/SMBClient/FileWriter.swift @@ -8,7 +8,7 @@ public class FileWriter { init(session: Session, path: String) { self.session = session - self.path = path + self.path = path.precomposedStringWithCanonicalMapping } public func upload(data: Data) async throws { diff --git a/Sources/SMBClient/Messages/ErrorResponse.swift b/Sources/SMBClient/Messages/Errors/ErrorResponse.swift similarity index 100% rename from Sources/SMBClient/Messages/ErrorResponse.swift rename to Sources/SMBClient/Messages/Errors/ErrorResponse.swift diff --git a/Sources/SMBClient/Messages/NTStatus.swift b/Sources/SMBClient/Messages/Errors/NTStatus.swift similarity index 95% rename from Sources/SMBClient/Messages/NTStatus.swift rename to Sources/SMBClient/Messages/Errors/NTStatus.swift index 338e2ed..f1f2ebc 100644 --- a/Sources/SMBClient/Messages/NTStatus.swift +++ b/Sources/SMBClient/Messages/Errors/NTStatus.swift @@ -77,6 +77,8 @@ extension NTStatus: CustomStringConvertible { return "The network name specified by the client has been deleted on the server. This error is returned if the client specifies an incorrect TID or the share on the server represented by the TID was deleted." case .badNetworkName: return "The specified share name cannot be found on the remote server." + case .directoryNotEmpty: + return "Indicates that the directory trying to be deleted is not empty." case .notADirectory: return "A requested opened file is not a directory." case .fileClosed: @@ -87,6 +89,8 @@ extension NTStatus: CustomStringConvertible { return "The transport-connection attempt was refused by the remote system." case .networkSessionExpired: return "The client's session has expired; therefore, the client MUST re-authenticate to continue accessing remote resources." + case .fileSystemLimitation: + return "The requested operation could not be completed due to a file system limitation." case .smbTooManyUIDs: return "The client has requested too many UID values from the server or the client already has an SMB session setup with this UID value." default: @@ -164,6 +168,8 @@ extension NTStatus: CustomDebugStringConvertible { return "NETWORK_NAME_DELETED" case .badNetworkName: return "BAD_NETWORK_NAME" + case .directoryNotEmpty: + return "DIRECTORY_NOT_EMPTY" case .notADirectory: return "NOT_A_DIRECTORY" case .fileClosed: @@ -174,6 +180,8 @@ extension NTStatus: CustomDebugStringConvertible { return "CONNECTION_REFUSED" case .networkSessionExpired: return "NETWORK_SESSION_EXPIRED" + case .fileSystemLimitation: + return "FILE_SYSTEM_LIMITATION" case .smbTooManyUIDs: return "SMB_TOO_MANY_UIDS" default: @@ -216,11 +224,13 @@ public enum ErrorCode: UInt32 { case notSupported = 0xC00000BB case networkNameDeleted = 0xC00000C9 case badNetworkName = 0xC00000CC + case directoryNotEmpty = 0xC0000101 case notADirectory = 0xC0000103 case fileClosed = 0xC0000128 case userSessionDeleted = 0xC0000203 case connectionRefused = 0xC0000236 case networkSessionExpired = 0xC000035C + case fileSystemLimitation = 0xC0000427 case smbTooManyUIDs = 0xC000205A } diff --git a/Sources/SMBClient/Messages/Header+CustomDebugStringConvertible.swift b/Sources/SMBClient/Messages/Header+CustomDebugStringConvertible.swift index c90d829..c16bc37 100644 --- a/Sources/SMBClient/Messages/Header+CustomDebugStringConvertible.swift +++ b/Sources/SMBClient/Messages/Header+CustomDebugStringConvertible.swift @@ -80,7 +80,7 @@ extension Header.Command: CustomDebugStringConvertible { extension Header.Flags: CustomDebugStringConvertible { public var debugDescription: String { - var values: [String] = [] + var values = [String]() if contains(.serverToRedir) { values.append("Response") diff --git a/Sources/SMBClient/SMBClient.swift b/Sources/SMBClient/SMBClient.swift index 2b2cf8f..c5ad5b2 100644 --- a/Sources/SMBClient/SMBClient.swift +++ b/Sources/SMBClient/SMBClient.swift @@ -79,7 +79,7 @@ public class SMBClient { } public func createDirectory(path: String) async throws { - try await session.createDirectory(path: Pathname.normalize(path)) + try await session.createDirectory(path: Pathname.normalize(path.precomposedStringWithCanonicalMapping)) } public func rename(from: String, to: String) async throws { @@ -87,7 +87,7 @@ public class SMBClient { } public func move(from: String, to: String) async throws { - try await session.move(from: Pathname.normalize(from), to: Pathname.normalize(to)) + try await session.move(from: Pathname.normalize(from), to: Pathname.normalize(to.precomposedStringWithCanonicalMapping)) } public func deleteDirectory(path: String) async throws { diff --git a/Sources/SMBClient/Session.swift b/Sources/SMBClient/Session.swift index 38bfe0b..3583011 100644 --- a/Sources/SMBClient/Session.swift +++ b/Sources/SMBClient/Session.swift @@ -131,7 +131,7 @@ public class Session { let response = Logoff.Response(data: data) sessionId = 0 - + return response } @@ -461,56 +461,12 @@ public class Session { shareAccess: [.read, .write, .delete], createDisposition: .create, createOptions: [.directoryFile], - name: path + name: path.precomposedStringWithCanonicalMapping ) try await close(fileId: response.fileId) return response } - public func copy(path source: String, path dest: String) async throws { - let createResponse = try await create( - desiredAccess: [.genericRead], - fileAttributes: [], - shareAccess: [.read], - createDisposition: .open, - createOptions: [], - name: source - ) - - let creditSize = creditSize(size: maxReadSize) - let request = Read.Request( - creditCharge: creditSize, - messageId: messageId.next(count: UInt64(creditSize)), - treeId: treeId, - sessionId: sessionId, - fileId: createResponse.fileId, - offset: 0, - length: maxReadSize - ) - - var buffer = Data() - var response = Read.Response(data: try await send(request.encoded())) - buffer.append(response.buffer) - - while NTStatus(response.header.status) != .endOfFile { - let request = Read.Request( - creditCharge: creditSize, - messageId: messageId.next(count: UInt64(creditSize)), - treeId: treeId, - sessionId: sessionId, - fileId: createResponse.fileId, - offset: UInt64(buffer.count), - length: maxReadSize - ) - - let data = try await send(request.encoded()) - response = Read.Response(data: data) - buffer.append(response.buffer) - } - - try await close(fileId: createResponse.fileId) - } - public func deleteDirectory(path: String) async throws { let files = try await queryDirectory(path: path, pattern: "*") for file in files { @@ -616,7 +572,7 @@ public class Session { sessionId: sessionId, fileId: temporaryUUID, infoType: .file, - fileInformation: FileRenameInformation(fileName: to) + fileInformation: FileRenameInformation(fileName: to.precomposedStringWithCanonicalMapping) ) let closeRequest = Close.Request( headerFlags: [.relatedOperations],