Skip to content

Commit

Permalink
Merge pull request #25 from amosavian/v2.0
Browse files Browse the repository at this point in the history
Integrating Swift 5.0 new features
  • Loading branch information
amosavian authored Apr 28, 2019
2 parents fcbef23 + ec4e4bb commit e52efbb
Show file tree
Hide file tree
Showing 16 changed files with 695 additions and 315 deletions.
2 changes: 1 addition & 1 deletion AMSMB2.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Pod::Spec.new do |s|
#

s.name = "AMSMB2"
s.version = "1.9.0"
s.version = "2.0.0"
s.summary = "Swift framework to connect SMB2/3 shares"

# This description is used to generate tags and improve search results.
Expand Down
4 changes: 4 additions & 0 deletions AMSMB2.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
/* Begin PBXBuildFile section */
790FA33920B2CC5600578C8A /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 790FA33820B2CC5600578C8A /* Extensions.swift */; };
7957E1F220F1EEB100F96CE4 /* Fsctl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7957E1F120F1EEB100F96CE4 /* Fsctl.swift */; };
7966DC4D2273A5CA00BBC39D /* ObjCCompat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7966DC4C2273A5CA00BBC39D /* ObjCCompat.swift */; };
79B5EDBD210FE1DB00449EF6 /* MSRPC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79B5EDBC210FE1DB00449EF6 /* MSRPC.swift */; };
79FDFE5E20AD439F00749FB5 /* AMSMB2.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 79FDFE5420AD439F00749FB5 /* AMSMB2.framework */; };
79FDFE6320AD439F00749FB5 /* AMSMB2Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79FDFE6220AD439F00749FB5 /* AMSMB2Tests.swift */; };
Expand All @@ -33,6 +34,7 @@
/* Begin PBXFileReference section */
790FA33820B2CC5600578C8A /* Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
7957E1F120F1EEB100F96CE4 /* Fsctl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fsctl.swift; sourceTree = "<group>"; };
7966DC4C2273A5CA00BBC39D /* ObjCCompat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjCCompat.swift; sourceTree = "<group>"; };
79B5EDBC210FE1DB00449EF6 /* MSRPC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MSRPC.swift; sourceTree = "<group>"; };
79FDFE5420AD439F00749FB5 /* AMSMB2.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AMSMB2.framework; sourceTree = BUILT_PRODUCTS_DIR; };
79FDFE5720AD439F00749FB5 /* AMSMB2.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AMSMB2.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -92,6 +94,7 @@
79FDFE5820AD439F00749FB5 /* Info.plist */,
79FDFE5720AD439F00749FB5 /* AMSMB2.h */,
79FDFE6E20AD45C100749FB5 /* AMSMB2.swift */,
7966DC4C2273A5CA00BBC39D /* ObjCCompat.swift */,
79FDFE7B20AD545D00749FB5 /* Context.swift */,
79B5EDBC210FE1DB00449EF6 /* MSRPC.swift */,
7957E1F120F1EEB100F96CE4 /* Fsctl.swift */,
Expand Down Expand Up @@ -235,6 +238,7 @@
790FA33920B2CC5600578C8A /* Extensions.swift in Sources */,
79FDFE6F20AD45C100749FB5 /* AMSMB2.swift in Sources */,
79B5EDBD210FE1DB00449EF6 /* MSRPC.swift in Sources */,
7966DC4D2273A5CA00BBC39D /* ObjCCompat.swift in Sources */,
7957E1F220F1EEB100F96CE4 /* Fsctl.swift in Sources */,
79FDFE7A20AD541500749FB5 /* URL.swift in Sources */,
79FDFF0120AD852200749FB5 /* FileHandle.swift in Sources */,
Expand Down
363 changes: 230 additions & 133 deletions AMSMB2/AMSMB2.swift

Large diffs are not rendered by default.

97 changes: 60 additions & 37 deletions AMSMB2/Context.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}
Expand All @@ -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)
Expand Down Expand Up @@ -314,17 +351,11 @@ extension SMB2Context {
var status: UInt32 {
return UInt32(bitPattern: result)
}

static func new() -> UnsafeMutablePointer<CBData> {
let cbPtr = UnsafeMutablePointer<CBData>.allocate(capacity: 1)
cbPtr.initialize(to: .init())
return cbPtr
}
}

