#!/bin/sh
# mkinitrd
#
# Written by Erik Troan <ewt@redhat.com>
#
# Contributors:
#	Elliot Lee <sopwith@cuc.edu>
#	Miguel de Icaza <miguel@nuclecu.unam.mx>
#	Christian 'Dr. Disk' Hechelmann <drdisk@ds9.au.s.shuttle.de>
#	Michael K. Johnson <johnsonm@redhat.com>
#	Pierre Habraken <Pierre.Habraken@ujf-grenoble.fr>
#	Jakub Jelinek <jj@ultra.linux.cz>
#	Carlo Arenas Belon (carenas@chasqui.lared.net.pe>
#	Keith Owens <kaos@ocs.com.au>
#
# Rewritten by Dmitry V. Levin <ldv@altlinux.org>
# Initramfs support and other changes by Sergey Vlasov <vsu@altlinux.org>

PROG=mkinitrd
VERSION=3.0.18
CATCHED=

# override PATH
export PATH="/bin:/sbin:/usr/bin:/usr/sbin"

umask 077

Exit()
{
	local rc=$?
	[ -z "$1" ] || rc="$1"
	CATCHED=1
	exit $rc
}

Warning()
{
	echo "$PROG: warning: $*" >&2
}

Fatal()
{
	echo "$PROG: $*" >&2
	Exit 1
}

verbose=
Verbose()
{
	[ -n "$verbose" ] || return 0
	echo "$PROG: $*"
}

debug=
Debug()
{
	[ -n "$debug" ] || return 0
	echo "$PROG: $*"
}

is_yes()
{
	# Test syntax	
	if [ $# = 0 ]; then
		return 2
	fi

	# Check value
	case "$1" in
		yes|Yes|YES|true|True|TRUE|on|On|ON|Y|y|1)
			# true returns zero
			return 0
		;;
		*)
			# false returns one
			return 1
		;;
	esac
}

is_no()
{
	# Test syntax
	if [ $# = 0 ] ; then
		return 2
	fi

	case "$1" in
		no|No|NO|false|False|FALSE|off|Off|OFF|N|n|0)
			# true returns zero
			return 0
			;;
		*)
			# false returns one
			return 1
		;;
	esac
}

WORKDIR=
IMAGE=
MNTDIR=
MNTPOINT=
IMAGESIZE=
INODES=

exit_handler()
{
	local rc=$?
	trap - EXIT
	[ -n "$CATCHED" -o $rc -eq 0 ] ||
		echo "$PROG: unhandled error, exiting..."
	[ -n "$MNTPOINT" ] && umount "$MNTPOINT" >/dev/null 2>&1 ||:
	[ -z "$WORKDIR" ] || rm -rf "$WORKDIR"
	exit $rc
}

signal_handler()
{
	echo 'Interrupted!' >&2
	Exit 1
}

trap exit_handler EXIT
trap signal_handler HUP INT TERM PIPE QUIT

NormalizedName()
{
	echo -n "X$1" | sed -e '1s/^X//' -e 'y/-/_/'
}

MODPROBE_TYPE=
USE_COMPRESS=1
COMPRESSOR=
TARGET=
KERNEL=
KERNEL_MAJOR=
KERNEL_MINOR=
KERNEL_PATCH=
ADD_BOOTSPLASH=1
strict=
noscsi=
noide=
noraid=
pause=
MODULES=
EXTRA_MODULES=
MODULES_DIR=
MODULES_CONF=
HAVE_RAID=
RAID_MODULES=
SCSI_MODULES=
IDE_MODULES=
FOUND_NON_PCI_IDE=
ADD_FILES=
PRELOAD_MODNAMES=
POSTLOAD_MODNAMES=
EXTRA_MODNAMES=
FIRMWARE_DIRS="/lib/firmware /usr/lib/hotplug/firmware /usr/local/lib/firmware"
LSPCI=/usr/bin/lspci
MDADM=/sbin/mdadm
MDASSEMBLE=/sbin/mdassemble
FSTAB_FILE=/etc/fstab
MDADM_CONF=/etc/mdadm.conf
BIN_SPLASH=/sbin/splash
PRE_SCSI_MODNAMES="scsi_mod sd_mod"
PRE_SCSI_MODNAMES_ADDED=
IGNORE_MODNAMES="`NormalizedName "$IGNORE_MODNAMES"`"
IGNORE_MODNAMES=" $IGNORE_MODNAMES ppa imm ide_scsi usb_storage"
LOADED_MODNAMES=
ROOTDEV=
ROOTFS=
# LVM constants
HAVE_LVM=
LVM_MODNAME="dm-mod"
LVM=/sbin/lvm.static
DMSETUP=/sbin/dmsetup.static
NBD_TYPE=

loopDev=
loopFs=
loopFile=

[ -s /etc/sysconfig/$PROG ] && . /etc/sysconfig/$PROG

[ -x "$MDADM" ] || MDADM=/usr/sbin/mdadm

[ -x "$BIN_SPLASH" ] || ADD_BOOTSPLASH=

uname_r=`uname -r`

[ -x "$LVM" ] || LVM=
[ -x "$DMSETUP" ] || DMSETUP=

ParseKernelVersion()
{
	KERNEL_MAJOR="${KERNEL%%.*}"
	KERNEL_MINOR="${KERNEL#*.}"
	KERNEL_MINOR="${KERNEL_MINOR%%.*}"
	KERNEL_PATCH="${KERNEL#*.*.}"
	KERNEL_PATCH="${KERNEL_PATCH%%[^0-9]*}"

	if [ -z "${KERNEL_MAJOR##*[^0-9]*}" ] \
	   || [ -z "${KERNEL_MINOR##*[^0-9]*}" ] \
	   || [ -z "${KERNEL_PATCH##*[^0-9]*}" ] ; then
		Fatal "Invalid kernel version \"$KERNEL\""
	fi
}

KernelVersionAtLeast()
{
	local major="$1" && shift
	local minor="$1" && shift
	local patch="$1" && shift

	[ "$KERNEL_MAJOR" -ge "$major" ] || return 1
	[ "$KERNEL_MAJOR" -eq "$major" ] || return 0

	[ "$KERNEL_MINOR" -ge "$minor" ] || return 1
	[ "$KERNEL_MINOR" -eq "$minor" ] || return 0

	[ "$KERNEL_PATCH" -ge "$patch" ] || return 1
	return 0
}

InList()
{
	local value="$1" list="$2" i
	for i in $list; do
		[ "$i" != "$value" ] || return 0
	done
	return 1
}

ModuleAlreadyLoaded()
{
	local name="$1" m
	for m in $LOADED_MODNAMES; do
		[ "$m" != "$name" ] || return 0
	done
	LOADED_MODNAMES="$LOADED_MODNAMES
$name"
	return 1
}

IgnoredModule()
{
	local name="$1" m
	for m in off null $IGNORE_MODNAMES; do
		[ "$m" != "$name" ] || return 0
	done
	return 1
}

