W

Tematy związane z oprogramowaniem, instalacją, konfiguracją
silos
Posty: 14
Rejestracja: 08 października 2009, 15:50

Własna wersja przenośnego Debiana z gotowych skryptów

Post autor: silos »

Witam.
Próbuję sobie zrobić mobilnego Debiana Lenny za pomocą skryptów z http://www.linux-live.org.
Do tej pory wszystko idzie gładko (oczywiście po dość poważnych modyfikacjach skryptów pod katem Debiana), jadro 2.6.30 ładnie się kompiluje ale po uruchomieniu gotowego Live-Debiana z pendrive'a dostaje coś takiego:

Obrazek

Czym to może być spowodowane? Gdzie to naprawić?
Mój plik linuxrc:

Kod: Zaznacz cały

#!/bin/ash

export PATH=.:/:/usr/sbin:/usr/bin:/sbin:/bin

mount -n -t proc proc /proc
mount -n -t sysfs sysfs /sys

mount -n -o remount,rw / #ta linijka powoduje pierwszy blad 
ln -sf /proc/mounts /etc/mtab    # this allows us to use umount -a

. liblinuxlive # it requires proc to be mounted



# Don't print kernel messages to konsole now.
# Syslog will reset printk settings, no need to remember it here anymore.
# echo "0" >/proc/sys/kernel/printk
# hagon

# Load essential drivers, like CDROM drivers, aufs/squashfs etc,
# use mdev to create /dev/ devices and setup it as a hotplug-handler

modprobe_essential_modules

# /usr and some drivers are compressed in initrd 
# so it must be mounted from .lzm files

mount_initrd_loops

# start hotplugging before hw drivers load

mdev_start_hotplug

# Then load drivers for data storage and input devices

modprobe_usb_modules

modprobe_pcmcia_modules

debug_shell

# make sure ext3 partitions are not mounted using ext2 driver,
# and vfat partitions are not mounted using msdos driver
echo -e "ext3\next2\nvfat\n*" >/etc/filesystems

mkdir -p $UNION

mkdir -p $MEMORY

debug_shell

# Find livecd data directory by searching for livecd.sgn file
SGN=$(cmdline_value sgnfile)
if [ "$SGN" = "" ]; then SGN=livecd.sgn; fi
echolog "looking for '$LIVECDNAME' data directory..."

# First, try from= boot argument, if given
DATAFROM=$(cmdline_value from)

# If ip= parameter is present, assume we're booted over PXE
# In that case, we have to initialize network very soon (now)
# ip=<client-ip>:<boot-server-ip>:<gw-ip>:<netmask>
# Nevertheless, from= parameter won't be overwritten by this
IP=$(cmdline_value ip)
if [ "$IP" != "" -a "$DATAFROM" = "" ]; then
   DATAFROM="http://"$(echo $IP | cut -d : -f 2)
fi