private func wait_for_reply(_ cbPtr: UnsafeMutablePointer<CBData>) 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())
Expand All @@ -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
}
Expand All @@ -360,23 +391,23 @@ 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
}
cbdata.data = command_data
}
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
}
cbdata.isFinished = true
}
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
}
Expand All @@ -390,59 +421,51 @@ extension SMB2Context {
func async_await(defaultError: POSIXError.Code, execute handler: AsyncAwaitHandler<Int32>)
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)
}

@discardableResult
func async_await_pdu(defaultError: POSIXError.Code, execute handler: AsyncAwaitHandler<UnsafeMutablePointer<smb2_pdu>?>)
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
}
Expand Down
41 changes: 23 additions & 18 deletions AMSMB2/Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,33 @@ extension POSIXError {
}

extension Dictionary where Key == URLResourceKey, Value == Any {
var filename: String? {
var fileName: String? {
return self[.nameKey] as? String
}

var filepath: String? {
var filePath: String? {
return self[.pathKey] as? String
}

var filetype: URLFileResourceType? {
var fileType: URLFileResourceType? {
return self[.fileResourceTypeKey] as? URLFileResourceType
}

var filesize: Int64? {
var fileSize: Int64? {
return self[.fileSizeKey] as? Int64
}

var fileModificationDate: Date? {
return self[.contentModificationDateKey] as? Date
}

var fileAccessDate: Date? {
return self[.contentAccessDateKey] as? Date
}

var fileCreationDate: Date? {
return self[.creationDateKey] as? Date
}
}

extension Data {
Expand All @@ -58,25 +70,18 @@ extension Data {
uuid.uuid.12, uuid.uuid.13, uuid.uuid.14, uuid.uuid.15])
}

func scanValue<T: FixedWidthInteger>(start: Int) -> T? {
let length = MemoryLayout<T>.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<T: FixedWidthInteger>(offset: Int, as: T.Type) -> T? {
guard self.count >= offset + MemoryLayout<T>.size else { return nil }
return T(littleEndian: withUnsafeBytes { $0.load(fromByteOffset: offset, as: T.self) })
}

func scanValue<T: FixedWidthInteger>(start: Int, as: T.Type) -> T? {
let length = MemoryLayout<T>.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<T: FixedWidthInteger>(offset: Int, as: T.Type) -> Int? {
return scanValue(offset: offset, as: T.self).map(Int.init)
}
}

extension InputStream {
func readData(ofLength length: Int) throws -> Data {
func readData(maxLength length: Int) throws -> Data {
var buffer = [UInt8](repeating: 0, count: length)
let result = self.read(&buffer, maxLength: buffer.count)
if result < 0 {
Expand All @@ -88,7 +93,7 @@ extension InputStream {
}

extension OutputStream {
func write(data: Data) throws -> Int {
func write<DataType: DataProtocol>(_ data: DataType) throws -> Int {
var buffer = Array(data)
let result = self.write(&buffer, maxLength: buffer.count)
if result < 0 {
Expand Down
44 changes: 17 additions & 27 deletions AMSMB2/FileHandle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<smb2_pdu>? in
let pReq = UnsafeMutablePointer<smb2_create_request>.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 {
Expand Down Expand Up @@ -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<smb2_pdu>? in
smb2_cmd_ioctl_async(context, &req, SMB2Context.generic_handler, cbdata)
(context, cbPtr) -> UnsafeMutablePointer<smb2_pdu>? 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 {
Expand Down Expand Up @@ -239,17 +244,17 @@ final class SMB2FileHandle {
try fcntl(command: command, data: Data(), needsReply: false)
}

func fcntl<T: DataRepresentable>(command: IOCtl.Command, args: T) throws -> Void {
try fcntl(command: command, data: args.data(), needsReply: false)
func fcntl<T: DataProtocol>(command: IOCtl.Command, args: T) throws -> Void where T.Regions == CollectionOfOne<Data> {
try fcntl(command: command, data: args.regions[0], needsReply: false)
}

func fcntl<R: DataInitializable>(command: IOCtl.Command) throws -> R {
let result = try fcntl(command: command, data: Data())
return try R(data: result)
}

func fcntl<T: DataRepresentable, R: DataInitializable>(command: IOCtl.Command, args: T) throws -> R {
let result = try fcntl(command: command, data: args.data())
func fcntl<T: DataProtocol, R: DataInitializable>(command: IOCtl.Command, args: T) throws -> R where T.Regions == CollectionOfOne<Data> {
let result = try fcntl(command: command, data: args.regions[0])
return try R(data: result)
}
}
Expand All @@ -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
}
}
}
Loading

0 comments on commit e52efbb

Please sign in to comment.