PreScsiModule()
{
	local name="$1" m
	for m in $PRE_SCSI_MODNAMES; do
		[ "$m" != "$name" ] || return 0
	done
	return 1
}

ListModuleFiles()
{
	case "$MODPROBE_TYPE" in
		modutils)
			modprobe --kernel-release "$KERNEL" \
				--list-module-files "$@" 2>/dev/null
			;;
		module-init-tools|kmod)
			modprobe --set-version="$KERNEL" --show-depends -i \
				"$@" 2>/dev/null
			;;
		*) Fatal "Unknown modprobe type '$MODPROBE_TYPE'" ;;
	esac
}

ModuleExists()
{
	local name="$1" list m
	list=`ListModuleFiles "$name"`
	if [ $? -ne 0 ]; then
		Debug "Module \"$name\" does not exist"
		return 1
	fi
	for m in $list; do
		if [ -z "${m##/lib/modules/*}" ]; then
			Debug "Module \"$name\" exists"
			return 0
		fi
	done
	Debug "Module \"$name\" seems to be aliased to null"
	return 1
}

AddModuleFirmware()
{
	local mod_name="$1" && shift
	local mod_path="$1" && shift
	local fw_list fw_name fw_dir fw_file

	fw_list="$(modinfo -F firmware "$mod_path")" || return
	[ -n "$fw_list" ] || return
	for fw_name in $fw_list; do
		fw_file=
		for fw_dir in $FIRMWARE_DIRS; do
			if [ -r "$fw_dir/$KERNEL/$fw_name" ]; then
				fw_name="$KERNEL/$fw_name"
				fw_file="$fw_dir/$fw_name"
				break
			elif [ -r "$fw_dir/$fw_name" ]; then
				fw_file="$fw_dir/$fw_name"
				break
			fi
		done
		[ -n "$fw_file" ] || {
			Warning "Firmware file \"$fw_name\" for module \"$mod_name\" not found"
			continue
		}
		Verbose "Adding firmware file \"$fw_file\" for module \"$mod_name\""
		ADD_FILES="$ADD_FILES
/lib/firmware/$fw_name=$fw_file"
	done
}

AddModuleFile()
{
	local path="$1" name="$1"
	name="${name##*/}"
	name="${name%.gz}"
	name="${name%.o}"
	name="${name%.ko}"
	name="`NormalizedName "$name"`"

	if ModuleAlreadyLoaded "$name"; then
		return 0
	fi

	if IgnoredModule "$name"; then
		Warning "Module \"$name\" cannot be ignored because other modules depend on it"
	fi

	Debug "Found module \"$name\" as $path"
	MODULES="$MODULES $path"

	AddModuleFirmware "$name" "$path"
}

AddModuleFiles()
{
	local m
	for m in $@; do
		AddModuleFile $m
	done
}

FindModule()
{
	local skip_errors= name="$1"
	if [ -z "${name##-*}" ]; then
		skip_errors=1
		name="${name#-*}"
	fi
	name="${name%.gz}"
	name="${name%.o}"
	name="${name%.ko}"
	name="`NormalizedName "$name"`"

	if IgnoredModule "$name"; then
		Debug "Ignoring \"$name\" module"
		return 0
	fi

	Debug "Looking for \"$name\" module"

	local list

	list=`ListModuleFiles "$name"`
	if [ $? -ne 0 ]; then
		if [ -n "$skip_errors" ]; then
			Debug "Ignoring missing \"$name\" module"
			return 0
		fi

		if PreScsiModule "$name"; then
			Debug "Ignoring missing \"$name\" SCSI module"
			return 0
		fi

		echo "No module \"$name\" found for kernel $KERNEL" >&2
		[ -z "$strict" ] && return 1 || Exit 1
	fi

	local m
	for m in $list; do
		[ -z "${m##/lib/modules/*}" ] || continue
		AddModuleFile "$m"
	done
}

FindModules()
{
	local n
	for n in "$@"; do
		FindModule "$n"
	done
}

FindModulesFromCommandLine()
{
	local IGNORE_MODNAMES=
	FindModules "$@"
}

FindExtraModulesFromCommandLine()
{
	local MODULES=
	FindModulesFromCommandLine "$@"
	EXTRA_MODULES="$MODULES"
}

# Detect module kind by examining its dependencies.
# Currently these kinds are detected:
#  - "block"	- standalone block device driver;
#  - "scsi"	- SCSI host adapter driver;
#  - "ide"	- IDE controller driver;
#  - "missing"	- module does not exist at all;
#  - "unknown"	- none of the above.
#
# Note: Order of check is important here:
#  - cciss is a block device driver, but it also registers a SCSI host adapter
#    to access non-disk devices on its SCSI bus; it should be treated as
#    "block" (no need to load sd_mod) - therefore "block" check comes before
#    "scsi";
#  - usb-storage uses functions from the IDE layer; it should be treated as
#    "scsi" (sd_mod is needed) - therefore "scsi" check comes before "ide".
ModuleKind()
{
	local name="$1" list
	list=`ListModuleFiles "$name"`
	if [ $? -ne 0 ]; then
		echo "missing"
	elif [ -z "$list" ]; then
		echo "missing"
	elif [ -z "${list##*/kernel/drivers/block/*}" ]; then
		echo "block"
	elif [ -z "${list##*/kernel/drivers/scsi/*}" ]; then
		echo "scsi"
	elif [ -z "${list##*/kernel/drivers/ide/*}" ]; then
		echo "ide"
	else
		echo "unknown"
	fi
}

AddScsiModules()
{
	local n
	for n in "$@"; do
		n="`NormalizedName "$n"`"
		if IgnoredModule "$n"; then
			Debug "Ignoring \"$n\" module"
			continue
		fi

		case `ModuleKind "$n"` in
			ide)
				# IDE was handled earlier - skip it
				Debug "Ignoring IDE module \"$n\""
				continue
				;;

			block)
				# No additional modules needed
				Debug "Adding block module \"$n\""
				;;

			scsi|unknown)
				# SCSI needs sd_mod to be useful in initrd;
				# also add it for unknown modules to be safe
				# (maybe someone built scsi_mod into the kernel
				# and tries to use an out-of-kernel driver)
				if [ -z "$PRE_SCSI_MODNAMES_ADDED" ]; then
					SCSI_MODULES="$SCSI_MODULES $PRE_SCSI_MODNAMES"
					PRE_SCSI_MODNAMES_ADDED=1
				fi
				;;
		esac

		SCSI_MODULES="$SCSI_MODULES $n"
	done
}

FindScsiModulesFromConfig()
{
	local scsimodules
	scsimodules=`egrep -s '(alias|probeall)[ 	]+scsi_hostadapter' "$MODULES_CONF" |
		grep -v '^[ 	]*#' |
		LC_COLLATE=C sort -u |
		awk '{$1=$2="";print}'`

	AddScsiModules $scsimodules

	if [ -n "$SCSI_MODULES" ]; then
		Verbose "SCSI modules from config:$SCSI_MODULES"
		FindModules $SCSI_MODULES
	else
		Verbose "No SCSI modules specified in config"
	fi
}

