From 8ef4eb24a2c0a9c9ef7a476c8c57a911936b9ef8 Mon Sep 17 00:00:00 2001 From: Martin Bastien Date: Thu, 23 Jul 2026 09:10:16 -0400 Subject: [PATCH] Keep Btrfs snapshots out of the locate index and put /home in it The stock Arch updatedb.conf interacts badly with Omarchy's Btrfs layout in both directions: - Snapper snapshots under /.snapshots are nested subvolumes reached by plain directory traversal, so updatedb indexes the entire system once per snapshot. On machines that accumulated snapshots this means multi-hour updatedb runs at full CPU, gigabytes of RAM, and a multi-gigabyte plocate.db (observed: 18 GB db, 7.5 h runs at 96% CPU with 592 snapshots; 43 MB and ~1 min after the fix). - PRUNE_BIND_MOUNTS="yes" treats Btrfs subvolume mounts like /home as bind mounts, so locate finds nothing in home directories at all. Configure updatedb.conf at install time and migrate existing installs, then rebuild the index in the background. Both settings are matched tolerantly and appended when absent, so a hand-edited updatedb.conf is fixed rather than silently skipped. --- install/config/all.sh | 1 + install/config/locate.sh | 23 +++++++++ migrations/1784809451.sh | 30 +++++++++++ test/shell.d/locate-test.sh | 100 ++++++++++++++++++++++++++++++++++++ 4 files changed, 154 insertions(+) create mode 100644 install/config/locate.sh create mode 100644 migrations/1784809451.sh create mode 100644 test/shell.d/locate-test.sh diff --git a/install/config/all.sh b/install/config/all.sh index e416b7bc..c4c70752 100644 --- a/install/config/all.sh +++ b/install/config/all.sh @@ -4,5 +4,6 @@ run_logged "$OMARCHY_INSTALL/config/lockscreen-pam.sh" run_logged "$OMARCHY_INSTALL/config/fix-powerprofilesctl-shebang.sh" run_logged "$OMARCHY_INSTALL/config/docker.sh" run_logged "$OMARCHY_INSTALL/config/snapper.sh" +run_logged "$OMARCHY_INSTALL/config/locate.sh" run_logged "$OMARCHY_INSTALL/config/enable-services.sh" run_logged "$OMARCHY_INSTALL/config/firewall.sh" diff --git a/install/config/locate.sh b/install/config/locate.sh new file mode 100644 index 00000000..d532f5df --- /dev/null +++ b/install/config/locate.sh @@ -0,0 +1,23 @@ +UPDATEDB_CONF_PATH="${OMARCHY_UPDATEDB_CONF_PATH:-/etc/updatedb.conf}" + +echo "Configuring locate to skip Btrfs snapshots and index Btrfs subvolumes" + +[[ -f $UPDATEDB_CONF_PATH ]] || exit 0 + +# Btrfs subvolume mounts (like /home) look like bind mounts, so pruning +# bind mounts leaves them out of the index entirely. +if grep -qE '^PRUNE_BIND_MOUNTS[[:space:]]*=' "$UPDATEDB_CONF_PATH"; then + sed -i -E 's|^PRUNE_BIND_MOUNTS[[:space:]]*=.*|PRUNE_BIND_MOUNTS = "no"|' "$UPDATEDB_CONF_PATH" +else + printf '%s\n' 'PRUNE_BIND_MOUNTS = "no"' >>"$UPDATEDB_CONF_PATH" +fi + +# Snapper snapshots are nested subvolumes reached by plain directory +# traversal, so without this updatedb indexes the system once per snapshot. +if ! grep -E '^PRUNEPATHS[[:space:]]*=' "$UPDATEDB_CONF_PATH" | grep -qF '/.snapshots'; then + if grep -qE '^PRUNEPATHS[[:space:]]*=[[:space:]]*"' "$UPDATEDB_CONF_PATH"; then + sed -i -E 's|^(PRUNEPATHS[[:space:]]*=[[:space:]]*")|\1/.snapshots |' "$UPDATEDB_CONF_PATH" + else + printf '%s\n' 'PRUNEPATHS = "/.snapshots"' >>"$UPDATEDB_CONF_PATH" + fi +fi diff --git a/migrations/1784809451.sh b/migrations/1784809451.sh new file mode 100644 index 00000000..0cf9d1a0 --- /dev/null +++ b/migrations/1784809451.sh @@ -0,0 +1,30 @@ +echo "Configure locate to skip Btrfs snapshots and index Btrfs subvolumes" + +OMARCHY_PATH="${OMARCHY_PATH:-/usr/share/omarchy}" +locate_config_script=/usr/share/omarchy/install/config/locate.sh +if [[ ! -f $locate_config_script ]]; then + locate_config_script="$OMARCHY_PATH/install/config/locate.sh" +fi + +UPDATEDB_CONF_PATH="${OMARCHY_UPDATEDB_CONF_PATH:-/etc/updatedb.conf}" + +as_root() { + if (( EUID == 0 )); then + "$@" + else + sudo "$@" + fi +} + +[[ -f $UPDATEDB_CONF_PATH ]] || exit 0 + +if grep -q '^PRUNE_BIND_MOUNTS = "no"' "$UPDATEDB_CONF_PATH" && + grep -E '^PRUNEPATHS' "$UPDATEDB_CONF_PATH" | grep -qF '/.snapshots'; then + exit 0 +fi + +as_root env OMARCHY_UPDATEDB_CONF_PATH="$UPDATEDB_CONF_PATH" bash -euo pipefail "$locate_config_script" + +# Rebuild the index with the new exclusions; pruning /.snapshots turns +# multi-hour runs on snapshot-heavy systems back into one-minute runs. +as_root systemctl start --no-block plocate-updatedb.service >/dev/null 2>&1 || true diff --git a/test/shell.d/locate-test.sh b/test/shell.d/locate-test.sh new file mode 100644 index 00000000..700b5bd2 --- /dev/null +++ b/test/shell.d/locate-test.sh @@ -0,0 +1,100 @@ +#!/bin/bash + +set -euo pipefail + +source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh" + +config_script="$ROOT/install/config/locate.sh" + +test_tmp=$(mktemp -d) +trap 'rm -rf "$test_tmp"' EXIT + +stock_conf() { + cat >"$1" <<'CONF' +PRUNE_BIND_MOUNTS = "yes" +PRUNEFS = "9p afs autofs cifs fuse nfs nfs4 proc sysfs tmpfs" +PRUNENAMES = ".git .hg .svn" +PRUNEPATHS = "/afs /media /mnt /net /sfs /tmp /udev /var/cache /var/lib/pacman/local /var/lock /var/run /var/spool /var/tmp" +CONF +} + +conf="$test_tmp/updatedb.conf" +stock_conf "$conf" + +OMARCHY_UPDATEDB_CONF_PATH="$conf" bash -euo pipefail "$config_script" >/dev/null + +grep -qFx 'PRUNE_BIND_MOUNTS = "no"' "$conf" || fail "locate config indexes Btrfs subvolume mounts like /home" +grep -qF 'PRUNEPATHS = "/.snapshots /afs' "$conf" || fail "locate config prunes /.snapshots" +pass "locate config skips Btrfs snapshots and indexes Btrfs subvolumes" + +OMARCHY_UPDATEDB_CONF_PATH="$conf" bash -euo pipefail "$config_script" >/dev/null + +[[ $(grep -o '/\.snapshots' "$conf" | wc -l) -eq 1 ]] || fail "locate config is idempotent" +pass "locate config leaves an already-configured file alone" + +OMARCHY_UPDATEDB_CONF_PATH="$test_tmp/missing.conf" bash -euo pipefail "$config_script" >/dev/null +pass "locate config tolerates a missing updatedb.conf" + +# A hand-edited updatedb.conf may drop the settings entirely, or write them +# without the spaces around the "=" that the stock Arch file uses. +conf="$test_tmp/sparse-updatedb.conf" +printf '%s\n' 'PRUNENAMES = ".git .hg .svn"' >"$conf" + +OMARCHY_UPDATEDB_CONF_PATH="$conf" bash -euo pipefail "$config_script" >/dev/null + +grep -qFx 'PRUNE_BIND_MOUNTS = "no"' "$conf" || fail "locate config adds a missing PRUNE_BIND_MOUNTS" +grep -qFx 'PRUNEPATHS = "/.snapshots"' "$conf" || fail "locate config adds a missing PRUNEPATHS" +pass "locate config adds settings a hand-edited updatedb.conf is missing" + +conf="$test_tmp/unspaced-updatedb.conf" +printf '%s\n' 'PRUNE_BIND_MOUNTS="yes"' 'PRUNEPATHS="/tmp /var/tmp"' >"$conf" + +OMARCHY_UPDATEDB_CONF_PATH="$conf" bash -euo pipefail "$config_script" >/dev/null + +grep -qFx 'PRUNE_BIND_MOUNTS = "no"' "$conf" || fail "locate config rewrites an unspaced PRUNE_BIND_MOUNTS" +grep -qFx 'PRUNEPATHS="/.snapshots /tmp /var/tmp"' "$conf" || fail "locate config prunes /.snapshots in an unspaced PRUNEPATHS" +[[ $(grep -c '^PRUNEPATHS' "$conf") -eq 1 ]] || fail "locate config keeps a single PRUNEPATHS setting" +pass "locate config handles updatedb.conf written without spaces around =" + +locate_migration=$(grep -rl 'Configure locate to skip Btrfs snapshots' "$ROOT/migrations" | head -n 1 || true) +[[ -n $locate_migration ]] || fail "locate migration exists" + +fake_bin="$test_tmp/bin" +mkdir -p "$fake_bin" + +cat >"$fake_bin/sudo" <<'STUB' +#!/bin/bash +exec "$@" +STUB +chmod +x "$fake_bin/sudo" + +cat >"$fake_bin/systemctl" <<'STUB' +#!/bin/bash +printf 'systemctl %s\n' "$*" >>"$TEST_LOG" +STUB +chmod +x "$fake_bin/systemctl" + +conf="$test_tmp/migration-updatedb.conf" +stock_conf "$conf" + +TEST_LOG="$test_tmp/calls.log" \ +PATH="$fake_bin:$PATH" \ +OMARCHY_PATH="$ROOT" \ +OMARCHY_UPDATEDB_CONF_PATH="$conf" \ + bash -euo pipefail "$locate_migration" >/dev/null + +grep -qFx 'PRUNE_BIND_MOUNTS = "no"' "$conf" || fail "locate migration rewrites updatedb.conf" +grep -qF 'PRUNEPATHS = "/.snapshots /afs' "$conf" || fail "locate migration prunes /.snapshots" +grep -qFx 'systemctl start --no-block plocate-updatedb.service' "$test_tmp/calls.log" || fail "locate migration rebuilds the locate index without blocking" +pass "locate migration fixes existing installs and rebuilds the index" + +: >"$test_tmp/calls.log" + +TEST_LOG="$test_tmp/calls.log" \ +PATH="$fake_bin:$PATH" \ +OMARCHY_PATH="$ROOT" \ +OMARCHY_UPDATEDB_CONF_PATH="$conf" \ + bash -euo pipefail "$locate_migration" >/dev/null + +[[ ! -s $test_tmp/calls.log ]] || fail "locate migration skips already-configured installs" +pass "locate migration is a no-op once updatedb.conf is configured"