#!/usr/bin/env nix-shell #!nix-shell -i bash -p bash -p coreutils-full -p gocryptfs -p inotify-tools passfile="$1" # e.g. /run/gocryptfs/private.key conffile="$2" # e.g. /nix/persist/private/gocryptfs.conf passdir=$(dirname "$passfile") waitForPassfileOnce() { local timeout=$1 if [ -f "$passfile" ]; then return 0 else # wait for some file to be created inside the directory. # inotifywait returns 0 if the file was created. 1 or 2 if timeout was hit or it was interrupted by a different event. inotifywait --timeout "$timeout" --event create "$passdir" return 1 #< maybe it was created; we'll pick that up immediately, on next check fi } waitForPassfile() { # there's a race condition between testing the path and starting `inotifywait`. # therefore, use a retry loop. exponential backoff to decrease the impact of the race condition, # especially near the start of boot to allow for quick reboots even if/when i hit the race. for timeout in 4 4 8 8 8 8 16 16 16 16 16 16 16 16; do if waitForPassfileOnce "$timeout"; then return 0 fi done while true; do if waitForPassfileOnce 30; then return 0 fi done } validatePassword() { echo "validating password ..." if ! cat "$passfile" | gocryptfs-xray -dumpmasterkey "$conffile" > /dev/null; then echo "failed key validation" rm -f "$passfile" return 1 fi } waitForPassfile while ! validatePassword; do waitForPassfile done echo "key provisioned"