MatchPciMassStorageClass()
{
	[ $(($pci_class & 0xff0000)) -eq $((0x010000)) ] || return 1
}

SortScsiModules()
{
	local n all normal= ata_generic= pata_legacy=
	for n in "$@"; do
		case "$n" in
			ata_piix)
				# Move ahci before ata_piix (there is some PCI
				# ID overlap between them, and using ata_piix
				# loses NCQ support).
				if InList ahci "$*"; then
					normal="$normal ahci"
				fi
				normal="$normal $n"
				;;
			ata_generic)
				# ata_generic must be after all other drivers...
				ata_generic="$ata_generic $n"
				;;
			pata_legacy)
				# ...except for pata_legacy, which must be even
				# after ata_generic.
				pata_legacy="$pata_legacy $n"
				;;
			*)
				normal="$normal $n"
				;;
		esac
	done
	all="$normal$ata_generic$pata_legacy"

	Verbose "Found SCSI modules:$all"
	FindModules $all
}

FindScsiModules()
{
	[ -z "$noscsi" ] || return

	if ModuleExists scsi_hostadapter; then
		# Manual configuration
		FindScsiModulesFromConfig
		return
	fi

	[ -x "$LSPCI" ] || {
		Verbose "$LSPCI not found - skipping autodetection of SCSI adapters"
		return
	}

	local scsimodules

	# Look for modules for all "Mass storage controller" class devices.
	# Looking only for SCSI and RAID subclasses is not sufficient - there
	# are some SATA controllers which have IDE subclass, but Linux SATA
	# drivers for them use the SCSI subsystem.
	scsimodules=`ListPciModulesUsingLspci MatchPciMassStorageClass`

	AddScsiModules $scsimodules

	if [ -n "$SCSI_MODULES" ]; then
		SortScsiModules $SCSI_MODULES
	else
		Verbose "No SCSI modules found"
	fi
}

# Some PCI IDE devices have more than one matching driver in modules.pcimap;
# common conflicts are SATA and vendor-provided RAID drivers (although vendor
# drivers often do not have PCI ID tables at all).  Adding such modules to
# initrd as IDE drivers can break booting, therefore they must be filtered out.
#
# This function assumes that IDE modules either reside in
# /lib/modules/$KERNEL/kernel/drivers/ide/, or depend on at least one module
# which is located there, and also do not depend on anything in
# /lib/modules/$KERNEL/kernel/drivers/scsi/.
#
AddIdeModule()
{
	local name="$1" list
	list=`ListModuleFiles "$name"`
	if [ $? -ne 0 ]; then
		Debug "Module \"$name\" does not exist - ignored"
	elif [ -z "${list##*/kernel/drivers/scsi/*}" ]; then
		Debug "Module \"$name\" looks like a SCSI driver, not IDE - skipping"
	elif [ -n "${list##*/kernel/drivers/ide/*}" ]; then
		Debug "Module \"$name\" does not look like an IDE driver - skipping"
	else
		Debug "Module \"$name\" looks like an IDE driver - adding"
		IDE_MODULES="$IDE_MODULES $name"
	fi
}

AddIdeModules()
{
	local n
	for n in "$@"; do
		AddIdeModule "$n"
	done
}

# Look for PCI device driver in modules.pcimap.  Code copied from hotplug
# (pci.agent).
#
# Parameters are passed in shell variables: pci_class, pci_id_vendor,
# pci_id_device, pci_subid_vendor, pci_subid_device.  Names of matching modules
# are output to stdout.
#
ListPciModulesByMap()
{
	local pcimap="/lib/modules/$KERNEL/modules.pcimap"
	[ -r "$pcimap" ] || return 1

	pci_class=$(($pci_class))
	pci_id_vendor=$(($pci_id_vendor))
	pci_id_device=$(($pci_id_device))
	pci_subid_vendor=$(($pci_subid_vendor))
	pci_subid_device=$(($pci_subid_device))

	(
		read ignored

		PCI_ANY=$((0xffffffff))
		while read module vendor device subvendor subdevice class class_mask ignored
		do
			case "$module" in
				\#*) continue ;;
			esac

			vendor=$(($vendor)); device=$(($device))
			subvendor=$(($subvendor)); subdevice=$(($subdevice))
			class=$(($class)); class_mask=$(($class_mask))

			if [ $vendor -ne $PCI_ANY ] && [ $vendor -ne $pci_id_vendor ]; then
				continue
			fi
			if [ $device -ne $PCI_ANY ] && [ $device -ne $pci_id_device ]; then
				continue
			fi
			if [ $subvendor -ne $PCI_ANY ] && [ $subvendor -ne $pci_subid_vendor ]; then
				continue
			fi
			if [ $subdevice -ne $PCI_ANY ] && [ $subdevice -ne $pci_subid_device ]; then
				continue
			fi

			class_temp=$(($pci_class & $class_mask))
			if [ $class_temp -eq $class ]; then
				echo "$module"
				# There was "break" here in the hotplug code;
				# this does not work if there can be more than
				# one matching module for the given PCI IDs -
				# and there can be (one example is sata_nv,
				# which has PCI ID entry matching all IDE
				# controllers made by nVidia).
			fi
		done
	) < "$pcimap"
}

ListPciModulesByAlias()
{
	local aliases="/lib/modules/$KERNEL/modules.alias"
	[ -r "$aliases" ] || return 1
	local dummy alias
	local modalias="pci:v0000${pci_id_vendor#0x}d0000${pci_id_device#0x}sv0000${pci_subid_vendor#0x}sd0000${pci_subid_device#0x}bc$(echo "$pci_class" | cut -c 3,4)sc$(echo "$pci_class" | cut -c 5,6)i$(echo "$pci_class" | cut -c 7,8)"

	grep '^alias ' "$aliases" | \
	while read dummy alias module; do
		case "$modalias" in
			$alias) echo "$module"; break ;;
		esac
	done
}

ListPciModules()
{
	ListPciModulesByMap || ListPciModulesByAlias
}

ListPciModulesUsingSysfs()
{
	local dev="$1"
	local pci_class pci_id_vendor pci_id_device pci_subid_vendor pci_subid_device

	pci_class=`cat "$dev/class"`
	pci_id_vendor=`cat "$dev/vendor"`
	pci_id_device=`cat "$dev/device"`
	pci_subid_vendor=`cat "$dev/subsystem_vendor"`
	pci_subid_device=`cat "$dev/subsystem_device"`

	ListPciModules
}

ListPciModulesUsingIdeConfig()
{
	local config="$1"
	local pci_class pci_id_vendor pci_id_device pci_subid_vendor pci_subid_device

	eval `awk '
		NR == 2 {
			print "pci_id_vendor=0x" $2 $1;
			print "pci_id_device=0x" $4 $3;
			print "pci_class=0x" $12 $11 $10;
		}
		NR == 4 {
			print "pci_subid_vendor=0x" $14 $13;
			print "pci_subid_device=0x" $16 $15;
		}
	' <"$config"`

	ListPciModules
}

