#!/bin/sh -efu

fhs_config='/etc/sisyphus_check/fhs'

rpm_name="${rpm_name?RPM name required}"
rpm_filenames="${rpm_filenames?RPM file list required}"
rpm_arch="${rpm_arch?RPM arch required}"

fhs_exceptions()
{
	grep '^[^#].*[[:space:]].*/' "$fhs_config" |
	while read -r pkgname_pattern filename_re; do
		case "$rpm_name" in
			$pkgname_pattern) ;;
			*) continue ;;
		esac
		printf '%s\n' "$filename_re"
	done
}

# check for FHS violations
check_fhs()
{
	local f="$1" && shift || return 1

	# Do not check filesystem package.
	[ "$rpm_name" != filesystem ] || return 0

	local rc=0
	# libdir
	#
	# The same processor can run ELFs with different well-known ABIs.
	# The rpm_arch specifies the "default" ABI of the target system
	# (and hence the processor arch). We allow such a package to
	# include ELFs runnable on this processor with a "non-default" ABI.
	# ELFs compatible with a processor (say, x86_64), but with
	# different ABIs (32bit, 64bit, or x32) are put into different
	# dirs (lib, lib64, libx32), so that there is no collision between
	# potentially runnable ELFs.
	#
	# The regexp below is intended to allow exactly these dirs
	# for each processor arch determined by rpm_arch.
	#
	# (Note that we don't care about collisions between ELFs that can
	# never be run natively on the same processor, e.g., x86_64 and
	# aarch64 ELFs; the used paths are simple and don't distinguish
	# processor archs: lib, lib64. This approach is different to what
	# is done in Debian and for riscv by default.)
	local libsuffix_re=''
	case "$rpm_arch" in
		*64*|x32|e2k*|s390x) libsuffix_re="$libsuffix_re|64"
						;;
	esac
	case "$rpm_arch" in
		x32|x86_64) libsuffix_re="$libsuffix_re|x32"
						;;
	esac
	# An MCST tradition:
	case "$rpm_arch" in
		e2k*) libsuffix_re="$libsuffix_re|32"
						;;
	esac
	local fhs_re="^/(bin|boot|etc|lib($libsuffix_re)|run|sbin|usr/(bin|etc|games|include|lib(exec|$libsuffix_re)|sbin|share|src)|var/(cache|games|lib|lock|log|run|spool|www|yp))/.*"
	local bogus_re='^/var/lib/(cache|lib|lock|log|nis|run|spool|www|yp)/.*'
	local filenames bad_filenames except_re

	# Check packaged files.
	# NB: extra grep to exclude source packages.
	if [ -n "$rpm_filenames" ] &&
	   filenames=$(printf '%s\n' "$rpm_filenames" |grep '^/'); then
		if bad_filenames=$(printf '%s\n' "$filenames" |grep -Ev -e "$fhs_re"); then
			except_re=$(fhs_exceptions)
			if [ -z "$except_re" ] ||
			   bad_filenames=$(printf '%s\n' "$bad_filenames" |grep -Ev -e "$except_re"); then
				FileError "FHS violations: $(oneliner "$bad_filenames")" "$f"
				rc=1
			fi
		fi
		if bad_filenames=$(printf '%s\n' "$filenames" |grep -Ee "$bogus_re"); then
			FileError "Invalid path names: $(oneliner "$bad_filenames")" "$f"
			rc=1
		fi
	fi

	# Check provided paths.
	if [ -n "$rpm_provides" ] &&
	   filenames=$(printf '%s\n' "$rpm_provides" |grep -o '^/[^[:space:]]*'); then
		if bad_filenames=$(printf '%s\n' "$filenames" |grep -Ev -e "$fhs_re"); then
			except_re=$(fhs_exceptions)
			if [ -z "$except_re" ] ||
			   bad_filenames=$(printf '%s\n' "$bad_filenames" |grep -Ev -e "$except_re"); then
				FileError "FHS violations: provides $(oneliner "$bad_filenames")" "$f"
				rc=1
			fi
		fi
		if bad_filenames=$(printf '%s\n' "$filenames" |grep -Ee "$bogus_re"); then
			FileError "Invalid path names: $(oneliner "$bad_filenames")" "$f"
			rc=1
		fi
	fi

	return $rc
}

run_check() {
	if ! check_fhs "$1"; then
		CheckError 'FHS violation'
		return 1
	fi
}
