You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- Always set NetFS `UIOption=NoUI` so macOS never shows its own auth dialogs; auth failures come back as typed errors instead. Map the NetAuth codes (`-6600` → auth failed, `-6004` → auth required, `-6003` → share not found).
- Auth-class mount failures now render the login form (error inline, username pre-filled) instead of the dead-end error pane; submitting retries the mount with the entered creds and saves them to the Keychain on success.
- Activating a share when creds are required and none are held opens the login form first instead of firing a doomed guest mount (happens when the share listing succeeded via the system Keychain through `smbutil`, which Cmdr can't reuse).
- Compare servers by identity (mDNS service name ↔ `.local` hostname ↔ IP, via the new `network/server_identity.rs`) in mount-path disambiguation, and return `already_mounted` early when the same server+share+port is already mounted, so one NAS can't be treated as two and forced into a second doomed session.
- New tests pin all of the above; incident analysis and the P1/P2 redesign proposal live in `docs/notes/smb-auth-flow-redesign.md`.
-**Mounting** (platform-specific via `#[path]` in `mod.rs`):
23
23
-`mount.rs`: macOS `NetFSMountURLSync` for native `/Volumes/` mounts; also `unmount_smb_shares_from_host` (iterates `/Volumes/`, matches via `statfs`, unmounts via `diskutil`)
24
24
-`mount_linux.rs`: Linux `gio mount` for GVFS-based user-space mounts
25
+
-**Server identity**: `server_identity.rs`: `same_server` / `same_server_live` equivalence over the names a server goes by (mDNS service name, `.local` hostname, IP), enriched from the discovery state. Used by the mount-path disambiguation and the already-mounted short-circuit so string-shape differences can't split one server into two.
25
26
-**Auth** (platform-agnostic):
26
27
-`keychain.rs`: SMB credential management. Delegates storage to `crate::secrets::store()` (see `secrets/CLAUDE.md` for backend details)
27
28
-**State**: `known_shares.rs`: Connection history in `known-shares.json` (usernames, last auth mode, timestamps).
@@ -81,6 +82,13 @@ guest/credentials toggle. `keychain.rs` delegates to `crate::secrets::store()` f
81
82
(macOS Keychain, Linux Secret Service, encrypted file fallback). Passwords never stored in our settings file.
82
83
`CMDR_SECRET_STORE=file` forces the plain file backend in dev mode (set by `tauri-wrapper.js`).
83
84
85
+
To make this hold, every NetFS mount sets `UIOption = NoUI` (`open_option_entries` in `mount.rs`). Without it, NetFS
86
+
hands auth *failures* to NetAuthAgent even when we pass explicit credentials: the agent pops a system dialog ("You
87
+
entered an invalid username or password...") on top of Cmdr, blocks the mount call while open, and returns
88
+
`kNetAuthErrorInternal` (-6600) when dismissed. With `NoUI`, the same failure returns immediately as a typed code
89
+
(`error_from_code` maps -6600 → `AuthFailed`, -6004 `kNetAuthErrorGuestNotSupported` → `AuthRequired`) and the frontend
90
+
renders its own login form.
91
+
84
92
### `smb2` for SMB share enumeration (not `pavao`/libsmbclient, `smb-rs`, or `smbutil`)
85
93
86
94
MIT license (compatible with BSL, allows dual-licensing for enterprise), pure Rust (no C dependencies), async-native
@@ -197,6 +205,12 @@ already taken by a different server (via `statfs`), and if so picks `/Volumes/{s
197
205
convention) and passes it as an explicit mount point to `NetFSMountURLSync`. The volume switcher shows
198
206
`{share} on {server}` for SMB mounts so the user knows which server each volume belongs to.
199
207
208
+
"Different server" is an identity comparison (`server_identity::same_server_live`), never a string compare: `statfs`
209
+
may report the existing mount as `Naspolya._smb._tcp.local` while we mount by `192.168.1.111`, and a string mismatch
210
+
would treat one NAS as two, force a second mount with `ForceNewSession`, and break session reuse. For the same reason,
211
+
`mount_share_sync` returns early with `already_mounted: true` when `find_mount_path_for_share` finds the same
-**Don't hold mutex during DNS resolution**: `get_host_for_resolution` / `update_host_resolution` extract host info and release the mutex before blocking DNS, then re-acquire to update. Holding the mutex across network calls risks deadlock.
0 commit comments