#!/bin/bash

function die {
    echo $@ >&2
    exit 1
}

function show_help {
    cat << EOF
Usage: ${0##*/} -[hqnu] [PATH ...]

Clear out any XATTRs set by libvirt on all files that have them.
The idea is to reset refcounting, should it break.

  -h    display this help and exit
  -q    quiet (don't print which files are being fixed)
  -n    dry run; don't remove any XATTR just report the file name
  -u    unsafe; don't check whether there are running VMs; PATH must be specified

PATH can be specified to refine search to only to given path
instead of whole root ('/'), which is the default.
EOF
}

QUIET=0
DRY_RUN=0
UNSAFE=0

# So far only qemu and lxc drivers use security driver.
URI=("qemu:///system"
     "lxc:///system")

if [ $(whoami) != "root" ]; then
    die "Must be run as root"
fi

while getopts hqnu opt; do
    case $opt in
        h)
            show_help
            exit 0
            ;;
        q)
            QUIET=1
            ;;
        n)
            DRY_RUN=1
            ;;
        u)
            UNSAFE=1
            ;;
        *)
            show_help >&2
            exit 1
            ;;
    esac
done

case $(uname -s) in
    Linux)
        XATTR_PREFIX="trusted.libvirt.security"
        ;;

    FreeBSD)
        XATTR_PREFIX="system.libvirt.security"
        ;;

    *)
        die "$0 is not supported on this platform"
        ;;
esac


if [ ${DRY_RUN} -eq 0 ] && [ ${UNSAFE} -eq 0 ]; then
    for u in ${URI[*]} ; do
        if [ -n "`virsh -q -c $u list 2>/dev/null`" ]; then
            die "There are still some domains running for $u"
        fi
    done
fi


declare -a XATTRS
for i in "dac" "selinux"; do
    XATTRS+=("$XATTR_PREFIX.$i" "$XATTR_PREFIX.ref_$i" "$XATTR_PREFIX.timestamp_$i")
done

fix_xattrs() {
    local DIR="$1"

    for i in $(getfattr -R -d -m ${XATTR_PREFIX} --absolute-names ${DIR} 2>/dev/null | grep "^# file:" | cut -d':' -f 2); do
        if [ ${DRY_RUN} -ne 0 ]; then
            getfattr -d -m ${XATTR_PREFIX} --absolute-names $i
            continue
        fi

        if [ ${QUIET} -eq 0 ]; then
            echo "Fixing $i";
        fi
        for x in ${XATTRS[*]}; do
            setfattr -x $x $i
        done
    done
}


shift $((OPTIND - 1))
if [ $# -gt 0 ]; then
    while [ $# -gt 0 ]; do
        fix_xattrs "$1"
        shift $((OPTIND - 1))
    done
else
    if [ ${UNSAFE} -eq 1 ]; then
        die "Unsafe mode (-u) requires explicit 'PATH' argument"
    fi
    fix_xattrs "/"
fi