if [ "$DATAFROM" ]; then
   if [ "$(echo $DATAFROM | cut -b 1-7 | tr "[:upper:]" "[:lower:]")" = "http://" ]; then
      init_dhcp $(modprobe_network_modules)
      mount_httpfs $DATAFROM $MOUNTDIR/httpfs
      # if mountdir/httpfs/livecdname is found, set DATA=, else umount httpfs
      # - currently this part works even without the above mentioned, but livecd.sgn is required now untill it's uncommented
   else
      DATAFROM=$(find_in_computer $DATAFROM)
      if [ "$DATAFROM" ]; then
         mount_device $DATAFROM $LOOPMOUNT # mount again, it may be loop device
         if [ $? -eq 0 -a "$(find_modules $LOOPMOUNT/$LIVECDNAME)" != "" ]; then
            echolog "found in $DATAFROM"
            DATA=$LOOPMOUNT/$LIVECDNAME
         else
            fumount $LOOPMOUNT
            fumount $MOUNTDIR/*
         fi
      fi
   fi
fi

if [ "$DATA" = "" ]; then
   # from= is not used or it didn't contain valid data
   DATA=$(find_in_computer $LIVECDNAME/$SGN)
   DATA=$(dirname $DATA 2>/dev/null)
fi

if [ "$DATA" = "" ]; then fatal \
"$LIVECDNAME data not found.
You are maybe using an unsupported boot device (eg. SCSI or old PCMCIA).
Workaround: Copy the directory $LIVECDNAME from your boot device to an IDE/SATA
disk, eg. to /mnt/hda1/$LIVECDNAME or C:\\$LIVECDNAME. Then try to boot again."
fi

echolog "using $LIVECDNAME data from $DATA"

debug_shell

echolog "setting up file/directory for persistent changes"
CHANGESVAL=$(cmdline_value changes)

if [ "$CHANGESVAL" ]; then
   CHANGESMNT=$(find_in_computer $CHANGESVAL)
   echolog $CHANGESMNT
fi

debug_shell

mount_device "$CHANGESMNT" $MEMORY # removes $MEMORY if CHANGESMNT is wrong

# test if the filesystem is writable so changes can be stored to it
touch $MEMORY/empty 2>/dev/null && \
rm -f $MEMORY/empty 2>/dev/null

# if changes can't be mounted or the filesystem is not writable,
# fallback to the default: tmpfs
if [ $? -ne 0 ]; then
   echolog "changes not used or not writable, using memory only"
   fumount $MEMORY
   mkdir -p $MEMORY # mount_device might removed it

   RAMSIZE=$(cmdline_value ramsize)
   if [ "$RAMSIZE" = "" ]; then RAMSIZE="60%"; fi
   mount -t tmpfs -o "size=$RAMSIZE" tmpfs $MEMORY
   XINO=$MEMORY
else
   # So it is writable, we will keep the filesystem mounted.
   # Check if it supports links and chmod.
   # If not, overmount CHANGES using posixovl
   echolog "testing the filesystem for posix compatibility"
   touch $MEMORY/.empty1 && \
   ln -sf $MEMORY/.empty1 $MEMORY/.empty2 2>/dev/null && \
   chmod +x $MEMORY/.empty1 2>/dev/null  && \
   test -x $MEMORY/.empty1 && \
   chmod -x $MEMORY/.empty1 2>/dev/null  && \
   test ! -x $MEMORY/.empty1 && \
   rm $MEMORY/.empty1 $MEMORY/.empty2 2>/dev/null

   if [ $? -ne 0 ]; then
      echolog "not compatible - starting posixovl"
      rm $MEMORY/.empty1 $MEMORY/.empty2 2>/dev/null
      mkdir -p $CHANGES
      posixovl -F $CHANGES -- -o attr_timeout=300,entry_timeout=300,negative_timeout=300,kernel_cache,allow_other
      find $CHANGES >/dev/null 2>&1 # cache everything now
   fi
fi

# $UNION will be used as a root directory, livecd modules will be added soon
echolog "setup union directory (using aufs)"

mkdir -p $CHANGES
mkdir -p $IMAGES

debug_shell

# store the xino file in memory in all cases, it's faster and safer
if [ "$XINO" != "$MEMORY" ]; then
   mkdir -p $XINO
   mount -n -t tmpfs tmpfs $XINO
fi

# mount aufs using the writable branch as the first one (leftmost/topmost)
mount -t aufs -o nowarn_perm,xino=$XINO/.aufs.xino,br:$CHANGES=rw aufs $UNION
if [ $? -ne 0 ]; then dmesg | tail -n 1; fatal "can't setup union (aufs)"; fi

debug_shell

# If toram or copy2ram boot parameter is present, copy all fs modules to RAM.
# (skip modules from /optional/ which are not listed in load= boot option)
# Finaly modify DATA variable so it will point to correct directory
if [ "$(cmdline_parameter toram)" != "" -o "$(cmdline_parameter copy2ram)" != "" ]; then
   echolog "copying $LIVECDNAME data to RAM, this may take some time..."
   mkdir -p $COPY2RAM

   # make sure it's in RAM even with changes= parameter
   if [ "$CHANGESMNT" ]; then mount -t tmpfs -o "size=$RAMSIZE" tmpfs $COPY2RAM; fi
   copy_to_ram $DATA $COPY2RAM

   cd_autoeject 1
   fumount $DATA
   fumount $MOUNTDIR/*
   rmdir $MOUNTDIR/* 2>/dev/null # mounted device names are empty, remove them
   DATA=$COPY2RAM
   cd_autoeject 0
fi

debug_shell

# DATA contains path to the base directory of all fs modules which need
# to be mounted and inserted into live filesystem. Do it now.
echolog "inserting all modules and creating live filesystem"
union_insert_modules $UNION $DATA $IMAGES

# the $MEMORY directory can contain $MEMORY/modules too
# in the case if changes= boot argument is used. If not, it doesn't hurt
union_insert_modules $UNION $MEMORY $IMAGES

debug_shell

echolog "copying content of rootcopy directory"
cp -af $DATA/rootcopy/* $UNION 2>/dev/null # may be empty

# TODO: if copy2ram is used, there is no need to preserve the original in memory anymore
#if [ "$DATA" = "$COPY2RAM" ]; then 
#    rm from memory once ??
#fi

echolog "copying liblinuxlive library to union"
cp -af /liblinuxlive $UNION/usr/lib/

debug_shell

echolog "recreating /etc/fstab and /mnt directories"
touch $UNION/etc/fstab
rmdir $UNION/mnt/* 2>/dev/null
fstab_update $UNION

# everything is ready now, so we may unload unused kernel modules
# and do some cleanup, unmount few things which are no longer needed.
rmmod_unused_modules
fumount /usr
fumount /sys

# More likely these directories aren't there.
# Even if they are, this won't hurt.
mkdir -p $UNION/boot
mkdir -p $UNION/proc
mkdir -p $UNION/sys
mkdir -p $UNION/dev
mkdir -p $UNION/tmp
chmod 1777 $UNION/tmp

# Boot will contain whatever was in ./boot directory in the bootable media
# Error output goes to null, as nothing is mounted with copy2ram
mount -n -o rbind $(dirname $DATA)/boot $UNION/boot 2>/dev/null

debug_shell

# Union contains all the files and directories unioned from all modules.
# Change root directory to it, and move initrd's root to /mnt/live/initramdisk
# Finaly execute /sbin/init to start the distribution.
echolog "changing root directory..."

cd $UNION
mkdir -p $INITRAMDISK

# make sure some devices are there
if [ ! -e dev/console ]; then mknod dev/console c 5 1; fi
if [ ! -e dev/null ]; then mknod dev/null c 1 3; fi

# If a boot parameter nohotplug is used, copy all dev files (found by mdev)
# to the unioned dev directory, your Linux may need them since most likely
# udev will not be started in that case
if [ "$(cmdline_parameter nohotplug)" != "" ]; then
   cp -fdR /dev . 2>/dev/null
fi

# find chroot and init
if [ -x bin/chroot ]; then  CHROOT=bin/chroot; fi
if [ -x sbin/chroot ]; then  CHROOT=sbin/chroot; fi
if [ -x usr/bin/chroot ]; then  CHROOT=usr/bin/chroot; fi
if [ -x usr/sbin/chroot ]; then CHROOT=usr/sbin/chroot; fi
if [ "$CHROOT" = "" ]; then fatal "Can't find executable chroot command"; fi

if [ -x bin/init ]; then INIT=bin/init; fi
if [ -x sbin/init ]; then INIT=sbin/init; fi
if [ "$INIT" = "" ]; then fatal "Can't find executable init command"; fi

# time to end Linux Live scripts and start the distribution itself,
# using /sbin/init or whatever was found.
header "starting $LIVECDNAME"

debug_shell

mount -n -o remount,ro aufs .

# We will copy init from the distro to initrd (there should be 2MB free)
# This allows us to use the cleanup script during reboot, as init will be
# started from memory and not from the union and /union will not be busy.

cp -af $INIT /bin
if [ $? -eq 0 ]; then
   pivot_root . $INITRAMDISK
   exec $CHROOT . $INITRAMDISK/bin/init <dev/console >dev/console 2>&1
else # If copying fails, start init directly.
   pivot_root . $INITRAMDISK
   exec $CHROOT . $INIT <dev/console >dev/console 2>&1
fi

header "!!ERROR!!"
fatal "You are not supposed to be here, something went wrong!"
oraz plik liblinuxlive wykorzystywany przez linuxrc (definicje funkcji wykorzystywanych między innymi przez linuxrc).

Kod: Zaznacz cały

#!/bin/bash

# Functions library :: for Linux Live scripts 6
# Author: Tomas M. <http://www.linux-live.org>
#

# =========================================
# GLOBAL variables
# =========================================

# linux live flag to fstab, if fstab line doesn't contain it,
# never remove it from fstab automatically (user added it)
FSTABLLFLAG="# AutoUpdate"

# We have to set these variables very carefully
UNION=union
MEMORY=memory
MOUNTDIR=mnt
CHANGES=$MEMORY/changes
XINO=$MEMORY/xino
COPY2RAM=$MEMORY/copy2ram
IMAGES=$MEMORY/images
INITRAMDISK=$MOUNTDIR/live
LOOPMOUNT=$MOUNTDIR/tmp

# this will be replaced by build script, so never change the following line!
LIVECDNAME="mylinux"

#=========================================
# debug and output functions
#=========================================

# global variable
DEBUG_IS_ENABLED=$(cat /proc/cmdline 2>/dev/null | grep debug)

debug_log()
{
   if [ "$DEBUG_IS_ENABLED" ]; then
      echo "- debug: $*" >&2
      log "- debug: $*"
   fi
}

# echogreen will echo $@ in green color
# $1 = text
#
echogreen()
{
   echo -ne "[0;32m""$@""[0;39m"
}

# echolog
# $1 = text to show and to write to /var/log/messages
#
echolog()
{
   if [ "$1" != "" ]; then
      echogreen "* "
      log "LIVECD:" "$@" 
      echo "$@"
   fi
}

# log
# store given text in /var/log/livedbg
log()
{
   echo "$@" 2>/dev/null >>/var/log/livedbg
}

# show information about the debug shell
show_debug_banner()
{
   echo
   echo "====="
   echo ": Debugging started. Here is the root shell for you."
   echo ": Type your desired commands or hit Ctrl+D to continue booting."
   echo
}

# debug_shell
# executed when debug boot parameter is present
#
debug_shell()
{
   if [ "$DEBUG_IS_ENABLED" ]; then
      show_debug_banner
      ash < /dev/console
      echo
   fi
}

# header
# $1 = text to show
#
header()
{
   echo "[0;1m""$@""[0;0m"
}

fatal()
{
   echolog
   header "Fatal error occured - $1"
   echolog "Something went wrong and we can't continue. This should never happen."
   echolog "Please reboot your computer with Ctrl+Alt+Delete ..."
   echolog
   ash < /dev/console
}

allow_only_root()
{
  # test if the script is started by root user. If not, exit
  if [ "0$UID" -ne 0 ]; then
     echo "Only root can run $(basename $0)"; exit 1
  fi
}

# =========================================
# text processing functions
# =========================================

# look into cmdline and echo $1 back if $1 is set
# $1 = value name, case sensitive, for example 'debug'
#
cmdline_parameter()
{
   debug_log "cmdline_parameter" "$*"
   log "searching for bootparam: $1"
   egrep -o "(^|[[:space:]])$1([[:space:]]|\$)" /proc/cmdline | tr -d " " | tail -n 1
}

# look into cmdline and echo value of $1 option
# $1 = value name, case sensitive, for example 'changes'
#
cmdline_value()
{
   debug_log "cmdline_value" "$*"
   log "searching for bootparam value: $1"
   egrep -o "(^|[[:space:]])$1=[^[:space:]]+" /proc/cmdline | cut -d "=" -f 2- | tail -n 1
}

# Make sure the part of a script after 'mutex_lock' call is atomic,
# that means the 'locked' part of the script can never be execuetd 
# from several processes at the same time, in parallel.
# Every script waits until it gathers the lock.
# The lock directory is saved in /dev instead of /tmp, because /tmp may be
# readonly at the time when the lock is needed (eg. when udev is starting)
# $1 = name of the lock
#
mutex_lock()
{
   debug_log "mutex_lock" "$*"
   while ! mkdir "/dev/ll-mutex-lock-$1" 2>/dev/null; do
      usleep 100000;
   done
}

# Unlock the lock so another waiting process can reusse it and continue
# $1 = name of the lock
#
mutex_unlock()
{
   debug_log "mutex_unlock" "$*"
   rmdir "/dev/ll-mutex-lock-$1" 2>/dev/null
}

# =========================================
# system functions
#=========================================

# setup /usr from /usr.lzm inside initrd
mount_initrd_loops()
{
   debug_log "mount_initrd_loops" "$*"
   if [ -e /usr.lzm ]; then
      mount_device /usr.lzm /usr loop,ro squashfs
   fi
   if [ -e /drivers.lzm ]; then
      mount_device /drivers.lzm /lib/modules/*/kernel/drivers loop,ro squashfs
   fi
}

