Instantly share code, notes, and snippets.
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save romac/58ca1d9157effa9f9eedac50a1b479c1 to your computer and use it in GitHub Desktop.
Convenient swift-like wrapper over NetFS smb mounting
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import NetFS | |
enum ShareMountError: Error { | |
case InvalidURL | |
case MountpointInaccessible | |
case InvalidMountOptions | |
} | |
enum MountOption { | |
case NoBrowse | |
case ReadOnly | |
case AllowSubMounts | |
case SoftMount | |
case MountAtMountDirectory | |
case Guest | |
case AllowLoopback | |
case NoAuthDialog | |
case AllowAuthDialog | |
case ForceAuthDialog | |
} | |
typealias NetFSMountCallback = (Int32, UnsafeMutableRawPointer?, CFArray?) -> Void | |
typealias MountCallbackHandler = (Int32, URL?, [String]?) -> Void; | |
protocol ShareDelegate { | |
func shareWillMount(url: URL) -> Void | |
func shareDidMount(url: URL, at paths: [String]?) -> Void | |
func shareMountingDidFail(for url: URL, withError: Int32) -> Void | |
} | |
fileprivate func processOptionsForNetFS(options: [MountOption]) throws -> (NSMutableDictionary, NSMutableDictionary) { | |
let openOptions: NSMutableDictionary = NSMutableDictionary() | |
let mountOptions: NSMutableDictionary = NSMutableDictionary() | |
for opt in options { | |
switch opt { | |
// mount_options | |
case .NoBrowse: | |
if let existingValue = mountOptions.value(forKey: kNetFSMountFlagsKey) { | |
mountOptions[kNetFSMountFlagsKey] = existingValue as! Int32 | MNT_DONTBROWSE | |
} else { | |
mountOptions[kNetFSMountFlagsKey] = MNT_DONTBROWSE | |
} | |
case .ReadOnly: | |
if let existingValue = mountOptions.value(forKey: kNetFSMountFlagsKey) { | |
mountOptions[kNetFSMountFlagsKey] = existingValue as! Int32 | MNT_RDONLY | |
} else { | |
mountOptions[kNetFSMountFlagsKey] = MNT_RDONLY | |
} | |
case .AllowSubMounts: | |
mountOptions[kNetFSAllowSubMountsKey] = true | |
case .SoftMount: | |
mountOptions[kNetFSSoftMountKey] = true | |
case .MountAtMountDirectory: | |
mountOptions[kNetFSMountAtMountDirKey] = true | |
// open_options | |
case .Guest: | |
openOptions[kNetFSUseGuestKey] = true | |
case .AllowLoopback: | |
openOptions[kNetFSAllowLoopbackKey] = true | |
case .NoAuthDialog: | |
openOptions[kNAUIOptionKey] = kNAUIOptionNoUI | |
case .AllowAuthDialog: | |
openOptions[kNAUIOptionKey] = kNAUIOptionAllowUI | |
case .ForceAuthDialog: | |
openOptions[kNAUIOptionKey] = kNAUIOptionForceUI | |
} | |
} | |
return (openOptions, mountOptions) | |
} | |
class Share { | |
let url: URL | |
var mountPoint: String = "/Volumes" | |
var username: String? | |
var password: String? | |
fileprivate var asyncRequestId: AsyncRequestID? | |
public var delegate: ShareDelegate? | |
init(_ url: URL) { | |
self.url = url | |
} | |
init(_ urlString: String) throws { | |
guard let url = URL(string: urlString) else { | |
throw ShareMountError.InvalidURL | |
} | |
self.url = url | |
} | |
public func cancelMounting() { | |
NetFSMountURLCancel(self.asyncRequestId) | |
} | |
static func cancelMounting(id requestId: AsyncRequestID) { | |
NetFSMountURLCancel(requestId) | |
} | |
public func mount() throws { | |
let mountDirectoryURL = URL(fileURLWithPath: self.mountPoint) | |
let operationQueue = OperationQueue.main | |
let mountReportBlock: NetFSMountCallback = { | |
status, asyncRequestId, mountedDirs in | |
let mountedDirectories = mountedDirs as! [String]? ?? nil | |
if (status != 0) { | |
self.delegate?.shareMountingDidFail(for: self.url, withError: status) | |
} else { | |
self.delegate?.shareDidMount(url: self.url, at: mountedDirectories) | |
} | |
} | |
NetFSMountURLAsync(url as CFURL, | |
mountDirectoryURL as CFURL, | |
username as CFString?, | |
password as CFString?, | |
nil, | |
nil, | |
&self.asyncRequestId, | |
operationQueue.underlyingQueue, | |
mountReportBlock) | |
self.delegate?.shareWillMount(url: url) | |
} | |
public func mount(options: [MountOption]?, callbackHandler: @escaping MountCallbackHandler) throws -> AsyncRequestID? { | |
let mountDirectoryURL = URL(fileURLWithPath: self.mountPoint) | |
let operationQueue = OperationQueue.main | |
let mountReportBlock: NetFSMountCallback = { | |
(status, asyncRequestId, mountedDirs) in | |
callbackHandler(status, self.url, mountedDirs as? [String]) | |
} | |
var openOptions: NSMutableDictionary | |
var mountOptions: NSMutableDictionary | |
if options != nil { | |
(openOptions, mountOptions) = try processOptionsForNetFS(options: options!) | |
} else { | |
openOptions = NSMutableDictionary() | |
mountOptions = NSMutableDictionary() | |
} | |
NetFSMountURLAsync(url as CFURL, | |
mountDirectoryURL as CFURL, | |
username as CFString?, | |
password as CFString?, | |
openOptions as CFMutableDictionary, | |
mountOptions as CFMutableDictionary, | |
&self.asyncRequestId, | |
operationQueue.underlyingQueue, | |
mountReportBlock) | |
self.delegate?.shareWillMount(url: url) | |
return self.asyncRequestId | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment