diff --git a/docker/main/rootfs/etc/s6-overlay/s6-rc.d/prepare/run b/docker/main/rootfs/etc/s6-overlay/s6-rc.d/prepare/run index 257d0d9574..deb454fd66 100755 --- a/docker/main/rootfs/etc/s6-overlay/s6-rc.d/prepare/run +++ b/docker/main/rootfs/etc/s6-overlay/s6-rc.d/prepare/run @@ -157,11 +157,29 @@ if [[ "$(id -u)" -eq 0 ]]; then # below it. A parent /media mount doesn't count: a dedicated # /media/frigate volume added later would be shadowed and skipped. sentinel_args=(--sentinel /config/.permissions_version) + root_services_mode="" + if [[ -n "${FRIGATE_ROOT_SERVICES:-}" ]]; then + # || true: grep -v exits non-zero on an all-empty list (e.g. ",") + # and errexit+pipefail would otherwise abort the boot over it + root_services_mode=$(tr ',' '\n' <<< "${FRIGATE_ROOT_SERVICES//[[:space:]]/}" | grep -v '^$' | sort -u | paste -sd, - || true) + if [[ -n "$root_services_mode" ]]; then + sentinel_args+=(--mode "$root_services_mode") + fi + fi if ! awk '$2 == "/media/frigate" || $2 ~ /^\/media\/frigate\//' /proc/mounts | grep -q .; then sentinel_args=() fi /usr/local/bin/fix-ownership "${sentinel_args[@]}" \ "${PUID:-1000}" "${PGID:-1000}" /config /media/frigate + + # Root services write some files as root while running (clips + # stragglers, caches, stale db journals). These trees are small, so + # realign them on every boot. Recordings are chowned at create and + # stay behind the sentinel sweep above. + if [[ -n "$root_services_mode" ]]; then + /usr/local/bin/fix-ownership \ + "${PUID:-1000}" "${PGID:-1000}" /config /media/frigate/clips /media/frigate/exports + fi fi fi diff --git a/docker/main/rootfs/usr/local/bin/fix-ownership b/docker/main/rootfs/usr/local/bin/fix-ownership index 6c230606e9..c6c6d6bc8c 100755 --- a/docker/main/rootfs/usr/local/bin/fix-ownership +++ b/docker/main/rootfs/usr/local/bin/fix-ownership @@ -1,13 +1,14 @@ #!/bin/bash # Single source of truth for aligning volume ownership with the runtime user. # -# Usage: fix-ownership [--dry-run] [--sentinel FILE] UID GID PATH [PATH...] +# Usage: fix-ownership [--dry-run] [--sentinel FILE] [--mode STRING] UID GID PATH [PATH...] # # --dry-run report what would change, touch nothing # --sentinel skip entirely when FILE already records "SCHEMA:UID:GID"; # write it after a successful run (used by the boot path so # multi-TB volumes are swept once per UID/schema change, not # on every boot) +# --mode append STRING to the sentinel, so changing it re-sweeps once # # Only files whose uid OR gid differs are touched, so re-runs are cheap. # lost+found is skipped: it belongs to the filesystem, not to Frigate, and @@ -28,6 +29,7 @@ schema=2 dry_run=0 sentinel="" +mode="" while [[ "${1:-}" == --* ]]; do case "$1" in @@ -38,12 +40,18 @@ while [[ "${1:-}" == --* ]]; do exit 2 fi sentinel="$2"; shift 2 ;; + --mode) + if [[ -z "${2:-}" ]]; then + echo "[ERROR] fix-ownership: --mode requires a value" >&2 + exit 2 + fi + mode="$2"; shift 2 ;; *) echo "[ERROR] fix-ownership: unknown option $1" >&2; exit 2 ;; esac done if [[ $# -lt 3 ]]; then - echo "Usage: fix-ownership [--dry-run] [--sentinel FILE] UID GID PATH..." >&2 + echo "Usage: fix-ownership [--dry-run] [--sentinel FILE] [--mode STRING] UID GID PATH..." >&2 exit 2 fi @@ -56,9 +64,18 @@ if [[ "$(id -u)" -ne 0 ]]; then exit 0 fi +# The mode string folds the FRIGATE_ROOT_SERVICES list into the sentinel, so +# entering or leaving a granular root mode re-sweeps once. That is the +# backstop for anything a root service created that the per-boot sweep and +# chown-at-create did not cover. +sentinel_content="${schema}:${target_uid}:${target_gid}" +if [[ -n "$mode" ]]; then + sentinel_content="${sentinel_content}:${mode}" +fi + # A dry run always inspects: the sentinel records what a past sweep did, not # what the volume looks like now, and reporting from it would hide later drift. -if [[ "$dry_run" -eq 0 && -n "$sentinel" && -f "$sentinel" && "$(cat "$sentinel")" == "${schema}:${target_uid}:${target_gid}" ]]; then +if [[ "$dry_run" -eq 0 && -n "$sentinel" && -f "$sentinel" && "$(cat "$sentinel")" == "$sentinel_content" ]]; then echo "[INFO] fix-ownership: ${target_uid}:${target_gid} (schema ${schema}) already applied, skipping" exit 0 fi @@ -152,6 +169,6 @@ if [[ "$dry_run" -eq 0 && -d /config ]]; then fi if [[ "$dry_run" -eq 0 && -n "$sentinel" && "$swept_clean" -eq 1 ]]; then - echo "${schema}:${target_uid}:${target_gid}" > "$sentinel" || \ + echo "$sentinel_content" > "$sentinel" || \ echo "[WARN] fix-ownership: could not write ${sentinel}; the sweep will run again on next boot" fi