# modprobe module $1, including all dependencies, suppress all messages
# This was own function, because modprobe in busybox didn't support
# neither gzipped modules nor dependencies. Seems to be fixed now, though.
# $1 = module name, eg. ehci-hcd
# $* = optional arguments
#
modprobe_module()
{
   debug_log "modprobe_module" "$*"
   local MODULE

   MODULE="$1"
   shift

   if [ ! "$MODULE" ]; then return 1; fi
   modprobe "$MODULE" $* 2>/dev/null
}

# mknod next loop device
# - find biggest loop device in /dev/loop/, assume it to be used
# - preallocate (mknod) 20 more loop devices in one round
mknod_next_loop_dev()
{
   debug_log "mknod_next_loop_dev" "$*"
   local i NR END PFX

   mutex_lock mknod_next_loop_dev

   if [ -d /dev/loop ]; then
      NR=$(find /dev/loop/ -maxdepth 1 | sed -r 's/[^0-9]+//' | sort -n | tail -n 1)
      PFX="/"
   else
      NR=$(find /dev/ -maxdepth 1 | grep loop | sed -r 's/[^0-9]+//' | sort -n | tail -n 1)
      PFX=""
   fi
   NR=$(expr 0$NR + 1)
   END=$(expr 0$NR + 20)
   for i in $(seq $NR $END); do
      mknod /dev/loop$PFX$i b 7 $i 2>/dev/null
   done
   echo /dev/loop$PFX$NR

   mutex_unlock mknod_next_loop_dev
}