# Usage: ListPciModulesForSlot <slot> [<match-function> [<parameters>...]]
#
ListPciModulesForSlot()
{
	local slot="$1" && shift
	local pci_class pci_id_vendor pci_id_device pci_subid_vendor pci_subid_device

	# This lspci -x parsing crap is here because lspci -n does not output
	# the prog-if byte which can be used in modules.pcimap entries
	eval `$LSPCI -mnx -s "$slot" | awk '
		NR == 2 {
			print "pci_id_vendor=0x" $3 $2;
			print "pci_id_device=0x" $5 $4;
			print "pci_class=0x" $13 $12 $11;
		}
		NR == 4 {
			print "pci_subid_vendor=0x" $15 $14;
			print "pci_subid_device=0x" $17 $16;
		}
	'`

	if [ "$#" -gt 0 ]; then
		"$@" || return 0
	fi

	ListPciModules
}

# Usage: ListPciModulesUsingLspci [<match-function> [<parameters>...]]
#
ListPciModulesUsingLspci()
{
	local slot ignored

	$LSPCI -mn | \
	while read slot ignored; do
		ListPciModulesForSlot "$slot" "$@"
	done
}

MatchPciIdeClass()
{
	# Match only the "IDE Interface" class
	[ $(($pci_class & 0xffff00)) -eq $((0x010100)) ] || return 1
}

ListPciIdeModulesUsingLspci()
{
	ListPciModulesUsingLspci MatchPciIdeClass
}

# Usage: FindIdeModulesUsing <lister-function> [<parameters>...]
#
FindIdeModulesUsing()
{
	local modules
	Debug "Looking for IDE modules using $*"
	modules=` "$@" `
	Debug "Found matching modules: \"$modules\""
	AddIdeModules $modules
}

# This function is called if an IDE controller which is not driven by a PCI IDE
# driver is found.  This may happen in several different cases:
#
#  - Really old ISA IDE: ide-generic will handle normal cases, manual
#    configuration may be needed for some obscure cards.
#
#  - ISA-PnP IDE: driver is built into ide-core (at least in 2.6.10).
#
#  - Onboard PCI IDE unknown to the currently running kernel (and therefore
#    driven by ide-generic).  The newly installed kernel might have a driver
#    for this hardware; this function attempts to find such drivers.
#
#  - Kernel 2.6.x and /sys not mounted (on 2.6.x /proc/ide/ide*/config no
#    longer exists) - this can be considered a broken configuration, but this
#    function will make it work at least for plain PCI IDE (not RAID)
#    controllers.
#
HandleNonPciIde()
{
	# no point in scanning PCI bus multiple times
	[ -z "$FOUND_NON_PCI_IDE" ] || return 0
	FOUND_NON_PCI_IDE=1

	Verbose "Found IDE controller driven by a non-PCI IDE driver"
	[ -x "$LSPCI" ] || {
		Verbose "$LSPCI not found - not looking for PCI IDE devices"
		return 0
	}

	FindIdeModulesUsing ListPciIdeModulesUsingLspci
}

