Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
at devShellTools-shell 89 lines 3.2 kB view raw
1# shellcheck shell=bash 2 3# Guard against double inclusion. 4if (("${noBrokenSymlinksHookInstalled:-0}" > 0)); then 5 nixInfoLog "skipping because the hook has been propagated more than once" 6 return 0 7fi 8declare -ig noBrokenSymlinksHookInstalled=1 9 10# symlinks are often created in postFixup 11# don't use fixupOutputHooks, it is before postFixup 12postFixupHooks+=(noBrokenSymlinksInAllOutputs) 13 14# A symlink is "dangling" if it points to a non-existent target. 15# A symlink is "reflexive" if it points to itself. 16# A symlink is "unreadable" if the readlink command fails, e.g. because of permission errors. 17# A symlink is considered "broken" if it is either dangling, reflexive or unreadable. 18noBrokenSymlinks() { 19 local -r output="${1:?}" 20 local path 21 local pathParent 22 local symlinkTarget 23 local -i numDanglingSymlinks=0 24 local -i numReflexiveSymlinks=0 25 local -i numUnreadableSymlinks=0 26 27 # NOTE(@connorbaker): This hook doesn't check for cycles in symlinks. 28 29 if [[ ! -e $output ]]; then 30 nixWarnLog "skipping non-existent output $output" 31 return 0 32 fi 33 nixInfoLog "running on $output" 34 35 # NOTE: path is absolute because we're running `find` against an absolute path (`output`). 36 while IFS= read -r -d $'\0' path; do 37 pathParent="$(dirname "$path")" 38 if ! symlinkTarget="$(readlink "$path")"; then 39 nixErrorLog "the symlink $path is unreadable" 40 numUnreadableSymlinks+=1 41 continue 42 fi 43 44 # Canonicalize symlinkTarget to an absolute path. 45 if [[ $symlinkTarget == /* ]]; then 46 nixInfoLog "symlink $path points to absolute target $symlinkTarget" 47 else 48 nixInfoLog "symlink $path points to relative target $symlinkTarget" 49 # Use --no-symlinks to avoid dereferencing again and --canonicalize-missing to avoid existence 50 # checks at this step (which can lead to infinite recursion). 51 symlinkTarget="$(realpath --no-symlinks --canonicalize-missing "$pathParent/$symlinkTarget")" 52 fi 53 54 # use $TMPDIR like audit-tmpdir.sh 55 if [[ $symlinkTarget = "$TMPDIR"/* ]]; then 56 nixErrorLog "the symlink $path points to $TMPDIR directory: $symlinkTarget" 57 numDanglingSymlinks+=1 58 continue 59 fi 60 if [[ $symlinkTarget != "$NIX_STORE"/* ]]; then 61 nixInfoLog "symlink $path points outside the Nix store; ignoring" 62 continue 63 fi 64 65 if [[ $path == "$symlinkTarget" ]]; then 66 nixErrorLog "the symlink $path is reflexive" 67 numReflexiveSymlinks+=1 68 elif [[ ! -e $symlinkTarget ]]; then 69 nixErrorLog "the symlink $path points to a missing target: $symlinkTarget" 70 numDanglingSymlinks+=1 71 else 72 nixDebugLog "the symlink $path is irreflexive and points to a target which exists" 73 fi 74 done < <(find "$output" -type l -print0) 75 76 if ((numDanglingSymlinks > 0 || numReflexiveSymlinks > 0 || numUnreadableSymlinks > 0)); then 77 nixErrorLog "found $numDanglingSymlinks dangling symlinks, $numReflexiveSymlinks reflexive symlinks and $numUnreadableSymlinks unreadable symlinks" 78 exit 1 79 fi 80 return 0 81} 82 83noBrokenSymlinksInAllOutputs() { 84 if [[ -z ${dontCheckForBrokenSymlinks-} ]]; then 85 for output in $(getAllOutputNames); do 86 noBrokenSymlinks "${!output}" 87 done 88 fi 89}