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
SMB: Use smb2 consumer test harness, fix port handling
Monster commit
- Replace Cmdr's 11 local Docker container definitions with smb2's consumer test harness (14 containers, extracted on first run via `cargo run --example smb_compose`)
- Delete `test/smb-servers/containers/`, old `docker-compose.yml`, Pi deploy files
- `smb-e2e` Cargo feature now activates `smb2/testing`; examples gated with `required-features`
- `virtual_smb_hosts.rs` injects all 14 virtual hosts using `smb2::testing::*_port()` functions
- Add E2E tests for 50-share enumeration and unicode share rendering
- Update `smb-fixtures.ts`, `e2e-linux.sh`, and all docs for new container names/ports
Port handling fixes (pre-existing bugs):
- Add `port: u16` to `SmbMountInfo` (macOS + Linux) — extract port from `statfs` mount source instead of discarding it
- Thread port through `mount_share` → `mount_share_sync` → SMB URL (`smb://server:port/share`)
- Fix `upgrade_to_smb_volume`, `upgrade_existing_smb_mounts`, `try_upgrade_smb_mount` — use `info.port` instead of hardcoded 445
- Fix `NetworkMountView.svelte` — pass port separately instead of embedding in server string (caused `localhost:10480:10480` double-port)
Mount path disambiguation (partial):
- On EEXIST, read `NetFSMountURLSync` `mountpoints` array instead of hardcoding `/Volumes/{share}`
- Add `find_mount_path_for_share` fallback that scans `/Volumes/` via `statfs` to match server+share
- Full same-name-share collision handling (explicit mount points, volume switcher display) tracked separately
Dev experience fixes:
- Fix `effect_orphan`: move `initAiToastSync()` to synchronous `onMount` (before async IIFE)
- Fix SvelteKit HMR crash (sveltejs/kit#15287): `import.meta.hot.accept` + `invalidate()` in root layout forces clean reload instead of broken route-tree rebuild
- Fix UnoCSS HMR spam: restrict `content.filesystem` in `uno.config.ts` to the 3 files that use icons
Copy file name to clipboardExpand all lines: apps/desktop/src-tauri/src/network/CLAUDE.md
+10-3Lines changed: 10 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,7 +6,7 @@ Discover, browse, and mount SMB network shares. Works on macOS and Linux.
6
6
7
7
-**Discovery**: `mdns_discovery.rs` — Pure Rust mDNS using `mdns-sd` crate. Cross-platform.
8
8
-**Manual servers**: `manual_servers.rs` — User-added servers via "Connect to server..." dialog. Parses addresses, checks TCP reachability, persists to `manual-servers.json`, and injects synthetic `NetworkHost` entries with `source: Manual` into `DISCOVERY_STATE`. Loaded at startup.
9
-
-**E2E testing**: `virtual_smb_hosts.rs` — Injects synthetic `NetworkHost` entries for Docker SMB containers. Gated behind `smb-e2e` Cargo feature. Never enabled in production.
9
+
-**E2E testing**: `virtual_smb_hosts.rs` — Injects 14 synthetic `NetworkHost` entries for smb2's consumer Docker containers. Ports come from `smb2::testing::*_port()` functions (configurable via `SMB_CONSUMER_*_PORT` env vars, default 10480+). Hosts configurable via `SMB_E2E_*_HOST` env vars (default `localhost`). Gated behind `smb-e2e` Cargo feature. Never enabled in production.
-`smb_connection.rs` — TCP connection establishment and share listing via `smb2::SmbClient`
@@ -104,6 +104,13 @@ When the user mounts an SMB share, we establish a parallel smb2 connection along
104
104
105
105
Manual server IDs use the format `manual-{address}-{port}` with dots/colons replaced by dashes. This is deterministic (same address+port always produces the same ID), preventing duplicates. The `manual-` prefix avoids collision with mDNS-derived IDs.
106
106
107
+
### Mount path disambiguation for same-name shares
108
+
109
+
When two servers have a share with the same name (for example, two NAS devices both sharing `public`), macOS creates
110
+
disambiguated mount paths (`/Volumes/public`, `/Volumes/public-1`). The mount code reads the actual path from
111
+
`NetFSMountURLSync`'s `mountpoints` array on both success and EEXIST. If the array is empty (some macOS versions don't
112
+
populate it on EEXIST), `find_mount_path_for_share` scans `/Volumes/` and uses `statfs` to match the server+share.
113
+
107
114
## Gotchas
108
115
109
116
-**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.
@@ -113,7 +120,7 @@ Manual server IDs use the format `manual-{address}-{port}` with dots/colons repl
113
120
-**Account name is lowercase**: `make_account_name` lowercases server name for consistency. Prevents duplicate entries for "SERVER" vs "server".
114
121
-**Linux `gio mount` requires GVFS**: The `gvfs-smb` package must be installed. Standard on Ubuntu/Fedora GNOME desktops. KDE desktops may need it explicitly.
115
122
-**`ShareListError` uses internally tagged serde format** (`#[serde(tag = "type")]`) with struct variants. This keeps a flat JSON shape (`{ "type": "protocol_error", "message": "..." }`). The `MissingDependency` variant adds an optional `installCommand` field. When adding new variants, use struct syntax (not tuple).
116
-
-**macOS smbutil and NetFSMountURLSync fail with loopback IP + non-standard port**: `//127.0.0.1:9445` gives "Broken pipe", but `//localhost:9445` works. `build_smbutil_url` and `NetworkMountView.svelte` both fall back to hostname when IP is `127.0.0.1` or `::1`. This matters for E2E testing against Docker containers on localhost.
117
-
-**Mount URL must include port when non-standard**: `NetworkMountView.svelte` appends `:PORT` to the server string when `port !== 445`. Without this, `NetFSMountURLSync` defaults to port 445 and can't reach Docker containers on custom ports.
123
+
-**macOS smbutil and NetFSMountURLSync fail with loopback IP + non-standard port**: `//127.0.0.1:10480` gives "Broken pipe", but `//localhost:10480` works. `build_smbutil_url` and `NetworkMountView.svelte` both fall back to hostname when IP is `127.0.0.1` or `::1`. This matters for E2E testing against Docker containers on localhost.
124
+
-**Mount URL must include port when non-standard**: `mount_share_sync` builds `smb://server:port/share` for non-445 ports. The port is passed as a separate parameter through `mount_share` → `mount_share_sync`, not embedded in the server string (embedding it would cause `build_smb_addr`to double the port: `localhost:10480:10480`). `SmbMountInfo.port` extracts the port from `statfs` mount source for upgrade paths.
118
125
-**Strip `.local` from addr for smb2**: `smb2::Connection::connect()` extracts `server_name` from the addr string and uses it in UNC paths. Passing `"foo.local:445"` creates `\\foo.local\IPC$` which some servers reject. The `build_addr` helper in `smb_connection.rs` handles this.
119
126
-**Manual hosts always set `hostname`**: The share listing pipeline guards on `host.hostname` being truthy. `create_network_host` always sets `hostname` (to the address, even for IPs) so manual hosts flow through the pipeline correctly.
0 commit comments