#=========================================
# Filesystem functions
# =========================================

# Find out what locale is requested
# If no locale is given, use the firts one available (if any)
# $1 = locale (optional argument, if exists, no autodetection is made)
locale_id()
{
   debug_log "locale_id" "$*"
   local LOCALE i

   # first try to find out locale from boot parameters
   LOCALE="$1"
   if [ "$LOCALE" = "" ]; then LOCALE=$(cmdline_value locale); fi
   if [ "$LOCALE" = "" ]; then LOCALE=$(cmdline_value language); fi
   if [ "$LOCALE" = "" ]; then LOCALE=$(cmdline_value lang); fi

   # if not found, set it to locale from usr/lib/locale,
   # but only if there is just ONE directory, nothing more
   # (so we are sure which one to use)
   if [ "$LOCALE" = "" ]; then
      for LOCALE in $(ls -A1p /usr/lib/locale 2>/dev/null | grep / | sed -r "s:[/]|[.].*::"); do
         i="1$i"
      done
      if [ "$i" != "1" ]; then LOCALE=""; fi
   fi

   if [ "$LOCALE" != "" ]; then
      cat /usr/share/locale/locale.alias | sed -r "s/#.*//" | egrep "$LOCALE|$LOCALE""_" | tail -n 1 | tr -s "[[:space:]]" " " | cut -d " " -f 2- | tr -d " "
   fi
}

# Find out what iocharset to use
iocharset()
{
   debug_log "iocharset" "$*"
   local CHARSET IOCHARSET

   # if iocharset is explicitly set at the boot prompt,
   # return it regardless the locale settings
   IOCHARSET=$(cmdline_value iocharset)
   if [ "$IOCHARSET" != "" ]; then
      echo $IOCHARSET
      return 0;
   fi

   # else find out the iocharset from locale_id output, it should match
   # some kernel module (after stripping out few of the dashes)
   IOCHARSET=$(locale_id | cut -d . -f 2- | tr "[[:upper:]]" "[[:lower:]]" | tr -d -)
   if [ "$IOCHARSET" = "" ]; then return 0; fi

   find /lib/modules -name "nls_*" | sed -r 's:^.*/|[.]ko$::g' | cut -b 5- | while read CHARSET; do
      if [ "$(echo $CHARSET | tr "[[:upper:]]" "[[:lower:]]" | tr -d -)" = "$IOCHARSET" ]; then
         echo "$CHARSET"
         return 0
      fi
   done
   return 1
}

# Get filesystem options
# $1 = filesystem
# $2 = 'fstab' or 'mount' ... 'auto'/'noauto' string is enabled (fstab) or disabled (mount)
#

fs_options()
{
   debug_log "fs_options" "$*"
   local NOAUTO IOCHARSET

   NOAUTO=$(cmdline_parameter noauto)
   if [ "$NOAUTO" = "" ]; then NOAUTO="auto"; fi
   if [ "$2" = "fstab" ]; then echo -n "$NOAUTO," ; fi
   if [ "$1" = "swap" ]; then echo "defaults,pri=1"; return 0; fi
   echo -n "noatime,users,suid,dev,exec"

   IOCHARSET=$(iocharset)

   if [ "$1" = "vfat" ]; then
      echo -n ",quiet,umask=0,check=s,shortname=mixed"
      if [ "$IOCHARSET" ]; then
         echo ",iocharset=$IOCHARSET"
      fi
   fi

   if [ "$1" = "iso9660" ]; then
      echo -n ",ro"
      if [ "$IOCHARSET" ]; then
         echo ",iocharset=$IOCHARSET"
      fi
   fi

   if [ "$1" = "ntfs" ]; then
      echo -n ",ro"
      if [ "$IOCHARSET" ]; then
         echo ",nls=$IOCHARSET"
      fi
   fi

   if [ "$1" = "ntfs-3g" ]; then
      echo ",locale=$(locale_id)"
   fi
}

# discover filesystem used on the given device
# Use vfat for msdos filesystem. Use ntfs-3g for ntfs if ntfsmount exists.
# $1 = device, eg. /dev/hda1
#
device_filesystem()
{
   debug_log "device_filesystem" "$*"
   local NTFS

   if [ -e /bin/ntfsmount ]; then NTFS="ntfs-3g"; else NTFS="ntfs"; fi
   blkid -s TYPE "$1" -o value | sed "s/msdos/vfat/" | sed "s/ntfs/$NTFS/"
}

# tell us if the given filesystem is supported
# (eg. it's in /proc/filesystems or we know it)
# $1 = filesystem name
#
is_supported_filesystem()
{
   debug_log "is_supported_filesystem" "$*"

   if [ -e /bin/ntfsmount -a "$1" = "ntfs-3g" ]; then
      return 0
   fi

   # the following command will set the return value
   egrep -q "[[:space:]]$1\$" /proc/filesystems
}

