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-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-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-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-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-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 | | `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 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 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 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 ### 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. `/etc/samba/credentials/<name>` (chmod 600) and references it from the unit.
**Persistent mounts use systemd, not fstab.** `persist` writes a **Persistent mounts use systemd, not fstab.** `persist` writes a
`/etc/systemd/system/<mnt-name>.mount` unit (`systemd-escape`) with `/etc/systemd/system/<mnt-name>.mount` unit (**and** a matching
`x-systemd.automount` + `_netdev`: the share is mounted **on first access** `<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 instead of at boot, so an unreachable SMB server can never hang boot (with
fstab it could). `enable --now` arms the automount immediately. 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) mount <//server/share> <local-dir> [user] One-shot mount (creates local-dir if needed)
unmount <local-dir> Unmount the share unmount <local-dir> Unmount the share
list Show active + persistent SMB mounts list Show active + persistent SMB mounts
persist <//server/share> <local-dir> [user] Persistent mount via a systemd .mount unit persist <//server/share> <local-dir> [user] Persistent mount via systemd .mount + .automount
(automount — never blocks boot) units (mounts on first access — never blocks boot)
unpersist <local-dir> Stop, disable and remove the mount unit 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 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 the Samba password — one-shot mounts use a throwaway chmod-600 credentials
@@ -143,8 +143,10 @@ case "$cmd" in
validate_dir "$where" validate_dir "$where"
unit="$(systemd-escape --path --suffix=mount "$where")" unit="$(systemd-escape --path --suffix=mount "$where")"
auto_unit="${unit%.mount}.automount"
unit_file="${UNIT_DIR}/${unit}" 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 if [ -n "$user" ]; then
creds_file="$SMB_CREDS_DIR/$(basename "$where")" creds_file="$SMB_CREDS_DIR/$(basename "$where")"
sudo mkdir -p "$SMB_CREDS_DIR" sudo mkdir -p "$SMB_CREDS_DIR"
@@ -169,10 +171,20 @@ What=${what}
Where=${where} Where=${where}
Type=cifs Type=cifs
Options=${opts} 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 UNIT
sudo systemctl daemon-reload sudo systemctl daemon-reload
sudo systemctl enable --now "$unit" sudo systemctl enable --now "$auto_unit"
log "Persistent SMB mount: ${what} → ${where} (${unit})" log "Persistent SMB mount (automount): ${what} → ${where} (${auto_unit})"
notify_send "SMB mount persisted: ${what} → ${where}" notify_send "SMB mount persisted: ${what} → ${where}"
;; ;;
@@ -182,17 +194,20 @@ UNIT
validate_dir "$where" validate_dir "$where"
unit="$(systemd-escape --path --suffix=mount "$where")" unit="$(systemd-escape --path --suffix=mount "$where")"
auto_unit="${unit%.mount}.automount"
unit_file="${UNIT_DIR}/${unit}" 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})" warn "No systemd mount unit for $where (${unit})"
exit 0 exit 0
fi 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 disable "$unit" 2>/dev/null || true
sudo systemctl stop "$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" "$auto_file"
sudo rm -f "$unit_file"
sudo rm -f "$SMB_CREDS_DIR/$(basename "$where")" sudo rm -f "$SMB_CREDS_DIR/$(basename "$where")"
sudo rmdir "$SMB_CREDS_DIR" 2>/dev/null || true sudo rmdir "$SMB_CREDS_DIR" 2>/dev/null || true
sudo systemctl daemon-reload sudo systemctl daemon-reload