ExNetfs
Elixir bindings to macOS NetFS.framework for mounting and unmounting network file systems (SMB, NFS, AFP, WebDAV).
NetFS is the framework Finder uses under the hood for "Connect to Server…". It handles protocol negotiation, Kerberos authentication, DFS referral resolution, and the actual mount syscall.
Why NetFS over mount_smbfs?
- Kerberos-aware: Automatically uses TGT from credential cache (populated by
ExKrb5.kinit/2) - DFS support: Follows SMB DFS referrals automatically
- Proper error codes: Returns structured errors instead of exit codes
- Mount point reporting: Returns the actual path where the share was mounted
- No shell-out: Direct framework call, no
System.cmdneeded
Installation
def deps do
[{:ex_netfs, "~> 0.1.0"}]
endRequirements: macOS only. Links against NetFS.framework and CoreFoundation.framework.
Usage
Mount with Kerberos (AD-joined Mac)
# First, get a Kerberos ticket
:ok = ExKrb5.kinit("user@AD.EXAMPLE.COM", password)
# Mount — NetFS picks up the TGT automatically
{:ok, ["/Volumes/share"]} = ExNetfs.mount("smb://fileserver.ad.example.com/share")Mount with explicit credentials
{:ok, [mountpoint]} = ExNetfs.mount("smb://nas.local/backup",
user: "admin",
password: "secret")Mount hidden at specific path
{:ok, _} = ExNetfs.mount_hidden("smb://server/share", "/Volumes/.myshare")Mount as guest
{:ok, [path]} = ExNetfs.mount_guest("smb://publicserver/public")Unmount
:ok = ExNetfs.unmount("/Volumes/share")
:ok = ExNetfs.force_unmount("/Volumes/stuck_share")List network mounts
ExNetfs.list_mounts()
# => [{"//server/share", "/Volumes/share", "smbfs"},
# {"nfsserver:/export", "/Volumes/export", "nfs"}]Check if mounted
ExNetfs.mounted?("/Volumes/share") # => trueFind mount by server/share
ExNetfs.find_mount("fileserver", "share")
# => {:ok, "/Volumes/share"}Mount Options
Open Options (session-level)
| Atom | Effect |
|---|---|
:no_ui | Suppress authentication dialogs |
:guest | Login as guest user |
:allow_loopback | Allow mounting from localhost |
Mount Options (filesystem-level)
| Atom | Effect |
|---|---|
:no_browse | Hide from Finder sidebar (MNT_DONTBROWSE) |
:read_only | Mount read-only (MNT_RDONLY) |
:allow_sub_mounts | Allow mounting subdirectories of share |
:soft_mount | Soft failure semantics (shorter timeout) |
:mount_at_dir | Mount exactly at mountpath, not below it |
Full AD Auto-Mount Example
# 1. Authenticate to AD
:ok = ExKrb5.kinit("mac.w@AD.APTALASKA.COM", password)
# 2. Look up user's home share from AD
{:ok, node} = ExOpenDirectory.connect(:search)
{:ok, record} = ExOpenDirectory.find_user(node, "mac.w")
{:ok, attrs} = ExOpenDirectory.get_attributes(record, ["dsAttrTypeStandard:SMBHome"])
# => {:ok, [{"dsAttrTypeStandard:SMBHome", ["\\\\server\\share"]}]}
# 3. Convert UNC path to SMB URL
[{_, [unc_path]}] = attrs
smb_url = unc_path |> String.replace("\\", "/") |> then(&"smb:#{&1}")
# 4. Mount silently
{:ok, [mountpoint]} = ExNetfs.mount_smb_kerberos(smb_url)License
MIT