# Mount device $1 to $2
# If the device is using vfat or ntfs filesystem, use iocharset as a mount option
# $1 = /dev device to mount, eg. /dev/hda1, or loop file, or directory
# $2 = mountpoint, eg. /mnt/hda1
# $3 = optional mount options, for example "ro", or "remount,rw"
# $4 = optional filesystem name, in order to skip autodetection
#
mount_device()
{
   debug_log "mount_device" "$*"
   local FS DEV LOOPDEV OPTIONS FILESYSTEM ERR

   # make sure we have enough arguments
   if [ "$2" = "" ]; then return 1; fi
   if [ "$1" = "" ]; then rmdir "$2" 2>/dev/null; return 1; fi
   mkdir -p "$2"

   DEV="$1"
   if [ "$4" != "" ]; then FS="$4"; else FS=$(device_filesystem "$1"); fi
   if [ "$FS" ]; then OPTIONS=$(fs_options $FS mount); FS="-t $FS"; fi
   if [ "$OPTIONS" ]; then OPTIONS="$OPTIONS"; else OPTIONS=""; fi
   if [ -f "$DEV" ]; then OPTIONS="$OPTIONS,loop"; fi
   if [ -d "$DEV" ]; then OPTIONS="$OPTIONS,rbind"; fi
   if [ "$3" ]; then OPTIONS="$OPTIONS,$3"; fi
   OPTIONS=$(echo "$OPTIONS" | sed -r "s/^,+//")

if [ "$FS" = "-t ntfs-3g" ]; then
 OPTIONS="$OPTIONS,force"; fi

   if [ "$FS" = "-t ntfs-3g" ]; then
      ntfsmount "$DEV" "$2" -o $OPTIONS >/dev/null 2>&1
      ERR=$?
   else
      mount -n -o $OPTIONS $FS "$DEV" "$2" >/dev/null 2>&1
      ERR=$?
   fi

   # not enough loop devices? try to create one.
   if [ $ERR -eq 2 ]; then
       LOOPDEV=$(mknod_next_loop_dev)
       OPTIONS=$(echo "$OPTIONS" | sed -r "s/,loop//g")
       losetup "$LOOPDEV" "$DEV" 2>/dev/null # busybox's losetup doesn't support -r
       if [ $? -ne 0 ]; then
          losetup -r "$LOOPDEV" "$DEV" 2>/dev/null # force read-only in case of error
       fi
       mount -n -o $OPTIONS $FS "$LOOPDEV" "$2" >/dev/null 2>&1
       ERR=$?
   fi

   # if nothing works, try to force read-only mount
   if [ $ERR -ne 0 ]; then
       mount -n -r -o $OPTIONS $FS "$DEV" "$2" >/dev/null 2>&1
       ERR=$?
   fi

   if [ $ERR -ne 0 ]; then rmdir $2 2>/dev/null; fi
   return $ERR
}

# unmount all parameters. If the parameter is not mountpoint but
# it's a file or directory, umount the device where the file/dir is stored.
#
# First try normal umount, if that fails then remount read-only
# If -l parameter is specified, do lazy-umount when normal umount fails
# $1..$n = files/directories/devices to be unmounted
#
fumount()
{
   debug_log "fumount" "$*"
   local TARGET LAZY

   while [ "$1" ]; do
      if [ "$1" = "-l" ]; then LAZY="yes"; shift; fi
      TARGET=$(readlink -f "$1")
      if ! ismountpoint "$TARGET"; then
         if [ -f "$TARGET" -o -d "$TARGET" ]; then
            TARGET=$(df "$TARGET" | tail -n 1 | tr -s " " | cut -d " " -f 6)
         fi
      fi

      if [ "$TARGET" != "" ]; then
         umount -n "$TARGET" >/dev/null 2>&1
         if [ $? -ne 0 ]; then
            mount -n -o remount,ro -t ignored ignored "$TARGET" >/dev/null 2>&1
            if [ "$LAZY" ]; then umount -n -l "$TARGET" >/dev/null 2>&1; fi
         fi
      fi
      shift
   done
}

# =========================================
# live module functions
# =========================================

# Create module
# call mksquashfs with apropriate arguments
# $1 = directory which will be compressed to squashfs module
# $2 = output filesystem module file
# $3..$9 = optional arguments like -keep-as-directory or -b 123456789
#
create_module()
{
   debug_log "create_module" "$*"
   rm -f "$2" # overwrite, never append to existing file
   mksquashfs "$1" "$2" -nolzma $3 $4 $5 $6 $7 $8 $9>/dev/null
   if [ $? -ne 0 ]; then return 1; fi
   chmod a-wx "$2" # remove execute and write attrib
   chmod a+r "$2" # add read for everyone
}

# ismountpoint exits with 0 if $1 is mountpoint, else exits with 1
# $1 = directory or loop_file
#
ismountpoint()
{
   debug_log "ismountpoint" "$*"
   local MDIR

   MDIR=$(readlink -f "$1")
   cat /proc/mounts | cut -d " " -f 2 | egrep "^$MDIR\$" >/dev/null 2>&1
}

# Mount filesystem module to destination directory
# $1 = path to the compressed module
# $2 = destination folder
#
mount_module()
{
   debug_log "mount_module" "$*"
   mount_device "$1" "$2" loop,ro squashfs
}

# Insert a directory tree $2 to an union specified by $1
# Top-level read-write branch is specified by it's index 0
# Using =rr enables aufs to optimize real readonly branches
# $1 = union absolute path (starting with /)
# $2 = path to data directory
#
union_insert_dir()
{
   debug_log "union_insert_dir" "$*"
   mount -n -o remount,add:1:$2=rr aufs $1
}

# Find LZM modules in given dir
# $1 = root directory of mounted DATAdir
#
find_modules()
{
   debug_log "find_modules" "$*"
   find "$1/base" "$1/modules" "$1/optional" -name "*.lzm" 2>/dev/null | sort
}

