fix: share smb client persist — write a real .automount unit

x-systemd.automount in a unit file's Options= is only honored when read
from /etc/fstab, so persisted mounts never armed an automount; the unit was
also static (no [Install]) so enable was a no-op. Result: after a reboot the
share sat inactive and the disk never mounted on access.

persist now writes both <name>.mount and <name>.automount units (the
automount with WantedBy=multi-user.target) and enables/starts the automount;
unpersist stops/disables/removes both units.
This commit is contained in:
he
2026-08-14 10:31:45 -04:00
parent 1724096ea7
commit 93fb6b6b37
4 changed files with 29 additions and 13 deletions
+1 -1
View File
@@ -604,7 +604,7 @@ Use conventional prefixes: `feat:`, `fix:`, `docs:`, `refactor:`, `chore:`
| `bin/pos-network-scan` | 271 | Parallel ping sweep of CIDR |
| `bin/pos-share-nfs-client` | 138 | Mount NFS shares (ephemeral or persistent systemd mount units) |
| `bin/pos-share-nfs-server` | 134 | Manage the NFS kernel server (status, share/unshare exports, enable/disable) |
| `bin/pos-share-smb-client` | 202 | Mount SMB/CIFS shares (ephemeral or persistent systemd mount units) |
| `bin/pos-share-smb-client` | 217 | Mount SMB/CIFS shares (ephemeral or persistent systemd mount units) |
| `bin/pos-share-smb-server` | 253 | Manage the Samba server (status, share/unshare exports, users, enable/disable) |
| `bin/pos-share-usb-server` | 218 | USB Redirector server control (--ls, --share; prompts when args omitted) |
| `bin/pos-ssh-load-keys` | 31 | Load all SSH keys into the agent |
+1 -1
View File
@@ -255,7 +255,7 @@ Subcommands that need input prompt interactively when args are omitted.
| `pos share nfs server <cmd>` | `bin/pos-share-nfs-server` | Manage the NFS kernel server: `status`, `share <path> [client]`, `unshare <path>`, `list`, `reload`, `enable`, `disable` | Requires `nfs-kernel-server` (added to `preinstall.sh` PACKAGES). Exports live in `/etc/exports`; `share` is idempotent (replaces any existing line for the path) and runs `exportfs -ra`. Default client `*(rw,sync,no_subtree_check)` — the tool warns you to restrict it; help prints Tailscale CGNAT (`100.64.0.0/10`), WireGuard (`10.10.0.0/24`) and LAN examples. Mutating commands announce via `lib/notify.sh` |
| `pos share nfs client <cmd>` | `bin/pos-share-nfs-client` | Mount and manage NFS shares: `mount <server:export> <local-dir>`, `unmount <local-dir>`, `list`, `persist <server:export> <local-dir>`, `unpersist <local-dir>` | Requires `nfs-common` (added to `preinstall.sh` PACKAGES). `persist` writes a systemd `.mount` unit (`systemd-escape --path --suffix=mount`) with `After=network-online.target` / `Wants=network-online.target` — mounts only once all interfaces are up, no fstab edits to break boot — then `daemon-reload` + `enable --now`. `unpersist` stops/disables/removes the unit. `mount`/`persist` announce via `lib/notify.sh` |
| `pos share smb server <cmd>` | `bin/pos-share-smb-server` | Manage the Samba server: `status`, `share <path> [name] [--read-only|--guest|--users u1,u2]`, `unshare <name>`, `list`, `adduser <user>`, `deluser <user>`, `reload`, `enable`, `disable` | Requires `samba` (added to `preinstall.sh` PACKAGES). Shares are idempotent marker blocks (`# >>> pos-managed share: <name>``# <<< end pos-managed share`) in `/etc/samba/smb.conf` — hand edits outside the markers survive; `share` validates with `testparm` before applying and hot-reloads via `smbcontrol smbd reload-config`. Defaults rw + browsable; warns when unrestricted (guest or no `valid users`). `adduser`/`deluser` manage Samba accounts via `smbpasswd`. Mutating commands announce via `lib/notify.sh` |
| `pos share smb client <cmd>` | `bin/pos-share-smb-client` | Mount and manage SMB/CIFS shares: `mount <//server/share> <local-dir> [user]`, `unmount <local-dir>`, `list`, `persist <//server/share> <local-dir> [user]`, `unpersist <local-dir>` | Requires `cifs-utils` (added to `preinstall.sh` PACKAGES). With a user you are prompted for the Samba password — one-shot mounts use a throwaway chmod-600 credentials file, `persist` keeps one at `/etc/samba/credentials/<name>` (chmod 600). `persist` writes a systemd `.mount` unit (`systemd-escape --path --suffix=mount`) with `x-systemd.automount` + `_netdev` — mounts on first access, never blocks boot — then `daemon-reload` + `enable --now`. `unpersist` stops/disables/removes the unit + credentials. `list` shows active mounts (`findmnt -t cifs`) **and** persistent units (as automount shares aren't mounted until first access, they'd otherwise be invisible). `mount`/`persist` announce via `lib/notify.sh` |
| `pos share smb client <cmd>` | `bin/pos-share-smb-client` | Mount and manage SMB/CIFS shares: `mount <//server/share> <local-dir> [user]`, `unmount <local-dir>`, `list`, `persist <//server/share> <local-dir> [user]`, `unpersist <local-dir>` | Requires `cifs-utils` (added to `preinstall.sh` PACKAGES). With a user you are prompted for the Samba password — one-shot mounts use a throwaway chmod-600 credentials file, `persist` keeps one at `/etc/samba/credentials/<name>` (chmod 600). `persist` writes systemd `.mount` **and** `.automount` units (`systemd-escape --path --suffix=mount`) with `_netdev` — the automount defers the actual mount until first access, never blocks boot — then `daemon-reload` + `enable --now` the automount. `unpersist` stops/disables/removes both units + credentials. `list` shows active mounts (`findmnt -t cifs`) **and** persistent units (as automount shares aren't mounted until first access, they'd otherwise be invisible). `mount`/`persist` announce via `lib/notify.sh` |
### communication
+3 -2
View File
@@ -231,8 +231,9 @@ mounts use a throwaway chmod-600 credentials file, `persist` keeps one at
`/etc/samba/credentials/<name>` (chmod 600) and references it from the unit.
**Persistent mounts use systemd, not fstab.** `persist` writes a
`/etc/systemd/system/<mnt-name>.mount` unit (`systemd-escape`) with
`x-systemd.automount` + `_netdev`: the share is mounted **on first access**
`/etc/systemd/system/<mnt-name>.mount` unit (**and** a matching
`<mnt-name>.automount` unit, both `systemd-escape`d) with `_netdev`: the
automount is enabled and armed, and the share is mounted **on first access**
instead of at boot, so an unreachable SMB server can never hang boot (with
fstab it could). `enable --now` arms the automount immediately.
+24 -9
View File
@@ -20,9 +20,9 @@ Commands:
mount <//server/share> <local-dir> [user] One-shot mount (creates local-dir if needed)
unmount <local-dir> Unmount the share
list Show active + persistent SMB mounts
persist <//server/share> <local-dir> [user] Persistent mount via a systemd .mount unit
(automount — never blocks boot)
unpersist <local-dir> Stop, disable and remove the mount unit
persist <//server/share> <local-dir> [user] Persistent mount via systemd .mount + .automount
units (mounts on first access — never blocks boot)
unpersist <local-dir> Stop, disable and remove the mount/automount units
With no user, guest access is attempted. With a user, you are prompted for
the Samba password — one-shot mounts use a throwaway chmod-600 credentials
@@ -143,8 +143,10 @@ case "$cmd" in
validate_dir "$where"
unit="$(systemd-escape --path --suffix=mount "$where")"
auto_unit="${unit%.mount}.automount"
unit_file="${UNIT_DIR}/${unit}"
opts="$(mount_opts "$user"),_netdev,noexec,x-systemd.automount"
auto_file="${UNIT_DIR}/${auto_unit}"
opts="$(mount_opts "$user"),_netdev,noexec"
if [ -n "$user" ]; then
creds_file="$SMB_CREDS_DIR/$(basename "$where")"
sudo mkdir -p "$SMB_CREDS_DIR"
@@ -169,10 +171,20 @@ What=${what}
Where=${where}
Type=cifs
Options=${opts}
UNIT
cat <<UNIT | sudo tee "$auto_file" >/dev/null
[Unit]
Description=Automount of SMB share ${what} at ${where}
[Automount]
Where=${where}
[Install]
WantedBy=multi-user.target
UNIT
sudo systemctl daemon-reload
sudo systemctl enable --now "$unit"
log "Persistent SMB mount: ${what} → ${where} (${unit})"
sudo systemctl enable --now "$auto_unit"
log "Persistent SMB mount (automount): ${what} → ${where} (${auto_unit})"
notify_send "SMB mount persisted: ${what} → ${where}"
;;
@@ -182,17 +194,20 @@ UNIT
validate_dir "$where"
unit="$(systemd-escape --path --suffix=mount "$where")"
auto_unit="${unit%.mount}.automount"
unit_file="${UNIT_DIR}/${unit}"
auto_file="${UNIT_DIR}/${auto_unit}"
if [ ! -f "$unit_file" ]; then
if [ ! -f "$unit_file" ] && [ ! -f "$auto_file" ]; then
warn "No systemd mount unit for $where (${unit})"
exit 0
fi
sudo systemctl disable "$auto_unit" 2>/dev/null || true
sudo systemctl stop "$auto_unit" 2>/dev/null || true
sudo systemctl disable "$unit" 2>/dev/null || true
sudo systemctl stop "$unit" 2>/dev/null || true
sudo systemctl stop "${unit%.mount}.automount" 2>/dev/null || true
sudo rm -f "$unit_file"
sudo rm -f "$unit_file" "$auto_file"
sudo rm -f "$SMB_CREDS_DIR/$(basename "$where")"
sudo rmdir "$SMB_CREDS_DIR" 2>/dev/null || true
sudo systemctl daemon-reload