FindIdeModulesUsingSysfs()
{
	local ide_device controller controllers=

	for ide_device in /sys/bus/ide/devices/*; do
		controller="$(readlink -ev "$ide_device"/../..)" || {
			Warning "Unable to find IDE controller for $ide_device"
			continue
		}
		InList "$controller" "$controllers" || \
			controllers="$controllers
$controller"
	done

	local dev
	for dev in $controllers; do
		if    [ -r "$dev/class" ]		\
		   && [ -r "$dev/vendor" ]		\
		   && [ -r "$dev/device" ]		\
		   && [ -r "$dev/subsystem_vendor" ]	\
		   && [ -r "$dev/subsystem_device" ] ; then
			FindIdeModulesUsing ListPciModulesUsingSysfs "$dev"
		else
			HandleNonPciIde
		fi
	done
}

FindIdeModulesUsingProc()
{
	local interface cfg

	for interface in /proc/ide/ide*; do
		cfg="$interface/config"
		if [ -r "$cfg" ]; then
			FindIdeModulesUsing ListPciModulesUsingIdeConfig "$cfg"
		else
			HandleNonPciIde
		fi
	done
}

SortIdeModules()
{
	local n normal= last=
	for n in "$@"; do
		case "$n" in
			generic)
				last="$last $n" ;;
			*)
				normal="$normal $n" ;;
		esac
	done
	Verbose "Found IDE modules:$normal$last"
	FindModules $normal $last
}

FindIdeModules()
{
	[ -z "$noide" ] || return

	local ide
	ide=`ls -d /proc/ide/ide* 2>/dev/null`
	[ -n "$ide" ] || return

	local new_ide_modules=
	if [ "$KERNEL_MAJOR" -gt 2 ]; then
		new_ide_modules=1
	elif [ "$KERNEL_MAJOR" -eq 2 ]; then
		if [ "$KERNEL_MINOR" -gt 4 ]; then
			new_ide_modules=1
		elif [ "$KERNEL_MINOR" -eq 4 ]; then
			if [ "$KERNEL_PATCH" -gt 20 ]; then
				new_ide_modules=1
			fi
		fi
	fi

	if [ -z "$new_ide_modules" ]; then
		# For kernels <= 2.4.20 there were no per-chipset modules
		FindModule -ide-mod
		FindModule -ide-probe
		FindModule -ide-probe-mod
		FindModule -ide-disk
		return
	fi

	if ModuleExists ide_hostadapter; then
		# Manual configuration overrides even ide-generic
		FindModule ide_hostadapter
		FindModule -ide-detect
		FindModule -ide-disk
		return
	fi

	ModuleExists ide-core || \
		ModuleExists ide-disk || \
		ModuleExists ide-generic || \
		ModuleExists generic || \
		return

	FindModule -ide-core

	if [ -d /sys/bus/ide/devices ]; then
		Debug "Looking for IDE devices in /sys"
		FindIdeModulesUsingSysfs
	else
		Debug "Looking for IDE devices in /proc"
		FindIdeModulesUsingProc
	fi

	if [ -n "$IDE_MODULES" ]; then
		SortIdeModules $IDE_MODULES
	else
		Verbose "No IDE modules found"
	fi

	FindModule -ide-generic
	FindModule -ide-detect
	FindModule -ide-disk
}

FindRaidModuleForLevel()
{
	local level="$1" module=

	case "$level" in
		[0156]|10)
			module="raid$level"
			;;
		4)
			module="raid5"
			;;
		-1|linear)
			module="linear"
			;;
		-4|multipath)
			module="multipath"
			;;
		-5|faulty)
			module="faulty"
			;;
	esac

	if [ -n "$module" ]; then
		InList "$module" "$RAID_MODULES" || {
			RAID_MODULES="$RAID_MODULES $module"
			FindModule "$module"
		}
	else
		echo "raid level \"$level\" not recognized" >&2
	fi
}

ListRaidLevelsFromRaidtab()
{
	grep '^[	 ]*raid-level' /etc/raidtab | \
		awk '{print $2}' | LC_COLLATE=C sort -u
}

MdadmScanForArrays()
{
	$MDADM --detail --scan --verbose 2>/dev/null
	$MDADM --examine --scan --verbose 2>/dev/null
}

ListRaidLevelsFromMdadm()
{
	MdadmScanForArrays | \
		sed -ne 's/^.* level=\([^ ]*\) .*$/\1/p' |
		sed -e 's/^raid\([0-9]*\)$/\1/'
}

FindLVMModules()
{
	if [ -n "$HAVE_LVM" ]; then
		FindModule $LVM_MODNAME
	fi 
}

FindRaidModules()
{
	[ -z "$noraid" ] || return

	if egrep -s '^/dev/md(/?[0-9]+|[/_]d[0-9]+(p[0-9]+)?)[ 	]' "$FSTAB_FILE" |
		fgrep -qsv noauto; then
		HAVE_RAID=1
	fi

	if [ -z "$HAVE_RAID" ] && [ -x $MDADM ]; then
		if [ -n "$(MdadmScanForArrays)" ]; then
			HAVE_RAID=1
		fi
	fi

	if [ -n "$HAVE_RAID" ]; then
		FindModule -md
		if [ -r /etc/raidtab ]; then
			for number in `ListRaidLevelsFromRaidtab`; do
				FindRaidModuleForLevel "$number"
			done
		fi
		if [ -x $MDADM ]; then
			for number in `ListRaidLevelsFromMdadm`; do
				FindRaidModuleForLevel "$number"
			done
		fi
		if [ -z "$RAID_MODULES" ]; then
			Warning "RAID support enabled, but used RAID levels not detected"
		fi
	fi

	if grep -s '^/dev/ataraid' "$FSTAB_FILE" |fgrep -qsv noauto; then
		local ataraidmodules
		ataraidmodules=`egrep -s '(alias|probeall)[ 	]+ataraid_hostadapter' "$MODULES_CONF" |
			grep -v '^[ 	]*#' |
			LC_COLLATE=C sort -u |
			awk '{$1=$2="";print}'`
		local n
		for n in $ataraidmodules; do
			FindModule "$n"
		done
	fi
}

FindRootModules()
{
	# In case the root filesystem is modular.
	#rootdev=$(awk '{if (($2 == "/") && ($1 !~ /^[ \t]*#/) {print $1}}' "$FSTAB_FILE")
	if [ -z "$ROOTFS" ]; then
		ROOTFS=$(awk '$2 == "/" && !/^[[:blank:]]*#/ {print $3}' "$FSTAB_FILE")
		#ROOTFS=$(sed -rn '/^[[:blank:]]*[^#][[:graph:]]+[[:blank:]]+\/[[:blank:]]/s|[[:graph:]]+[[:blank:]]+[[:graph:]]+[[:blank:]]+([[:graph:]]+)[[:blank:]]+[[:graph:]]+[[:blank:]]+[[:digit:]][[:blank:]]+[[:digit:]]|\1|p' "$FSTAB_FILE")
		#'
	fi
	[ -z "$ROOTFS" ] || FindModule -"$ROOTFS"
}

FindLoopModules()
{
	# check to see if we need to set up a loopback filesystem
	local full
	full=$(awk '$2 == "/" && $4 ~ "loop" {print $1}' "$FSTAB_FILE")
	if [ -n "$full" ]; then
		local dir="$full" line=
		while [ -n "$dir" -a -z "$line" ]; do
			dir="${dir%/*}"
			line=$(awk -v "dir=$dir" '$2 == dir {print $0}' "$FSTAB_FILE")	
		done
		[ -n "$line" -a "$dir" != / ] ||
			Fatal "bad fstab, loopback file doesn't belong to any device."
		loopDev=$(echo "$line" |awk '{print $1}')
		loopFs=$(echo "$line" |awk '{print $3}')
		loopFile=$(echo "$full" |sed "s|$dir||")

		FindModule -loop
		FindModule -"$loopFs"
	fi
}

Install()
{
	install -D $debug "$1" "$2" ||
		Fatal "Failed to install \"$1\" file."
}

Cp()
{
	cp $debug "$@"
}

Ln()
{
	ln $debug "$@"
}

Mkdir()
{
	mkdir $debug "$@"
}

Mknod()
{
	file="$1"
	shift
	mknod "$file" "$@" &&
		Debug "Created $file device" ||
		Fatal "Failed to create \"$file\" device."
}

CopyFileToInitramfs()
{
	local source="$1" && shift
	local target="$1" && shift

	if grep -qs '@NO_INITRAMFS@' "$source"; then
		Debug "Skipped '$source' marked with @NO_INITRAMFS@"
	else
		Mkdir -p "${target%/*}"
		Cp -aL "$source" "$target"
	fi
}

CopyDirToInitramfs()
{
	local source="$1" && shift
	local target="$1" && shift

	local f
	for f in "$source"/*; do
		# Ignore *.rpm* and *~ files
		[ "${f%.rpm*}" = "$f" -a "${f%\~}" = "$f" ] || continue
		CopyFileToInitramfs "$f" "$target/${f##*/}"
	done
}

ConfigInitramfsForLVM()
{
	cat >>"$MNTDIR"/scripts/local-top <<'EOF'
log_begin_msg "Starting up LVM"
vgscan
vgchange -ay
log_end_msg
EOF
}

ConfigInitramfsForRaid()
{
	# /scripts/local-top runs before swsusp resume - start RAID in
	# read-only mode to avoid messing up resume state.  Actually the
	# start_ro parameter sets the "read-auto" mode - arrays initially
	# start read-only, but are switched to the normal read-write mode on
	# the first write request, so there is no need to switch arrays to
	# the read-write mode explicitly.
	#
	# "echo add > /sys/block/md0/uevent" after md_run is a workaround for
	# an unavoidable race between md_run invoking RAID_AUTORUN ioctl on
	# /dev/md0 and udevd probing the started md devices (one of which may
	# be /dev/md0): probing fails if it starts when md_run still has
	# /dev/md0 open.  The extra uevent makes sure that /dev/md0 is probed
	# properly, which is important when the root fs is specified by UUID.
	cat >>"$MNTDIR"/scripts/local-top <<'EOF'
log_begin_msg "Starting up RAID devices"
modprobe -q md_mod
echo 1 > /sys/module/md_mod/parameters/start_ro
md_run `cat /proc/cmdline`
echo add > /sys/block/md0/uevent ||:
log_end_msg
EOF

	# /scripts/local-premount runs after resume - restore start_ro
	# to its default value.
	cat >>"$MNTDIR"/scripts/local-premount <<'EOF'
echo 0 > /sys/module/md_mod/parameters/start_ro
EOF
}

