Krolyxon
Commit 090f586

Branches

Refs →

aurora090f586

mounter: mount via udisks and gvfs

Author
krolyxon
Date
2026-10-19 22:50 +0530
Parent
23391c7
Commit
090f586a09b788e10f768447cccc1600d5ed1f9e

+132 −54

3 files changed

  1. M .local/bin/mounter +78 −38
  2. M .local/bin/unmounter +53 −16
  3. M packages/desktop.txt +1 −0
M .local/bin/mounter View
@@ -1,41 +1,81 @@
11 #!/bin/sh
2+# Menu to mount drives, LUKS partitions and Android phones, the standard way:
3+# udisks mounts drives to /run/media/$USER/<label> (fstab entries to their fstab
4+# target) and unlocks LUKS with a passphrase asked in aurora's runner; phones go
5+# through gvfs (/run/user/$UID/gvfs/mtp:host=...). Root is asked via polkit. See unmounter.
26
3−# Gives a dmenu prompt to mount unmounted drives. If
4−# they're in /etc/fstab, they'll be mounted automatically. Otherwise, you'll
5−# be prompted to give a mountpoint from already existsing directories. If you
6−# input a novel directory, it will prompt you to create that directory.
7−
8−getmount() { \
9− [ -z "$chosen" ] && exit 1
10− # shellcheck disable=SC2086
11− mp="$(find $1 2>/dev/null | aurora-run -d -p "Type in mount point.")" || exit 1
12− test -z "$mp" && exit 1
13− if [ ! -d "$mp" ]; then
14− mkdiryn=$(printf "No\\nYes" | aurora-run -d -p "$mp does not exist. Create it?") || exit 1
15− [ "$mkdiryn" = "Yes" ] && (mkdir -p "$mp" || sudo -A mkdir -p "$mp")
7+menu() { aurora-run -d -p "$1"; }
8+notify() { notify-send -a mounter "$@"; }
9+# Lock a LUKS device we unlocked if we bail out before mounting it.
10+cancel() { [ -n "$unlocked" ] && udisksctl lock -b "$unlocked" >/dev/null 2>&1; exit 1; }
11+die() { notify -u critical "💾 Mount failed" "$1"; cancel; }
12+
13+# "💾 /dev/sdb1 32G LABEL (vfat)" per unmounted filesystem; LUKS only while locked
14+# (an open one is listed as its mapper). jq instead of parsing lsblk: labels are arbitrary.
15+drives() {
16+ lsblk -Jpo NAME,TYPE,FSTYPE,SIZE,LABEL,MOUNTPOINT | jq -r '
17+ .. | objects | select(has("name"))
18+ | select((.type | test("^(part|disk|rom|crypt)$")) and .fstype != null and .mountpoint == null)
19+ | select(.fstype | test("^(swap|LVM2_member|linux_raid_member)$") | not)
20+ | select(.fstype != "crypto_LUKS" or ((.children // []) | length) == 0)
21+ | (if .fstype == "crypto_LUKS" then "🔒" else "💾" end) as $icon
22+ | "\($icon) \(.name) \(.size) \(if .label then .label + " " else "" end)(\(.fstype | sub("crypto_"; "")))"'
23+}
24+
25+# "📱 mtp://Google_Pixel_7_XXXX/ Pixel 7" per unmounted phone, from gvfs's volume list.
26+phones() {
27+ mounted="$(gio mount -l 2>/dev/null)"
28+ gio mount -li 2>/dev/null | awk '
29+ /^ *Volume\([0-9]+\): / { sub(/^ *Volume\([0-9]+\): /, ""); name = $0 }
30+ /^ *activation_root=mtp:\/\// { sub(/^ *activation_root=/, ""); print $0 "\t" name }' |
31+ while IFS=" " read -r uri name; do
32+ printf '%s\n' "$mounted" | grep -qF -- "-> $uri" && continue
33+ printf '📱 %s %s\n' "$uri" "$name"
34+ done
35+}
36+
37+mountphone() {
38+ uri="$1"
39+ err="$(gio mount "$uri" 2>&1)" ||
40+ die "$err
41+Unlock the phone and set USB to File transfer."
42+ notify "📱 Phone mounted" "${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/gvfs/mtp:host=$(echo "$uri" | sed 's|^mtp://||; s|/$||')"
43+}
44+
45+mountdrive() {
46+ dev="$1"
47+
48+ if [ "$(lsblk -dno FSTYPE "$dev")" = "crypto_LUKS" ]; then
49+ pass="$(aurora-run -d --password --lines=0 --prompt="🔒" --placeholder="Passphrase for $dev" </dev/null)" || exit 1
50+ out="$(printf '%s' "$pass" | udisksctl unlock -b "$dev" --key-file /dev/stdin 2>&1)" ||
51+ die "Could not unlock $dev: $out"
52+ unset pass
53+ unlocked="$dev"
54+ # "Unlocked /dev/sda2 as /dev/dm-3."
55+ dev="$(echo "$out" | sed -n 's/.* as \(.*\)\.$/\1/p')"
56+ udevadm settle 2>/dev/null
57+ [ -n "$(lsblk -dno FSTYPE "$dev")" ] || die "$unlocked unlocked, but has no filesystem on it."
1658 fi
17− }
18−
19−mountusb() { \
20− chosen="$(echo "$usbdrives" | aurora-run -d -p "Mount which drive?")" || exit 1
21− chosen="$(echo "$chosen" | awk '{print $1}')"
22− sudo -A mount "$chosen" 2>/dev/null && notify-send "💻 USB mounting" "$chosen mounted." && exit 0
23− alreadymounted=$(lsblk -nrpo "name,type,mountpoint" | awk '$3!~/\/boot|\/home$|SWAP/&&length($3)>1{printf "-not ( -path *%s -prune ) ",$3}')
24− getmount "/mnt /media /mount /home -maxdepth 5 -type d $alreadymounted"
25− partitiontype="$(lsblk -no "fstype" "$chosen")"
26− case "$partitiontype" in
27− "vfat") sudo -A mount -t vfat "$chosen" "$mp" -o rw,umask=0000;;
28− "exfat") sudo -A mount "$chosen" "$mp" -o uid="$(id -u)",gid="$(id -g)";;
29− *) sudo -A mount "$chosen" "$mp"; user="$(whoami)"; ug="$(groups | awk '{print $1}')"; sudo -A chown "$user":"$ug" "$mp";;
30− esac
31− notify-send "💻 USB mounting" "$chosen mounted to $mp."
32− }
33−
34−usbdrives="$(lsblk -rpo "name,type,size,mountpoint" | grep 'part\|rom' | awk '$4==""{printf "%s (%s)\n",$1,$3}')"
35−
36−if [ -z "$usbdrives" ]; then
37− echo "No USB drive detected" && exit
38−else
39− echo "USB drive(s) detected."
40− mountusb
41−fi
59+
60+ # "Mounted /dev/sdb1 at /run/media/k/LABEL" (older udisks add a trailing dot)
61+ out="$(udisksctl mount -b "$dev" 2>&1)" || die "$out"
62+ mp="$(echo "$out" | sed -n 's/^Mounted .* at \(.*\)$/\1/p' | sed 's/\.$//')"
63+
64+ # Linux filesystems keep their owner: offer to take the root dir of the drive.
65+ if [ -n "$mp" ] && [ ! -w "$mp" ]; then
66+ owner="$(stat -c %U "$mp")"
67+ [ "$(printf 'Yes\nNo' | menu "$mp is owned by $owner. Make it yours?")" = "Yes" ] &&
68+ pkexec chown "$(id -u):$(id -g)" "$mp"
69+ fi
70+ notify "💾 Mounted" "$dev → ${mp:-$out}"
71+}
72+
73+list="$(drives; phones)"
74+[ -z "$list" ] && { notify "💾 Mounter" "Nothing to mount."; exit 0; }
75+
76+chosen="$(echo "$list" | menu "Mount")" || exit 1
77+target="$(echo "$chosen" | awk '{print $2}')"
78+case "$chosen" in
79+ "📱"*) mountphone "$target" ;;
80+ *) mountdrive "$target" ;;
81+esac
M .local/bin/unmounter View
@@ -1,22 +1,59 @@
11 #!/bin/sh
2+# Menu to unmount drives and phones mounted with mounter (or anything else on a
3+# /dev device, except /, /boot, /efi and /home), via udisks and gvfs. LUKS mappers
4+# are locked again afterwards: automatically if udisks opened them (luks-<uuid>),
5+# else after asking.
26
3−# A dmenu prompt to unmount drives.
4−# Provides you with mounted partitions, select one to unmount.
5−# Drives mounted at /, /boot and /home will not be options to unmount.
7+menu() { aurora-run -d -p "$1"; }
8+notify() { notify-send -a unmounter "$@"; }
9+die() { notify -u critical "💾 Unmount failed" "$1"; exit 1; }
610
7−unmountusb() {
8− [ -z "$drives" ] && exit
9− chosen="$(echo "$drives" | aurora-run -d -p "Unmount which drive?")" || exit 1
10− chosen="$(echo "$chosen" | awk '{print $1}')"
11− [ -z "$chosen" ] && exit
12− sudo -A umount "$chosen" && notify-send "💻 USB unmounting" "$chosen unmounted."
13− }
11+# kind<TAB>target<TAB>source<TAB>fstype per mount; jq so paths with spaces survive.
12+# Phones are gvfs mounts: target = their name, source = mtp:// uri.
13+mounts="$(
14+ findmnt -Jo TARGET,SOURCE,FSTYPE | jq -r '
15+ .. | objects | select(has("target"))
16+ | select(.source | startswith("/dev/"))
17+ | select(.target | test("^/(boot|efi|home)?$") | not)
18+ | ["drive", .target, (.source | sub("\\[.*"; "")), .fstype] | @tsv'
19+ gio mount -l 2>/dev/null | sed -n 's/^ *Mount([0-9]*): \(.*\) -> \(mtp:\/\/.*\)$/phone\t\1\t\2\tmtp/p'
20+)"
21+[ -z "$mounts" ] && { notify "💾 Unmounter" "Nothing to unmount."; exit 0; }
1422
15−drives=$(lsblk -nrpo "name,type,size,mountpoint,label" | awk -F':' '{gsub(/ /,":")}$4!~/\/boot|\/efi|\/home$|SWAP/&&length($4)>1{printf "%s (%s) %s\n",$4,$3,$5}')
23+i="$(echo "$mounts" | while IFS=" " read -r kind target src fstype; do
24+ case "$kind" in
25+ phone) icon="📱" ;;
26+ *) [ "$(lsblk -dno TYPE "$src" 2>/dev/null)" = "crypt" ] && icon="🔒" || icon="💾" ;;
27+ esac
28+ printf '%s %s %s (%s)\n' "$icon" "$target" "$src" "$fstype"
29+done | aurora-run -d --index -p "Unmount")" || exit 1
30+[ "$i" -ge 0 ] 2>/dev/null || exit 1
31+IFS=" " read -r kind target src fstype <<EOF
32+$(echo "$mounts" | sed -n "$((i + 1))p")
33+EOF
1634
17−if [ -z "$drives" ]; then
18− echo "No drives to unmount." && exit
19−else
20− echo "Unmountable USB drive detected."
21− unmountusb
35+if [ "$kind" = "phone" ]; then
36+ err="$(gio mount -u "$src" 2>&1)" || die "$err"
37+ notify "📱 Unmounted" "$target"
38+ exit 0
2239 fi
40+
41+err="$(udisksctl unmount -b "$src" 2>&1)" || {
42+ pids="$(fuser -m "$target" 2>/dev/null)"
43+ [ -n "$pids" ] && err="$err
44+In use by: $(ps -o comm= -p "$(echo $pids | tr ' ' ',')" | sort -u | tr '\n' ' ')"
45+ die "$err"
46+}
47+notify "💾 Unmounted" "$target"
48+
49+# Lock the LUKS container behind a mapper, unless it's still mounted somewhere else.
50+[ "$(lsblk -dno TYPE "$src" 2>/dev/null)" = "crypt" ] || exit 0
51+findmnt -n --source "$src" >/dev/null && exit 0
52+luks="$(lsblk -nprso NAME "$src" | sed -n 2p)"
53+[ "$(lsblk -dno FSTYPE "$luks")" = "crypto_LUKS" ] || exit 0
54+case "$(basename "$src")" in
55+ luks-*) ;;
56+ *) [ "$(printf 'Yes\nNo' | menu "Also lock $luks ($(basename "$src"))?")" = "Yes" ] || exit 0 ;;
57+esac
58+err="$(udisksctl lock -b "$luks" 2>&1)" || die "Could not lock $luks: $err"
59+notify "🔒 Locked" "$luks"
M packages/desktop.txt View
@@ -37,3 +37,4 @@ kanshi
3737 nsxiv
3838 xdg-utils
3939 curl
40+udisks2