diff --git a/AMSMB2/AMSMB2.swift b/AMSMB2/AMSMB2.swift index 5487d96..e38ff0b 100644 --- a/AMSMB2/AMSMB2.swift +++ b/AMSMB2/AMSMB2.swift @@ -29,6 +29,8 @@ public class AMSMB2: NSObject, NSSecureCoding, Codable, NSCopying, CustomReflect fileprivate var connectedShare: String? fileprivate let connectLock = NSLock() + fileprivate let operationLock = NSCondition() + fileprivate var operationCount: Int = 0 /** The timeout interval to use when doing an operation until getting response. Default value is 60 seconds. @@ -210,7 +212,7 @@ public class AMSMB2: NSObject, NSSecureCoding, Codable, NSCopying, CustomReflect self.connectedShare = name } - q.async { + queue { do { self.connectLock.lock() defer { self.connectLock.unlock() } @@ -235,22 +237,23 @@ public class AMSMB2: NSObject, NSSecureCoding, Codable, NSCopying, CustomReflect /** Disconnects from a share. - - Important: Disconnecting when an operation is in progress may cause disgraceful termination of operation. - */ - @objc(disconnectShare) - open func disconnectShare() { - self.disconnectShare(completionHandler: nil) - } - - /** - Disconnects from a share. + - Parameters: + - gracefully: waits until all queued operations are done before disconnecting from server. Default value is false. + - completionHandler: closure will be run after enumerating is completed. - Important: Disconnecting when an operation is in progress may cause disgraceful termination of operation. */ - @objc(disconnectShareWithCompletionHandler:) - open func disconnectShare(completionHandler: SimpleCompletionHandler) { - q.async { + @objc(disconnectShareGraceFully:completionHandler:) + open func disconnectShare(gracefully: Bool = false, completionHandler: SimpleCompletionHandler = nil) { + queue { do { + if gracefully { + self.operationLock.lock() + while self.operationCount > 0 { + self.operationLock.wait() + } + self.operationLock.unlock() + } self.connectLock.lock() defer { self.connectLock.unlock() } try self.context?.disconnect() @@ -268,7 +271,7 @@ public class AMSMB2: NSObject, NSSecureCoding, Codable, NSCopying, CustomReflect */ @objc(echoWithCompletionHandler:) open func echo(completionHandler: SimpleCompletionHandler) { - q.async { + queue { do { try self.context?.echo() completionHandler?(nil) @@ -290,7 +293,7 @@ public class AMSMB2: NSObject, NSSecureCoding, Codable, NSCopying, CustomReflect */ open func listShares(enumerateHidden: Bool = false, completionHandler: @escaping (_ result: Result<[(name: String, comment: String)], Error>) -> Void) { - q.async { + queue { do { // We use separate context because when a context connects to a tree, it won't connect to another tree. let context = try SMB2Context(timeout: self.timeout) @@ -304,9 +307,9 @@ public class AMSMB2: NSObject, NSSecureCoding, Codable, NSCopying, CustomReflect var shares = try context.shareEnum() if enumerateHidden { - shares = shares.filter { $0.type & 0x0fffffff == SHARE_TYPE_DISKTREE } + shares = shares.filter { $0.props.type == .diskTree } } else { - shares = shares.filter { $0.type == SHARE_TYPE_DISKTREE } + shares = shares.filter { !$0.props.isHidden && $0.props.type == .diskTree } } let result = shares.map { ($0.name, $0.comment) } completionHandler(.success(result)) @@ -327,7 +330,7 @@ public class AMSMB2: NSObject, NSSecureCoding, Codable, NSCopying, CustomReflect */ open func contentsOfDirectory(atPath path: String, recursive: Bool = false, completionHandler: @escaping (_ result: Result<[[URLResourceKey: Any]], Error>) -> Void) { - q.async { + queue { completionHandler(.init(catching: { return try self.listDirectory(path: path, recursive: recursive) })) @@ -345,7 +348,7 @@ public class AMSMB2: NSObject, NSSecureCoding, Codable, NSCopying, CustomReflect */ open func attributesOfFileSystem(forPath path: String, completionHandler: @escaping (_ result: Result<[FileAttributeKey: Any], Error>) -> Void) { - q.async { + queue { completionHandler(.init(catching: { let context = try self.tryContext() // This exactly matches implementation of Swift Foundation. @@ -374,7 +377,7 @@ public class AMSMB2: NSObject, NSSecureCoding, Codable, NSCopying, CustomReflect */ open func attributesOfItem(atPath path: String, completionHandler: @escaping (_ result: Result<[URLResourceKey: Any], Error>) -> Void) { - q.async { + queue { completionHandler(.init(catching: { let context = try self.tryContext() let stat = try context.stat(path) @@ -397,7 +400,7 @@ public class AMSMB2: NSObject, NSSecureCoding, Codable, NSCopying, CustomReflect */ @objc(createDirectoryAtPath:completionHandler:) open func createDirectory(atPath path: String, completionHandler: SimpleCompletionHandler) { - q.async { + queue { do { let context = try self.tryContext() try context.mkdir(path) @@ -418,7 +421,7 @@ public class AMSMB2: NSObject, NSSecureCoding, Codable, NSCopying, CustomReflect */ @objc(removeDirectoryAtPath:recursive:completionHandler:) open func removeDirectory(atPath path: String, recursive: Bool, completionHandler: SimpleCompletionHandler) { - q.async { + queue { do { let context = try self.tryContext() @@ -464,7 +467,7 @@ public class AMSMB2: NSObject, NSSecureCoding, Codable, NSCopying, CustomReflect */ @objc(removeFileAtPath:completionHandler:) open func removeFile(atPath path: String, completionHandler: SimpleCompletionHandler) { - q.async { + queue { do { let context = try self.tryContext() try context.unlink(path) @@ -488,7 +491,7 @@ public class AMSMB2: NSObject, NSSecureCoding, Codable, NSCopying, CustomReflect */ @objc(truncateFileAtPath:atOffset:completionHandler:) open func truncateFile(atPath path: String, atOffset: UInt64, completionHandler: SimpleCompletionHandler) { - q.async { + queue { do { let context = try self.tryContext() try context.truncate(path, toLength: atOffset) @@ -509,7 +512,7 @@ public class AMSMB2: NSObject, NSSecureCoding, Codable, NSCopying, CustomReflect */ @objc(moveItemAtPath:toPath:completionHandler:) open func moveItem(atPath path: String, toPath: String, completionHandler: SimpleCompletionHandler) { - q.async { + queue { do { let context = try self.tryContext() try context.rename(path, to: toPath) @@ -563,7 +566,7 @@ public class AMSMB2: NSObject, NSSecureCoding, Codable, NSCopying, CustomReflect let upper = Int64(exactly: range.upperBound) ?? Int64.max let int64Range = lower.. Data in let stream = OutputStream.toMemory() try self.read(path: path, range: int64Range, to: stream, progress: progress) @@ -593,7 +596,7 @@ public class AMSMB2: NSObject, NSSecureCoding, Codable, NSCopying, CustomReflect open func contents(atPath path: String, offset: Int64 = 0, fetchedData: @escaping ((_ offset: Int64, _ total: Int64, _ data: Data) -> Bool), completionHandler: SimpleCompletionHandler) { - q.async { + queue { do { let context = try self.tryContext() let file = try SMB2FileHandle(forReadingAtPath: path, on: context) @@ -632,7 +635,7 @@ public class AMSMB2: NSObject, NSSecureCoding, Codable, NSCopying, CustomReflect */ open func write(data: DataType, toPath path: String, progress: SMB2WriteProgressHandler, completionHandler: SimpleCompletionHandler) { - q.async { + queue { do { let stream = InputStream(data: Data(data)) try self.write(from: stream, toPath: path, chunkSize: 0, progress: progress) @@ -662,7 +665,7 @@ public class AMSMB2: NSObject, NSSecureCoding, Codable, NSCopying, CustomReflect @objc(writeStream:toPath:chunkSize:progress:completionHandler:) open func write(stream: InputStream, toPath path: String, chunkSize: Int = 0, progress: SMB2WriteProgressHandler, completionHandler: SimpleCompletionHandler) { - q.async { + queue { do { try self.write(from: stream, toPath: path, chunkSize: chunkSize, progress: progress) completionHandler?(nil) @@ -692,7 +695,7 @@ public class AMSMB2: NSObject, NSSecureCoding, Codable, NSCopying, CustomReflect @objc(copyContentsOfItemAtPath:toPath:recursiveprogress::completionHandler:) open func copyContentsOfItem(atPath path: String, toPath: String, recursive: Bool, progress: SMB2ReadProgressHandler, completionHandler: SimpleCompletionHandler) { - q.async { + queue { do { try self.recursiveCopyIterator(atPath: path, toPath: toPath, recursive: recursive, progress: progress, handle: self.copyContentsOfFile(atPath:toPath:progress:)) @@ -718,7 +721,7 @@ public class AMSMB2: NSObject, NSSecureCoding, Codable, NSCopying, CustomReflect @objc(copyItemAtPath:toPath:recursive:progress:completionHandler:) open func copyItem(atPath path: String, toPath: String, recursive: Bool, progress: SMB2ReadProgressHandler, completionHandler: SimpleCompletionHandler) { - q.async { + queue { do { try self.recursiveCopyIterator(atPath: path, toPath: toPath, recursive: recursive, progress: progress, handle: self.copyFile(atPath:toPath:progress:)) @@ -744,7 +747,7 @@ public class AMSMB2: NSObject, NSSecureCoding, Codable, NSCopying, CustomReflect @objc(uploadItemAtURL:toPath:progress:completionHandler:) open func uploadItem(at url: URL, toPath: String, progress: SMB2WriteProgressHandler, completionHandler: SimpleCompletionHandler) { - q.async { + queue { do { guard url.isFileURL, let stream = InputStream(url: url) else { throw POSIXError(.EIO, description: "Could not create NSStream from given URL.") @@ -781,7 +784,7 @@ public class AMSMB2: NSObject, NSSecureCoding, Codable, NSCopying, CustomReflect @objc(downloadItemAtPath:toURL:progress:completionHandler:) open func downloadItem(atPath path: String, to url: URL, progress: SMB2ReadProgressHandler, completionHandler: SimpleCompletionHandler) { - q.async { + queue { do { guard url.isFileURL, let stream = OutputStream(url: url, append: false) else { throw POSIXError(.EIO, description: "Could not create NSStream from given URL.") @@ -814,7 +817,7 @@ public class AMSMB2: NSObject, NSSecureCoding, Codable, NSCopying, CustomReflect @objc(downloadItemAtPath:toStream:progress:completionHandler:) open func downloadItem(atPath path: String, to stream: OutputStream, progress: SMB2ReadProgressHandler, completionHandler: SimpleCompletionHandler) { - q.async { + queue { do { try self.read(path: path, to: stream, progress: progress) completionHandler?(nil) @@ -826,6 +829,19 @@ public class AMSMB2: NSObject, NSSecureCoding, Codable, NSCopying, CustomReflect } extension AMSMB2 { + fileprivate func queue(_ closure: @escaping () -> Void) { + self.operationLock.lock() + self.operationCount += 1 + self.operationLock.unlock() + q.async { + closure() + self.operationLock.lock() + self.operationCount -= 1 + self.operationLock.broadcast() + self.operationLock.unlock() + } + } + fileprivate func initContext(_ context: SMB2Context) { context.securityMode = [.enabled] context.domain = _domain @@ -837,7 +853,7 @@ extension AMSMB2 { fileprivate func tryContext() throws -> SMB2Context { guard let context = self.context else { - throw POSIXError(POSIXError.ENOTCONN) + throw POSIXError.init(.ENOTCONN, description: "SMB2 server not connected.") } return context } diff --git a/AMSMB2/Context.swift b/AMSMB2/Context.swift index ad4c458..27cd020 100644 --- a/AMSMB2/Context.swift +++ b/AMSMB2/Context.swift @@ -18,6 +18,43 @@ extension smb2_negotiate_version { static let v2_10 = SMB2_VERSION_0210 static let v3_00 = SMB2_VERSION_0300 static let v3_02 = SMB2_VERSION_0302 + + static func == (lhs: smb2_negotiate_version, rhs: smb2_negotiate_version) -> Bool { + if lhs.rawValue == rhs.rawValue { return true } + switch (lhs, rhs) { + case (.any, _), (_, .any): + return true + case (.v2, v2_02), (v2_02, v2), (.v2, v2_10), (v2_10, v2): + return true + case (.v3, v3_00), (v3_00, v3), (.v3, v3_02), (v3_02, v3): + return true + default: + return false + } + } +} + +struct ShareProperties: RawRepresentable { + enum ShareType: UInt32 { + case diskTree + case printQ + case device + case ipc + } + + let rawValue: UInt32 + + var type: ShareType { + return ShareType(rawValue: rawValue & 0x0fffffff)! + } + + var isTemporary: Bool { + return rawValue & UInt32(bitPattern: SHARE_TYPE_TEMPORARY) != 0 + } + + var isHidden: Bool { + return rawValue & SHARE_TYPE_HIDDEN != 0 + } } final class SMB2Context: CustomDebugStringConvertible, CustomReflectable { @@ -153,7 +190,7 @@ extension SMB2Context { } var version: Version { - return self.context?.pointee.version ?? .any + return (self.context?.pointee.dialect).map { Version(rawValue: UInt32($0)) } ?? .any } var isConnected: Bool { @@ -220,7 +257,7 @@ extension SMB2Context { // MARK: DCE-RPC extension SMB2Context { - func shareEnum() throws -> [(name: String, type: UInt32, comment: String)] { + func shareEnum() throws -> [(name: String, props: ShareProperties, comment: String)] { let (_, cmddata) = try async_await(defaultError: .ENOLINK) { (context, cbPtr) -> Int32 in smb2_share_enum_async(context, SMB2Context.generic_handler, cbPtr) } @@ -236,7 +273,7 @@ extension SMB2Context { return parseShareEnum(ctr1: rep.pointee.ctr.pointee.ctr1) } - func shareEnumSwift(serverName: String) throws -> [(name: String, type: UInt32, comment: String)] + func shareEnumSwift(serverName: String) throws -> [(name: String, props: ShareProperties, comment: String)] { // Connection to server service. let srvsvc = try SMB2FileHandle(forPipe: "srvsvc", on: self) @@ -314,17 +351,11 @@ extension SMB2Context { var status: UInt32 { return UInt32(bitPattern: result) } - - static func new() -> UnsafeMutablePointer { - let cbPtr = UnsafeMutablePointer.allocate(capacity: 1) - cbPtr.initialize(to: .init()) - return cbPtr - } } - private func wait_for_reply(_ cbPtr: UnsafeMutablePointer) throws { + private func wait_for_reply(_ cb: inout CBData) throws { let startDate = Date() - while !cbPtr.pointee.isFinished { + while !cb.isFinished { var pfd = pollfd() pfd.fd = fileDescriptor pfd.events = Int16(whichEvents()) @@ -351,7 +382,7 @@ extension SMB2Context { switch (data, finishing) { case (true, true): return { smb2, status, command_data, cbdata in - guard let cbdata = cbdata?.assumingMemoryBound(to: CBData.self).pointee else { return } + guard let cbdata = cbdata?.bindMemory(to: CBData.self, capacity: 1).pointee else { return } if status != SMB2_STATUS_SUCCESS { cbdata.result = status } @@ -360,7 +391,7 @@ extension SMB2Context { } case (true, false): return { smb2, status, command_data, cbdata in - guard let cbdata = cbdata?.assumingMemoryBound(to: CBData.self).pointee else { return } + guard let cbdata = cbdata?.bindMemory(to: CBData.self, capacity: 1).pointee else { return } if status != SMB2_STATUS_SUCCESS { cbdata.result = status } @@ -368,7 +399,7 @@ extension SMB2Context { } case (false, true): return { smb2, status, command_data, cbdata in - guard let cbdata = cbdata?.assumingMemoryBound(to: CBData.self).pointee else { return } + guard let cbdata = cbdata?.bindMemory(to: CBData.self, capacity: 1).pointee else { return } if status != SMB2_STATUS_SUCCESS { cbdata.result = status } @@ -376,7 +407,7 @@ extension SMB2Context { } case (false, false): return { smb2, status, command_data, cbdata in - guard let cbdata = cbdata?.assumingMemoryBound(to: CBData.self).pointee else { return } + guard let cbdata = cbdata?.bindMemory(to: CBData.self, capacity: 1).pointee else { return } if status != SMB2_STATUS_SUCCESS { cbdata.result = status } @@ -390,20 +421,16 @@ extension SMB2Context { func async_await(defaultError: POSIXError.Code, execute handler: AsyncAwaitHandler) throws -> (result: Int32, data: UnsafeMutableRawPointer?) { - let cbPtr = CBData.new() - defer { - cbPtr.deinitialize(count: 1) - cbPtr.deallocate() - } + var cb = CBData() let result = try withThreadSafeContext { (context) -> Int32 in - return handler(context, cbPtr) + return handler(context, &cb) } try POSIXError.throwIfError(result, description: error, default: .ECONNRESET) - try wait_for_reply(cbPtr) - let cbResult = cbPtr.pointee.result + try wait_for_reply(&cb) + let cbResult = cb.result try POSIXError.throwIfError(cbResult, description: error, default: defaultError) - let data = cbPtr.pointee.data + let data = cb.data return (cbResult, data) } @@ -411,38 +438,34 @@ extension SMB2Context { func async_await_pdu(defaultError: POSIXError.Code, execute handler: AsyncAwaitHandler?>) throws -> (status: UInt32, data: UnsafeMutableRawPointer?) { - let cbPtr = CBData.new() - defer { - cbPtr.deinitialize(count: 1) - cbPtr.deallocate() - } + var cb = CBData() try withThreadSafeContext { (context) -> Void in - guard let pdu = handler(context, cbPtr) else { + guard let pdu = handler(context, &cb) else { throw POSIXError(.ENOMEM) } smb2_queue_pdu(context, pdu) } - try wait_for_reply(cbPtr) - let status = cbPtr.pointee.status + try wait_for_reply(&cb) + let status = cb.status if status & SMB2_STATUS_SEVERITY_MASK == SMB2_STATUS_SEVERITY_ERROR { let errorNo = nterror_to_errno(status) try POSIXError.throwIfError(-errorNo, description: nil, default: defaultError) } - let data = cbPtr.pointee.data + let data = cb.data return (status, data) } } fileprivate extension SMB2Context { - func parseShareEnum(ctr1: srvsvc_netsharectr1) -> [(name: String, type: UInt32, comment: String)] { - var result = [(name: String, type: UInt32, comment: String)]() + func parseShareEnum(ctr1: srvsvc_netsharectr1) -> [(name: String, props: ShareProperties, comment: String)] { + var result = [(name: String, props: ShareProperties, comment: String)]() let array = Array(UnsafeBufferPointer(start: ctr1.array, count: Int(ctr1.count))) for item in array { let name = String(cString: item.name) - let type = item.type + let type = ShareProperties(rawValue: item.type) let comment = String(cString: item.comment) - result.append((name: name, type: type, comment: comment)) + result.append((name: name, props: type, comment: comment)) } return result } diff --git a/AMSMB2/Extensions.swift b/AMSMB2/Extensions.swift index 1afef3f..925b07a 100644 --- a/AMSMB2/Extensions.swift +++ b/AMSMB2/Extensions.swift @@ -70,20 +70,13 @@ extension Data { uuid.uuid.12, uuid.uuid.13, uuid.uuid.14, uuid.uuid.15]) } - func scanValue(start: Int) -> T? { - let length = MemoryLayout.size - guard self.count >= start + length else { return nil } - var result: T = 0 - (self as NSData).getBytes(&result, range: NSRange(location: start, length: length)) - return result.littleEndian + func scanValue(offset: Int, as: T.Type) -> T? { + guard self.count >= offset + MemoryLayout.size else { return nil } + return T(littleEndian: withUnsafeBytes { $0.load(fromByteOffset: offset, as: T.self) }) } - func scanValue(start: Int, as: T.Type) -> T? { - let length = MemoryLayout.size - guard self.count >= start + length else { return nil } - var result: T = 0 - (self as NSData).getBytes(&result, range: NSRange(location: start, length: length)) - return result.littleEndian + func scanInt(offset: Int, as: T.Type) -> Int? { + return scanValue(offset: offset, as: T.self).map(Int.init) } } diff --git a/AMSMB2/FileHandle.swift b/AMSMB2/FileHandle.swift index 33326e8..426939f 100644 --- a/AMSMB2/FileHandle.swift +++ b/AMSMB2/FileHandle.swift @@ -48,13 +48,18 @@ final class SMB2FileHandle { convenience init(forPipe path: String, on context: SMB2Context) throws { // smb2_open() sets overwrite flag, which is incompatible with pipe in mac's smbx let (_, cmddata) = try context.async_await_pdu(defaultError: .ENOENT) { (context, cbPtr) -> UnsafeMutablePointer? in - let pReq = UnsafeMutablePointer.allocate(capacity: 1) - pReq.initialize(to: SMB2FileHandle.standardOpenRequest(path: path)) - defer { - pReq.deinitialize(count: 1) - pReq.deallocate() + return path.replacingOccurrences(of: "/", with: "\\").withCString { (path) in + var req = smb2_create_request() + req.requested_oplock_level = UInt8(SMB2_OPLOCK_LEVEL_NONE) + req.impersonation_level = UInt32(SMB2_IMPERSONATION_IMPERSONATION) + req.desired_access = UInt32(SMB2_FILE_READ_DATA | SMB2_FILE_READ_EA | SMB2_FILE_READ_ATTRIBUTES | + SMB2_FILE_WRITE_DATA | SMB2_FILE_WRITE_EA | SMB2_FILE_WRITE_ATTRIBUTES) + req.share_access = UInt32(SMB2_FILE_SHARE_READ | SMB2_FILE_SHARE_WRITE) + req.create_disposition = UInt32(SMB2_FILE_OPEN) + req.create_options = UInt32(SMB2_FILE_NON_DIRECTORY_FILE) + req.name = path + return smb2_cmd_create_async(context, &req, SMB2Context.generic_handler, cbPtr) } - return smb2_cmd_create_async(context, pReq, SMB2Context.generic_handler, cbPtr) } guard let reply = cmddata?.bindMemory(to: smb2_create_reply.self, capacity: 1) else { @@ -207,8 +212,8 @@ final class SMB2FileHandle { input: &buffer, flags: UInt32(SMB2_0_IOCTL_IS_FSCTL)) let (_, response) = try context.async_await_pdu(defaultError: .EBADRPC) { - (context, cbdata) -> UnsafeMutablePointer? in - smb2_cmd_ioctl_async(context, &req, SMB2Context.generic_handler, cbdata) + (context, cbPtr) -> UnsafeMutablePointer? in + smb2_cmd_ioctl_async(context, &req, SMB2Context.generic_handler, cbPtr) } guard let reply = response?.bindMemory(to: smb2_ioctl_reply.self, capacity: 1).pointee else { @@ -261,19 +266,4 @@ fileprivate extension SMB2FileHandle { } return handle } - - static func standardOpenRequest(path: String) -> smb2_create_request { - return path.replacingOccurrences(of: "/", with: "\\").withCString { (path) in - var req = smb2_create_request() - req.requested_oplock_level = UInt8(SMB2_OPLOCK_LEVEL_NONE) - req.impersonation_level = UInt32(SMB2_IMPERSONATION_IMPERSONATION) - req.desired_access = UInt32(SMB2_FILE_READ_DATA | SMB2_FILE_READ_EA | SMB2_FILE_READ_ATTRIBUTES | - SMB2_FILE_WRITE_DATA | SMB2_FILE_WRITE_EA | SMB2_FILE_WRITE_ATTRIBUTES) - req.share_access = UInt32(SMB2_FILE_SHARE_READ | SMB2_FILE_SHARE_WRITE) - req.create_disposition = UInt32(SMB2_FILE_OPEN) - req.create_options = UInt32(SMB2_FILE_NON_DIRECTORY_FILE) - req.name = path - return req - } - } } diff --git a/AMSMB2/Fsctl.swift b/AMSMB2/Fsctl.swift index 22ca55b..8c87ba7 100644 --- a/AMSMB2/Fsctl.swift +++ b/AMSMB2/Fsctl.swift @@ -109,15 +109,15 @@ struct IOCtl { let isRelative: Bool init(data: Data) throws { - guard data.scanValue(start: 0) as UInt32? == self.reparseTag else { + guard data.scanValue(offset: 0, as: UInt32.self) == self.reparseTag else { throw POSIXError(.EINVAL) } - guard let substituteOffset = data.scanValue(start: 8, as: UInt16.self).map(Int.init), - let substituteLen = data.scanValue(start: 10, as: UInt16.self).map(Int.init), - let printOffset = data.scanValue(start: 12, as: UInt16.self).map(Int.init), - let printLen = data.scanValue(start: 14, as: UInt16.self).map(Int.init), - let flag = data.scanValue(start: 16, as: UInt32.self) else { + guard let substituteOffset = data.scanInt(offset: 8, as: UInt16.self), + let substituteLen = data.scanInt(offset: 10, as: UInt16.self), + let printOffset = data.scanInt(offset: 12, as: UInt16.self), + let printLen = data.scanInt(offset: 14, as: UInt16.self), + let flag = data.scanValue(offset: 16, as: UInt32.self) else { throw POSIXError(.EINVAL) } @@ -165,14 +165,14 @@ struct IOCtl { let printName: String init(data: Data) throws { - guard data.scanValue(start: 0) as UInt32? == self.reparseTag else { + guard data.scanValue(offset: 0, as: UInt32.self) == self.reparseTag else { throw POSIXError(.EINVAL) } - guard let substituteOffset = (data.scanValue(start: 8) as UInt16?).map(Int.init), - let substituteLen = (data.scanValue(start: 10) as UInt16?).map(Int.init), - let printOffset = (data.scanValue(start: 12) as UInt16?).map(Int.init), - let printLen = (data.scanValue(start: 14) as UInt16?).map(Int.init) else { + guard let substituteOffset = data.scanInt(offset: 8, as: UInt16.self), + let substituteLen = data.scanInt(offset: 10, as: UInt16.self), + let printOffset = data.scanInt(offset: 12, as: UInt16.self), + let printLen = data.scanInt(offset: 14, as: UInt16.self) else { throw POSIXError(.EINVAL) } diff --git a/AMSMB2/MSRPC.swift b/AMSMB2/MSRPC.swift index 3f53636..02b7bb3 100644 --- a/AMSMB2/MSRPC.swift +++ b/AMSMB2/MSRPC.swift @@ -10,9 +10,9 @@ import Foundation class MSRPC { static func parseNetShareEnumAllLevel1(data: Data) throws - -> [(name: String, type: UInt32, comment: String)] + -> [(name: String, props: ShareProperties, comment: String)] { - var shares = [(name: String, type: UInt32, comment: String)]() + var shares = [(name: String, props: ShareProperties, comment: String)]() /* Data Layout : @@ -42,7 +42,7 @@ class MSRPC { } // Count of shares to be enumerated, [44-47] - guard let count = data.scanValue(start: 44, as: UInt32.self).map(Int.init) else { + guard let count = data.scanInt(offset: 44, as: UInt32.self) else { throw POSIXError(.EBADMSG) } @@ -50,10 +50,10 @@ class MSRPC { var offset = 48 + count * 12 for i in 0.. data.count { break diff --git a/AMSMB2/ObjCCompat.swift b/AMSMB2/ObjCCompat.swift index 4ae4f0b..6bfa5bb 100644 --- a/AMSMB2/ObjCCompat.swift +++ b/AMSMB2/ObjCCompat.swift @@ -9,6 +9,29 @@ import Foundation extension AMSMB2 { + /** + Disconnects from a share. + + - Important: Disconnecting when an operation is in progress may cause disgraceful termination of operation. + */ + @objc(disconnectShare) + open func __disconnectShare() { + self.disconnectShare() + } + + /** + Disconnects from a share. + + - Parameters: + - completionHandler: closure will be run after enumerating is completed. + + - Important: Disconnecting when an operation is in progress may cause disgraceful termination of operation. + */ + @objc(disconnectWithCompletionHandler:) + open func __disconnectShare(completionHandler: SimpleCompletionHandler) { + self.disconnectShare(completionHandler: completionHandler) + } + /** Enumerates shares' list on server. diff --git a/AMSMB2Tests/AMSMB2Tests.swift b/AMSMB2Tests/AMSMB2Tests.swift index 26d9e50..6308712 100644 --- a/AMSMB2Tests/AMSMB2Tests.swift +++ b/AMSMB2Tests/AMSMB2Tests.swift @@ -86,8 +86,8 @@ class AMSMB2Tests: XCTestCase { case .success(let value): XCTAssertFalse(value.isEmpty) XCTAssert(value.contains(where: { $0.name == self.share })) - case .failure: - XCTAssert(false) + case .failure(let error): + XCTAssert(false, error.localizedDescription) } expectation.fulfill() } @@ -112,8 +112,8 @@ class AMSMB2Tests: XCTestCase { XCTAssertNotNil(file.fileModificationDate) XCTAssertNotNil(file.fileCreationDate) XCTAssertGreaterThanOrEqual(file.fileModificationDate!, file.fileCreationDate!) - case .failure: - XCTAssert(false) + case .failure(let error): + XCTAssert(false, error.localizedDescription) } expectation.fulfill() } @@ -221,8 +221,8 @@ class AMSMB2Tests: XCTestCase { switch result { case .success(let rdata): XCTAssertEqual(data, rdata) - case .failure: - XCTAssert(false) + case .failure(let error): + XCTAssert(false, error.localizedDescription) } expectation.fulfill() }) @@ -234,8 +234,8 @@ class AMSMB2Tests: XCTestCase { switch result { case .success(let rdata): XCTAssertEqual(data.prefix(10), rdata) - case .failure: - XCTAssert(false) + case .failure(let error): + XCTAssert(false, error.localizedDescription) } expectation.fulfill() }) @@ -325,8 +325,8 @@ class AMSMB2Tests: XCTestCase { switch result { case .success(let value): XCTAssertEqual(value.fileSize, Int64(data.count)) - case .failure: - XCTAssert(false) + case .failure(let error): + XCTAssert(false, error.localizedDescription) } expectation.fulfill() })