CopyExtraFiles()
{
	local old_IFS="$IFS" old_options="-$-"
	IFS='
'
	set -f

	local f target source tmp
	for f in $ADD_FILES; do
		target="${f%%=*}"
		[ "$target" != "$f" ] ||
			Fatal "Invalid --add argument '$f': missing '='."
		[ -n "$target" ] ||
			Fatal "Invalid --add argument '$f': empty target."
		[ -n "${target%%*/}" ] ||
			Fatal "Invalid --add argument '$f': target ends with '/'."
		tmp="/$target/"
		[ -n "${tmp##*/./*}" ] && [ -n "${tmp##*/../*}" ] ||
			Fatal "Invalid --add argument '$f': target contains '.' or '..'."

		source="${f#*=}"
		[ -n "$source" ] ||
			Fatal "Invalid --add argument '$f': empty source."
		[ -n "${source%%*/}" ] ||
			Fatal "Invalid --add argument '$f': source ends with '/'."
		[ -e "$source" ] ||
			Fatal "Invalid --add argument '$f': source does not exist."

		target="/$target"
		Mkdir -p "$MNTDIR/${target%/*}" ||
			Fatal "Failed to make directories for '--add $f'."
		Cp -aLT "$source" "$MNTDIR/$target" ||
			Fatal "Failed to copy files for '--add $f'."
	done

	[ -n "${old_options##*f*}" ] || set +f
	IFS="$old_IFS"
}

AddFile()
{
	ADD_FILES="$ADD_FILES
$1"
}

AddFiles()
{
	local i
	for i in $@; do
		AddFile $i
	done
}

AddExtraModules()
{
	local m
	for m in $@; do
		EXTRA_MODNAMES="$EXTRA_MODNAMES$(AddModules "$m")"
	done
}

AddNBDFiles()
{
	local type="$1"
	case $1 in
		aoe)
			AddExtraModules aoe
			;;
		nbd)
			[ -x /usr/sbin/nbd-client.static ] || Fatal "Couldn't find executable nbd-client.static"
			AddExtraModules nbd
			AddFiles /sbin/nbd-client=/usr/sbin/nbd-client.static
			;;
		iscsi)
			[ -x /lib/mkinitrd/iscsistart ] || Fatal "Couldn't find executable iscsistart.static"
			AddExtraModules iscsi_tcp crc32c sd_mod
			AddFiles /sbin/iscsistart=/lib/mkinitrd/iscsistart
			;;
		srp)
			AddExtraModules ib_srp sd_mod
			;;
		"")
			;;
		*)
			Warning "Unknown nbd type '$type'"
			;;
	esac
}