# List all modules in all directories (base, modules, optional)
# and filter out unneeded optional modules (not specified by load= kernel parameter)
# separator for load and noload arguments is "," or ";"
# $1 = root directory of mounted DATAdir
#
list_modules()
{
   debug_log "list_modules" "$*"
   local LOAD NOLOAD

   LOAD=$(cmdline_value load | sed -r 's/\*/.\*/g' | sed -r 's/,|;/|/g')
   NOLOAD=$(cmdline_value noload | sed -r 's/\*/.\*/g' | sed -r 's/,|;/|/g')
   find_modules "$1" | while read LINE; do
      MODNAME=$(echo $LINE | cut -b ${#1}- | cut -b 2-)
      if [ "$(echo $LINE | grep /optional/)" ]; then
         if [ ! "$LOAD" -o ! "$(echo $MODNAME | egrep -i "$LOAD")" ]; then continue; fi
      fi
      if [ "$NOLOAD" -a "$(echo $MODNAME | egrep -i "$NOLOAD")" ]; then continue; fi
      echo $LINE
   done
}

# Insert one single filesystem module to the union
# $1 = union absolute path
# $2 = module full path
# $3 = destination folder, where images will be mounted to
# $4 = preffix length strip (number of characters)
#
union_insert_module()
{
   debug_log "union_insert_module" "$*"
   local TARGET

   TARGET="$3/$(basename $2)"
   if ismountpoint $TARGET; then return 1; fi # skip already used modules
   mkdir -p $TARGET
   mount_module $2 $TARGET
   if [ $? -ne 0 ]; then echo "Cannot read module data. corrupted download?" >&2; return 1; fi
   union_insert_dir $1 $TARGET
   if [ $? -ne 0 ]; then echo "can't insert module to union" >&2; return 2; fi
   echo "$2" | cut -b $(($4+1))-
   echolog "$2" >/dev/null
   return 0
}

# Insert all filesystem modules from $2 directory and subdirectories, to the union
# $1 = union absolute path (starting with /)
# $2 = LiveCD data dir (with directories /base, /modules, etc.)
# $3 = destination folder, where images will be mounted to
#
union_insert_modules()
{
   debug_log "union_insert_modules" "$*"
   local INSERTED

   list_modules $2 | while read MODULE; do
      INSERTED=$(union_insert_module $1 $MODULE $3 ${#2})
      if [ "$INSERTED" != "" ]; then echolog " -> $(echo $INSERTED | sed -r s:^/: :) "; fi
   done
}

# Copy LiveCD modules to RAM directory
# will copy only /boot, and module files from $1
# $1 = data directory
# $2 = target directory in RAM
#
copy_to_ram()
{
   debug_log "copy_to_ram" "$*"
   cp -a "$1/rootcopy" "$2" 2>/dev/null # could be empty
   list_modules "$1" | while read MODULE; do
      TARGET=$(dirname "$MODULE" | cut -b ${#1}- | cut -b 2-)
      mkdir -p "$2/$TARGET"
      cp "$MODULE" "$2/$TARGET"
      if [ $? -ne 0 ]; then fatal "Not enough memory. Using ramsize=$RAMSIZE"; fi
   done
}

# =========================================
# discovery functions
# =========================================

# List all supported network drivers
#
list_network_drivers()
{
   debug_log "list_network_drivers" "$*"

   # these drivers are probed in Slackware's initrd
   # (see initrd.img/scripts/network.sh).
   # I don't have personal experiences with most of these drivers
   # so I'll be happy if you report any particular one to be not working
   # (eg. causing hangups) in order to remove it from this list.

   echo 3c59x acenic atl1 b44 bnx2 de4x5 dgrs e100 eepro100 e1000 epic100 hp100 ne2k-pci \
   olympic pcnet32 r8169 rcpci 8139too 8139cp sktr skge sky2 tulip via-rhine \
   yellowfin tg3 dl2k ns83820 depca ibmtr 3c501 3c503 3c505 3c507 3c509 3c515 \
   ac3200 acenic at1700 cosa cs89x0 de4x5 de600 de620 e2100 eepro eexpress \
   es3210 eth16i ewrk3 fmv18x forcedeth hostess_sv11 hp-plus hp lne390 ne3210 \
   ni5010 ni52 ni65 sb1000 sealevel smc-ultra sis900 smc-ultra32 smc9194 wd \
   | tr " " "\n"
}

# List all CD-ROMs
# by using /proc entries
#
list_cdrom_devices()
{
   debug_log "list_cdrom_devices" "$*"
   local CDDEVICE

   for CDDEVICE in $(cat /proc/sys/dev/cdrom/info 2>/dev/null | head -n 3 | tail -n 1 | cut -d ":" -f 2); do
      echo "/dev/$CDDEVICE"
   done
}

# List all mounted directories
#
list_mounted_directories()
{
   debug_log "list_mounted_directories" "$*"
   if [ "$MOUNTDIR" ]; then
      ls -1 $MOUNTDIR | while read DIR; do
         if ismountpoint $MOUNTDIR/$DIR; then echo $DIR; fi
      done
   fi
}

# List all devices with filesystems
# Return empty result when nohd parameter was given.
#
list_partition_devices()
{
   debug_log "list_partition_devices" "$*"
   if [ "$(cmdline_parameter nohd)" != "" ]; then return 1; fi
   cat /proc/partitions | grep -v loop | grep -v major | grep -v '^$' | sed -r "s:^[0-9 ]+:/dev/:"
   if [ -e /dev/mapper/control ]; then # list LVM partitions if available
      ls -1 /dev/mapper/ | grep -v "^control\$" | sed -r "s:^:/dev/mapper/:"
   fi
}

# List all disk devices
#
list_disk_devices()
{
   debug_log "list_disk_devices" "$*"
   list_partition_devices | egrep -v "[0-9]"
}

# List all partitions marked as Linux Swap
#
list_swap_devices()
{
   debug_log "list_swap_devices" "$*"
   if [ "$(cmdline_parameter nohd)" != "" -o "$(cmdline_parameter noswap)" != "" ]; then return 1; fi
   blkid -t TYPE="swap" -o device
}

# List all block devices
#
list_block_devices()
{
   debug_log "list_block_devices" "$*"
   if [ "$(cmdline_parameter nocd)" = "" ]; then
      list_cdrom_devices
   fi
   list_partition_devices
}

# Format mountdir for device. This function used to append _cdrom or _removable
# suffix to the directory name so KDE was able to assign a nice icon for evey
# device, but this should be done using HAL in KDE nowadays, so we do not
# support these stupid suffixes anymore. Many people will be happy :)
# $1 = device full path, eg. /dev/hda1
#
device_mountdir()
{
   debug_log "device_mountdir" "$*"
   echo "/$MOUNTDIR/$(basename "$1")" | tr -s /
}

# Find file-path on given device
# First it mounts the device read-only. If then the 'path' is found, 
# then remount without RO flag (causes it to be mounted read-write if possible)
# and return the path, else unmount and exit.
# If the device/dev_directory is already mounted, preserve it mounted
# $1 = device
# $2 = path/filename
#
find_filepath()
{
   debug_log "find_filepath" "$*"
   local DIR FOUND PRESERVE

   DIR=$(device_mountdir $1)
   ismountpoint $DIR
   if [ $? -eq 0 ]; then
      PRESERVE="true"
   else
      mount_device $1 $DIR ro
      if [ $? -ne 0 ]; then rmdir $DIR 2>/dev/null; return 1; fi
      PRESERVE=""
   fi

   FOUND=$(ls -A1d $DIR/$2 2>/dev/null | head -n 1 | tr -s '/')

   if [ "$FOUND" = "" ]; then
      if [ "$PRESERVE" != "true" ]; then
         fumount $DIR
         rmdir $DIR 2>/dev/null
      fi
      return 1
   else
      # remount without the 'ro' option now, so use rw or defaults
      # Only in the case it was not mounted already before.
      if [ "$PRESERVE" != "true" ]; then
         fumount $DIR
         mount_device $1 $DIR
         if [ $? -ne 0 ]; then
            rmdir $DIR 2>/dev/null
	    return 2
	 fi
      fi
      echo "$FOUND"
      return 0
   fi
}

# Find file in computer by mounting disks or other storage devices
# and searching for $1 in the mounted directory
# $1 = filename or device-path or devicepath/filename
#
find_file()
{
   debug_log "find_file" "$*"
   local FIND DEVICE DEVPART PATHPART

   # allow using /mnt/... as well as /dev/...
   FIND=$(echo "$1" | sed -r "s:^/mnt/:/dev/:")

   # if parameter is just a device, echo it and exit
   if [ -b "$FIND" -o -c "$FIND" -o "$FIND" = "" ]; then echo "$FIND"; return; fi

   # If path doesn't start with /dev/, try to find the exact path on all devices
   # First, split DEV/PATH parts
   DEVPART=$(echo "$FIND" | egrep -o "^/dev/[^/]+")

   if [ "$DEVPART" = "" ]; then
      # no device is specified. Search all devices for filename $FIND
      PATHPART="$FIND";
      for DEVICE in $(list_mounted_directories) $(list_block_devices); do
         if ! grep -q ":$DEVICE@$PATHPART:" /tmp/_findfile 2>/dev/null; then
            find_filepath "$DEVICE" "$PATHPART"
            if [ $? -eq 0 ]; then return 0; fi
            echo ":$DEVICE@$PATHPART:" >>/tmp/_findfile
         fi
      done
   else
      # try to find PATHPART only on the given device
      PATHPART=$(echo "$FIND" | sed -r 's:^/dev/[^/]+(.*):\1:')
      find_filepath $DEVPART $PATHPART
   fi
}

# Find In Computer
# use 'find_file' function to find the given file/dir
# if nothing found, sleep for a while to allow devices to settle and try again.
# (is there any way to find out if there are devices queued through /sys?)
# $1 = file or directory to find
#
find_in_computer()
{
   debug_log "find_in_computer" "$*"
   local TIMEOUT RESULT

   TIMEOUT=$(cmdline_value scantimeout | sed -r 's/[^0-9]*([0-9]+).*/\1/')
   if [ "$TIMEOUT" = "" ]; then TIMEOUT=10; fi

   RESULT=$(find_file "$1")

   while [ $TIMEOUT -gt 0 -a "$RESULT" = "" ]; do
      echo -ne "- wait a while\r" >&2
      sleep 1
      TIMEOUT=$((TIMEOUT-1))
      RESULT=$(find_file "$1")
   done

   echo $RESULT
}

# Find and run all scripts from the given module
# This function is used by the activate and deactivate script when the distro
# is already started, not during live setup
# $1 = mounted module full path
# $2..$n = optional arguments for the scripts, eg. 'start'
#
find_n_run_scripts()
{
   debug_log "find_n_run_scripts" "$*"
   local MOD

   MOD="$1"
   shift

   if [ -d $MOD/etc/rc.d -o -d $MOD/etc/rc.d/init.d -o -d $MOD/etc/init.d ]; then
      ( find $MOD/etc/rc.d -type f -maxdepth 1 2>/dev/null ; \
        find $MOD/etc/init.d -type f -maxdepth 1 2>/dev/null  ; \
        find $MOD/etc/rc.d/init.d -type f -maxdepth 1 2>/dev/null \
      ) | cut -b ${#MOD}- | cut -b 2- | xargs -n 1 -r readlink -f | sort -u | while read SCRIPT; do
         if [ "$SCRIPT" != "" -a -x "$SCRIPT" -a ! -d "$SCRIPT" ]; then
            # call the script by real path, not from the module
            log "starting '$SCRIPT $@'"
            ${SCRIPT} "$@"
         fi
      done
   fi
}

# =========================================
# hardware preparation functions
# =========================================

# Create block devices to /dev described by /sys entries
#
mdev_start_hotplug()
{
   debug_log "mdev_start_hotplug" "$*"
   echolog "creating /dev entries for block devices"
   mdev -s
   rm /dev/pty??* /dev/tty??* # remove unneeded pty and tty devices
   echo /bin/mdev > /proc/sys/kernel/hotplug # use mdev as a hotplug handler
}

# Modprobe kernel modules needed for the LiveCD
#
modprobe_essential_modules()
{
   debug_log "modprobe_essential_modules" "$*"

   echolog "starting loop device support"
   modprobe_module loop
   echolog "starting cdrom filesystem support"
   modprobe_module isofs
   echolog "starting squashfs support"
   modprobe_module squashfs
   echolog "starting aufs support with brs=1"
   modprobe_module aufs brs=1
   echolog "starting linux filesystem support"
   modprobe_module ext2
   modprobe_module ext3
   modprobe_module reiserfs
   modprobe_module xfs
   modprobe_module vfat
   echolog "starting windows filesystem support"
   modprobe_module vfat
   modprobe_module fuse # for ntfs-3g
   modprobe_module ntfs # for ro driver
}

# Modprobe kernel modules needed for USB masstorage devices
#
modprobe_usb_modules()
{
   debug_log "modprobe_usb_modules" "$*"
   local LSPCI

   # skip module loading if nohotplug bootparam is present
   if [ "$(cmdline_parameter nohotplug)" ]; then return 0; fi

   LSPCI=$(lspci -v | grep -i prog-if)
   if [ "$(echo $LSPCI | egrep -i [eou]hci)" = "" ]; then
      return 0
   fi

   echolog "starting USB support"
   if [ "$(echo $LSPCI | grep -i ehci)" != "" ]; then
      modprobe_module ehci-hcd
   fi
   if [ "$(echo $LSPCI | grep -i ohci)" != "" ]; then
      modprobe_module ohci-hcd
   fi
   if [ "$(echo $LSPCI | grep -i uhci)" != "" ]; then
      modprobe_module uhci-hcd
   fi
   modprobe_module usb-storage
}

# Load drivers for PCMCIA CardBus devices
#
modprobe_pcmcia_modules()
{
   debug_log "modprobe_pcmcia_modules" "$*"

   # skip module loading if nohotplug bootparam is present
   if [ "$(cmdline_parameter nohotplug)" ]; then return 0; fi

   echolog "starting PCMCIA CardBus support"
   modprobe_module pcmcia_core
   modprobe_module pcmcia
   modprobe_module rsrc_nonstatic
   modprobe_module yenta_socket
}

# Load network drivers unless eth[0-9] is found
#
modprobe_network_modules()
{
   debug_log "modprobe_network_modules" "$*"
   local ETH

   # skip module loading if nohotplug bootparam is present
   if [ "$(cmdline_parameter nohotplug)" ]; then return 0; fi

   # probe all drivers. Start by the ones mentioned in pcimodules' output
   for module in $(list_network_drivers | egrep "$(pcimodules | tr "\n" "|")!") $(list_network_drivers); do
      modprobe_module $module
      ETH=$(cat /proc/net/dev | grep : | grep -v lo: | cut -d : -f 1 | tr -d " ")
      if [ "$ETH" != "" ]; then
         echo $ETH
         return 0
      fi
      rmmod $module 2>/dev/null
   done
}

# Start udhcpc to get IP address from DHCP server
# $1 = interface to use (optional)
#
init_dhcp()
{
   debug_log "start_dhcp_client" "$*"

   if [ "$1" != "" ]; then
      ifconfig $1 up
      udhcpc -i $1 -q
   else
      ifconfig eth0 up
      udhcpc -q
   fi
}

# Mount http filesystem from the given server
# $1 = server
# $2 = mountdir
#
mount_httpfs()
{
   debug_log "mount_httpfs" "$*"

   mkdir -p $2
   httpfs $1 $2
}


# Unload modules loaded to kernel which are not used
# This function used to unload more modules, but it may cause
# problems to auto-remove some of them (eg. a network module 
# can seem unneeded even if network is to be used very soon.
#
rmmod_unused_modules()
{
   debug_log "rmmod_unused_modules" "$*"
   rmmod usb-storage uhci-hcd ohci-hcd ehci-hcd 2>/dev/null
   rmmod yenta_socket rsrc_nonstatic pcmcia pcmcia_core 2>/dev/null
}

# kill all unneeded processes, which have bigger PID then the PID of
# current shell. We can't use killall5, as it would kill some processes
# which may be currently needed, for example ntfsmount.
# $1 = maximum pid (kill all lower)
#
killall_unneeded()
{
   debug_log "killall_unneeded" "$*"
   local LIST PID

   PID=$1
   for pid in $(ps | grep -v "PID" | egrep -v "\[.*\]" | fgrep -v mount | fgrep -v posixovl | fgrep -v ntfs | sed -r "s/^[[:space:]]*([0-9]+).*/\\1/"); do
      if [ $pid -lt $PID ]; then
         LIST="$LIST $pid"
      fi
   done

   kill -SIGTERM $LIST 2>/dev/null # SIGTERM
   sleep 2
   kill -SIGKILL $LIST 2>/dev/null # SIGKILL
}

# enable/disable CD autoejecting when unmounted
# $1 = 1|0 ... enable|disable
#
cd_autoeject()
{
   debug_log "cd_autoeject" "$*"
   echo $1 >/proc/sys/dev/cdrom/autoeject
}

# =========================================
# FSTAB functions
# =========================================

# $1 = fstab file
# $2 = device name
dev_is_in_fstab()
{
   debug_log "dev_is_in_fstab" "$*"
   cat "$1" | sed -r "s/#.*//" | egrep -q "^[[:space:]]*$2[[:space:]]"
}

# update given line in fstab, add new values only if the device is not found
# $1 = fstab file to parse
# $2 = device name
# $3 = mountpoint
# $4 = filesystem
# $5 = mount options
#
fstab_add_line()
{
   debug_log "fstab_add_line" "$*"
   local DIR

   if [ "$4" != "swap" ]; then DIR="$3"; else DIR="none"; fi
   if ! dev_is_in_fstab "$1" "$2"; then
      echo "$2" "$DIR" "$4" "$5" 0 0 "$FSTABLLFLAG" >>$1
   fi
}

# create correct fstab file in $1/etc/fstab and create apropriate
# mount directories in $1/mnt. This function is only calld once,
# during liveCD startup (even before init from the distro is started).
# $1 = root directory (union)
#
fstab_update()
{
   debug_log "fstab_update" "$*"
   local FSTAB FSTABTMP

   FSTAB="$1/etc/fstab"
   FSTABTMP=$FSTAB$$
   mkdir -p $1/etc $1/mnt
   cat $FSTAB 2>/dev/null | grep -v "$FSTABLLFLAG" >$FSTABTMP

   fstab_add_line $FSTABTMP aufs / aufs defaults
   fstab_add_line $FSTABTMP proc /proc proc defaults
   fstab_add_line $FSTABTMP sysfs /sys sysfs defaults
   fstab_add_line $FSTABTMP devpts /dev/pts devpts gid=5,mode=620
   fstab_add_line $FSTABTMP tmpfs /dev/shm tmpfs defaults

   list_cdrom_devices | while read DEVICE; do
      MNT=$(device_mountdir $DEVICE)
      FS=$(device_filesystem $DEVICE)
      if [ "$FS" = "" ]; then FS=iso9660; fi
      mkdir -p "$1/$MNT"
      fstab_add_line $FSTABTMP $DEVICE $MNT $FS $(fs_options $FS fstab)
   done
   list_partition_devices | while read DEVICE; do
      MNT=$(device_mountdir $DEVICE)
      FS=$(device_filesystem $DEVICE)
      OPT=$(fs_options $FS fstab)

      if [ "$FS" = "swap" ]; then
         fstab_add_line $FSTABTMP $DEVICE $MNT $FS $OPT
      fi

      # If the partition has a valid filesystem, add it to fstab
      if is_supported_filesystem "$FS"; then
         fstab_add_line $FSTABTMP $DEVICE $MNT $FS $OPT
         mkdir -p "$1/$MNT"
      fi
   done

   mv -f $FSTABTMP $FSTAB
}
ODPOWIEDZ