CreateInitramfsConfigs()
{
	local f
	Mkdir -p "$MNTDIR"/conf "$MNTDIR"/etc/udev/rules.d

	for m in $MODULES; do
		m="${m%.gz}"
		m="${m##*/}"
		m="${m%.o}"
		m="${m%.ko}"

		printf '%s\n' "$m" >>"$MNTDIR"/conf/modules
	done

	if [ -f /etc/modprobe.conf ]; then
		CopyFileToInitramfs /etc/modprobe.conf \
			"$MNTDIR"/etc/modprobe.conf
	fi
	if [ -d /lib/modprobe.d ]; then
		CopyDirToInitramfs /lib/modprobe.d "$MNTDIR"/etc/modprobe.d
		ls /etc/modprobe.d/* 2>/dev/null |
		while read f; do
			F="$MNTDIR/etc/modprobe.d/$(basename $f)"
			[ -s "$F" ] && cat "$f" >> "$F" || cp -dL --preserve=all "$f" "$F"
		done
	elif [ -d /etc/modprobe.d ]; then
		CopyDirToInitramfs /etc/modprobe.d "$MNTDIR"/etc/modprobe.d
	fi
	if [ -d /etc/udev/initramfs-rules.d ]; then
		for f in /etc/udev/initramfs-rules.d/*.rules; do
			[ -f "$f" ] || continue
			Cp -aL "$f" "$MNTDIR"/etc/udev/rules.d/
		done
	fi
	
	if [ -n "$HAVE_LVM" ]; then
		[ -n "$LVM" ] || Fatal "Couldn't find executable lvm.static "
		[ -n "$DMSETUP" ] || Fatal "Couldn't find executable dmsetup.static "
		Cp -aL "$LVM" "$MNTDIR"/sbin/lvm
		Cp -aL "$DMSETUP" "$MNTDIR"/sbin/dmsetup
		[ -e /lib/udev/dm_export ] && Cp -aL /lib/udev/dm_export "$MNTDIR"/lib/udev/dm_export
		cd "$MNTDIR"/sbin/
		Ln -s lvm vgscan
		Ln -s lvm vgchange
		ConfigInitramfsForLVM
	fi

	if [ -n "$HAVE_RAID" ]; then
		ConfigInitramfsForRaid
	fi

	if [ -n "$ADD_FILES" ]; then
		CopyExtraFiles
	fi
}

MakeMountDir()
{
	local m

	MNTDIR="$WORKDIR/tree"

	Mkdir -p "$MNTDIR"/etc "$MNTDIR"/dev "$MNTDIR"/safedev "$MNTDIR"/loopfs "$MNTDIR"/sys "$MNTDIR"/proc &&
		Mkdir -p "$MNTDIR/lib/modules/$KERNEL" ||
		Fatal "Failed to create directories."

	Mkdir "$MNTDIR/tmp"
	ln -s tmp "$MNTDIR/run"
	for m in /lib/mkinitrd/initramfs-base /lib/mkinitrd/klibc; do
		Cp -aL $m/* "$MNTDIR/"
	done
	[ -d /lib/mkinitrd/initramfs ] && Cp -a /lib/mkinitrd/initramfs/* "$MNTDIR/"
	Cp -a /lib/mkinitrd/udev/lib "$MNTDIR/"
	Cp --no-dereference --preserve=all /lib/mkinitrd/udev/sbin/udev* "$MNTDIR"/sbin/
	for m in /lib/mkinitrd/module-init-tools/sbin/modprobe /lib/mkinitrd/udev/sbin/blkid; do
		[ -e $m ] && Cp -nL $m "$MNTDIR"/sbin/
	done

	for m in $MODULES $EXTRA_MODULES; do
		Install "$m" "$MNTDIR/$m" ||
			Fatal "Failed to install $m module."
		m="$MNTDIR/$m"
		if [ -z "${m%%*.gz}" ]; then
			gunzip $debug "$m" ||
				Fatal "Failed to uncompress \"$m\" module."
		fi
	done

	[ -f "$MNTDIR/lib/modules/$KERNEL/modules.builtin" ] ||
		Cp --preserve=all /lib/modules/$KERNEL/modules.builtin "$MNTDIR/lib/modules/$KERNEL/"
	rm -rf "$MNTDIR/lib/modules/$KERNEL/modules.order"
	find "$MNTDIR/lib/modules/$KERNEL/kernel" -type f -name \*.ko |
		sed "s|$MNTDIR/lib/modules/$KERNEL/||" |
		grep -F -f - /lib/modules/$KERNEL/modules.order > "$MNTDIR/lib/modules/$KERNEL/modules.order"

	depmod -a -F "/boot/System.map-$KERNEL" -b "$MNTDIR" "$KERNEL" ||
		Fatal "Failed to generate module dependencies."

	Mknod "$MNTDIR/dev/console" c 5 1
	Mknod "$MNTDIR/dev/null" c 1 3
	Mknod "$MNTDIR/dev/ram" b 1 1
	Mknod "$MNTDIR/dev/systty" c 4 0
	Mknod "$MNTDIR/dev/tty1" c 4 1

	CreateInitramfsConfigs

	if [ -n "$pause" ]; then
		cat >&2 <<__EOF__

You can now edit initrd manually.
WORKDIR: $WORKDIR

Press ENTER to continue automatic initrd generation.

__EOF__
		read m
	fi

	INODES=`find "$MNTDIR" |wc -l` ||
		Fatal "Failed to calculate inodes."
	INODES=$((INODES+20))
	Verbose "Inode count: $((INODES))"
	IMAGESIZE=`du -ks --block-size=4096 "$MNTDIR" |cut -f1` ||
		Fatal "Failed to calculate image size."
	# Add more 20%
	IMAGESIZE=$((IMAGESIZE*6/5 + INODES/32))
	Verbose "Image size: $((IMAGESIZE*4))K"
}

KernelSupportsInitramfs()
{
	# udevd used in initramfs does not support kernels older than 2.6.15
	KernelVersionAtLeast 2 6 15 || return 1

	# Assume that CONFIG_BLK_DEV_INITRD is on (otherwise no type of
	# initrd/initramfs image will work).
	return 0
}

MakeImageFile()
{
	( cd "$MNTDIR" && find -print0 | cpio -o0 -H newc --quiet > "$IMAGE" ) &&
		Verbose "Created image from tree: $MNTDIR --> $IMAGE" ||
		Fatal "Failed to create initramfs image."

	Verbose "Created initramfs image file"
}

KernelSupportsBootSplash()
{
	grep -qs '[[:space:]]splash_' "/boot/System.map-$KERNEL" ||
		return 1
	return 0
}

PrepareBootSplashData()
{
	local config="/etc/sysconfig/bootsplash"
	local fbresolution=
	[ -n "$ADD_BOOTSPLASH" ]	|| return
	[ -s "$config" ]		|| return
	. "$config"			|| return
	is_yes "$SPLASH"		|| return
	[ -n "$THEME" ]			|| return
	fbresolution=`fbresolution 2>/dev/null`	|| return
	local themefile
	themefile="/etc/bootsplash/themes/$THEME/config/bootsplash-$fbresolution.cfg"
	if [ -f "$themefile" ]; then
		KernelSupportsBootSplash || {
			Verbose "No bootsplash support in kernel"
			return
		}
		if "$BIN_SPLASH" -f -s "$themefile" >"$WORKDIR/bootsplash"; then
			Verbose "Prepared bootsplash \"$themefile\""
			ADD_FILES="bootsplash=$WORKDIR/bootsplash
$ADD_FILES"
		else
			Verbose "Failed to prepare bootsplash \"$themefile\"."
			rm -f "$WORKDIR/bootsplash"
		fi
	fi
}

Usage()
{
	cat >&2 <<EOF
mkinitrd - creates an initial ramdisk image for preloading modules.

mkinitrd is free software, covered by the GNU General Public License.
mkinitrd comes with ABSOLUTELY NO WARRANTY, see license for details.

Usage: $PROG [options] <initrd-image> <kernel-version>

Valid options are:
--type TYPE                     select image type (initramfs).
--fstab FILENAME                use FILENAME instead of /etc/fstab.
--preload MODULES|@listfile     load MODULES before all found automatically.
--with MODULES|@listfile        load MODULES after all found automatically.
--extra MODULES||@listfile      add MODULES to be loaded only if needed.
--with-raid                     enable software RAID (md) support.
--with-lvm                      enable LVM support.
--with-nbd [nbd|aoe|iscsi|srp]  enable Network Block Device support (default type is nbd)
--omit-scsi-modules             do not load any SCSI modules.
--omit-ide-modules              do not load any IDE modules.
--omit-raid-modules             do not load any raid modules.
--pause                         pause for manual initrd editing.
--nocompress                    do not compress initrd image.
--compress                      compress initrd image (default).
--compressor {lzma,xz,gzip,bzip2,lzo}  compressor to be used (by default used gzip).
--nobootsplash                  do not add bootsplash to the initrd image.
--strict                        abort on errors.
--image-version                 make image name based on kernel name.
--ifneeded                      create initrd image only if needed.
--version                       print version number and exit.
-a, --add INITRAMFS_FILE=FILE   add FILE to initramfs as INITRAMFS_FILE.
-r, --root                      root device.
--rootfs                        root fs.
-f, --force                     force initrd image creation.
-v, --verbose                   be more verbose.
-d, --debug                     print debug information.
-h, --help                      show this text.

Example: $PROG /boot/initrd-$uname_r.img $uname_r

EOF
	[ -n "$1" ] && Exit "$1" || Exit
}

AddModules()
{
	local LISTFILE MODNAMES
	if [ -n "$1" ]; then
		LISTFILE=`echo "$1" | sed 's/^@//'`
		if [ "$1" != "$LISTFILE" ]; then
			if [ -f "$LISTFILE" ]; then
				MODNAMES=""
				for m in `grep -v '^[[:blank:]]*#' "$LISTFILE"` ; do
					MODNAMES="$MODNAMES $m"
				done
			else
				echo "warning: file $LISTFILE not exist!" >&2
			fi
		else
			MODNAMES=" $1"
		fi
		echo "$MODNAMES"
	fi
}

TEMP=`getopt -n "$PROG" -o a:fhvdr: -l help,version,verbose,debug,force,ifneeded,omit-scsi-modules,omit-ide-modules,omit-raid-modules,with-raid,with-lvm,with-nbd::,pause,image-version,nocompress,compress,compressor:,nobootsplash,strict,fstab:,before:,preload:,with:,after:,extra:,type:,add:,root:,rootfs: -- "$@"` || Usage 1
eval set -- "$TEMP"

img_vers=
force=
while :; do
	case "$1" in
		-a|--add)
			AddFile $2
			shift 2
			;;
		--type)
			case "$2" in
				''|initramfs) ;;
				*) Fatal "$2: Unsupported image type" ;;
			esac
			shift 2
			;;
		--fstab)
			FSTAB_FILE=$2
			shift 2
			;;
		--before|--preload)
			PRELOAD_MODNAMES="$PRELOAD_MODNAMES$(AddModules "$2")"
			shift 2
			;;
		--after|--with)
			POSTLOAD_MODNAMES="$POSTLOAD_MODNAMES$(AddModules "$2")"
			shift 2
			;;
		--extra)
			AddExtraModules $(AddModules "$2")
			shift 2
			;;
		--strict)
			strict=1
			shift
			;;
		--ifneeded)
			ifneeded=1
			shift
			;;
		--nocompress)
			USE_COMPRESS=
			shift
			;;
		--compress)
			USE_COMPRESS=1
			shift
			;;
		--compressor)
			COMPRESSOR=$2
			shift 2
			;;
		--nobootsplash)
			ADD_BOOTSPLASH=
			shift
			;;
		--omit-scsi-modules)
			noscsi=1
			shift
			;;
		--omit-ide-modules)
			noide=1
			shift
			;;
		--omit-raid-modules)
			noraid=1
			shift
			;;
		--with-raid)
			HAVE_RAID=1
			shift
			;;
		--with-lvm)
			HAVE_LVM=1
			shift
			;;
		--with-nbd)
			case $2 in
				"")	AddNBDFiles nbd ;;
				*)	AddNBDFiles "$2" ;;
			esac
			shift 2
			;;
		--pause)
			pause=1
			shift
			;;
		--image-version)
			img_vers=1
			shift
			;;
		-v|--verbose)
			verbose=-v
			shift
			;;
		-d|--debug)
			verbose=-v
			debug=-v
			shift
			;;
		-f|--force)
			force=1
			shift
			;;
		-r|--root)
			ROOTDEV=$(echo "$2" | sed 's|^/dev/||')
			case "$ROOTDEV" in
				nbd*)
					AddNBDFiles
					AddExtraModules af_packet
					ROOTFS=${ROOTFS:-squashfs}
					noscsi=1
					noide=1
					noraid=1
					;;
				lvm*)
					HAVE_LVM=1
					;;
			esac
			shift 2
			;;
		--rootfs)
			ROOTFS="$2"
			case "$ROOTFS" in
				nfs|nfs4)
					AddExtraModules nfs af_packet
					noscsi=1
					noide=1
					noraid=1
					;;
			esac
			shift 2
			;;
		--version)
			echo "$PROG: version $VERSION"
			exit 0
			;;
		-h|--help)
			Usage 0
			;;
		--)
			shift
			break
			;;
		*)
			Fatal "$PROG: unrecognized option: $1"
			;;
	esac
done

case "$(modprobe --version 2>/dev/null ||:)" in
	"module-init-tools version "*)
		MODPROBE_TYPE=module-init-tools
		;;
	"kmod version "*)
		MODPROBE_TYPE=kmod
		;;
	*modprobe*version*)
		MODPROBE_TYPE=modutils
		;;
	*)
		Fatal "Unable to detect modprobe type (modutils or module-init-tools)"
		;;
esac

# /proc needs to be mounted for the hardware autodetection to work properly.
# 2.6.x kernels also need /sys for that.
if [ -r /proc/filesystems ]; then
	if grep -qs '^.*[[:space:]]sysfs$' /proc/filesystems; then
		[ -d /sys/bus ] ||
			Warning "/sys not available - hardware autodetection will not work"
	fi
else
	Warning "/proc not available - hardware autodetection will not work"
fi

TARGET=$1
[ -n "$TARGET" ] || Fatal "Target image not specified."
shift

KERNEL=$1
[ -n "$KERNEL" ] || Fatal "Kernel version not specified."
ParseKernelVersion
shift

if [ -n "$img_vers" ]; then
	TARGET="$TARGET-$KERNEL.img"
fi

[ -n "$force" -o ! -f "$TARGET" ] || Fatal "$TARGET already exists."

MODULES_DIR="/lib/modules/$KERNEL"
[ -d "$MODULES_DIR" ] ||
	Fatal "Directory \"$MODULES_DIR\" doesn't exist or not accessible."

WORKDIR=`mktemp -td initrd.XXXXXXXXXX` ||
	Fatal "Failed to create working directory."

Verbose "Generating module dependencies..."
depmod -a -F "/boot/System.map-$KERNEL" "$KERNEL" &&
	Verbose "...done." ||
	Fatal "Failed to generate module dependencies."

MODULES_CONF="$WORKDIR/modules.conf"
case "$MODPROBE_TYPE" in
	modutils)
		modprobe --kernel-release "$KERNEL" -c >"$MODULES_CONF" ||
			Fatal "Failed to parse modutils configuration."
		;;
	module-init-tools|kmod)
		modprobe --set-version="$KERNEL" -c >"$MODULES_CONF" ||
			Fatal "Failed to parse module-init-tools configuration."
		;;
	*) Fatal "Unknown modprobe type '$MODPROBE_TYPE'" ;;
esac

KernelSupportsInitramfs ||
	Fatal 'Unable to create image file: initramfs support not found in your kernel'

### Begin module lookup.

FindModulesFromCommandLine $PRELOAD_MODNAMES

FindIdeModules

FindScsiModules

FindRaidModules

FindLVMModules

FindRootModules

FindLoopModules

FindModulesFromCommandLine $POSTLOAD_MODNAMES

FindExtraModulesFromCommandLine $EXTRA_MODNAMES

### End module lookup.

if [ -n "$ifneeded" -a -z "$MODULES$EXTRA_MODULES" ]; then
	Verbose "No modules are needed - not building initrd image."
	exit 0
fi

Verbose "Using modules: $MODULES"
if [ -n "$EXTRA_MODULES" ]; then
	Verbose "Extra modules: $EXTRA_MODULES"
fi

PrepareBootSplashData

MakeMountDir

MNTPOINT="$WORKDIR/mnt"
Mkdir -p "$MNTPOINT" || Fatal "Failed to create mount point."

IMAGE="$WORKDIR/img"
:>"$IMAGE" || Fatal "Failed to create image file."

MakeImageFile

if [ -n "$USE_COMPRESS" ]; then
	COMPRESSOR_OPTS="-9"
	if [ -s /boot/config-$KERNEL ]; then
		for i in $COMPRESSOR gzip lzma xz bzip2 lzo; do
			if which $COMPRESSOR >/dev/null 2>&1 && grep -qi "^CONFIG_RD_$i=y" /boot/config-$KERNEL; then
				COMPRESSOR=$i
				case $i in
					lzo)	COMPRESSOR="lzop" ;;
					xz)	COMPRESSOR_OPTS="--threads=1 --check=crc32 --lzma2=preset=8e,dict=1MiB" ;;
				esac
				break;
			else
				COMPRESSOR=
			fi
		done
	else
		COMPRESSOR=
	fi
	${COMPRESSOR:-gzip} $COMPRESSOR_OPTS <"$IMAGE" >"$TARGET" &&
		Verbose "Installed ramdisk into $TARGET" ||
		Fatal "Failed to install ramdisk into $TARGET."
else
	Cp "$IMAGE" "$TARGET" &&
		Verbose "Installed ramdisk into $TARGET" ||
		Fatal "Failed to install ramdisk into $TARGET."
fi

if [ -n "$verbose" ]; then
	size=`du -hs $TARGET |cut -f1`
	echo "Ramdisk size: $size"
fi
