#!/bin/sh
#
#  Contributed by Kent Robotti <robotti@godmail.com>
#  Fixes by Paul Cunnane <paul@cunnane.net>
#


usage() {
echo "
=============================================================
convertfs v1.3

Convert filesystem on device from this type to this type.

Usage: convertfs device from_fs_type to_fs_type

Filesystem types: minix xfs jfs reiserfs ext2 ext3

Example: convertfs /dev/hda2 ext2 reiserfs
=============================================================
"
exit 0
}

fs_types() {
unset fs_type
for fs_type in minix xfs jfs reiserfs reiser4 ext2 ext3; do
if [ "$1" = "$fs_type" ]; then
  fs_type=yes
  break
fi
done
if [ ! "$fs_type" = "yes" ]; then
  echo "Not a filesystem type \`$1' option."
  echo "Filesystem types: minix xfs jfs reiserfs reiser4 ext2 ext3"
  exit 1
fi
}

minix_convert() {
if [ "$1" = "minix" ]; then
  hd_dev=`echo "$2" | cut -b 1-8`
  blocks=`fdisk -l "$hd_dev" | grep "$2" | tr -d + | tr -s " " | cut -d ' ' -f 4`
  if [ "$blocks" -gt "2048287" ]; then
    echo "The \`minix' partition on \`$2' is more than 2GB."
    echo "It's probably to big to convert."
    echo -n "Try anyway? [N/y] "
    read ans
    if [ ! "$ans" = "y" -a ! "$ans" = "Y" ]; then
      exit 1
    fi
  fi
fi
}

ext2_convert() {
if [ "$1" = "ext2" -a "$2" = "ext3" ]; then
  echo "You can convert \`$3' to \`$2' this way."
  echo "tune2fs -j $3"
  exit
elif [ "$1" = "ext3" -a "$2" = "ext2" ]; then
  echo "You can convert \`$3' to \`$2' this way."
  echo "tune2fs -O ^has_journal $3"
  exit
fi
}

find_mkfs() {
if ! grep -q "$1" /proc/filesystems; then
  echo "No support for \`$1' filesystem in the kernel."
  echo "If it's a module load it \`modprobe $1'."
  exit 1
elif ! grep -q "$2" /proc/filesystems; then
  echo "No support for \`$2' filesystem in the kernel."
  echo "If it's a module load it \`modprobe $2'."
  exit 1
fi

if ! type "$3" 1>/dev/null 2>/dev/null; then
  echo "Can't find \`$3' on the system."
  exit 1
fi

if [ "$2" = "reiserfs" ]; then
  kernel_version=`uname -r | tr -d . | cut -b 1-2` 
  mkreiserfs 1>/dev/null 2>/tmp/mkreiserfs.out
if grep -q "\--format" /tmp/mkreiserfs.out; then
if [ "$kernel_version" -ge "24" ]; then
  reiserfs_version="--format 3.6"
else
  reiserfs_version="--format 3.5"
fi
else
if [ "$kernel_version" -ge "24" ]; then
  reiserfs_version="-v 2"
else
  reiserfs_version="-v 1"
fi
fi
  rm -f /tmp/mkreiserfs.out
fi
}

mk_fs() {
if [ "$1" = "ext2" ]; then
  mke2fs "$2"
elif [ "$1" = "ext3" ]; then
  mke2fs -j "$2"
elif [ "$1" = "reiserfs" ]; then
  echo -e "y\ny" | mkreiserfs $reiserfs_version "$2"
elif [ "$1" = "reiser4" ]; then
  echo "y" | mkfs.reiser4 -f "$2"
elif [ "$1" = "jfs" ]; then
  mkfs.jfs -f "$2"
elif [ "$1" = "xfs" ]; then
  mkfs.xfs -f "$2"
elif [ "$1" = "minix" ]; then
  hd_dev=`echo "$dev" | cut -b 1-8`
  blocks=`fdisk -l "$hd_dev" | grep "$dev" | tr -d + | tr -s " " | cut -d ' ' -f 4`
  mkfs.minix -v "$2" "$blocks"
fi
}

error() {
  umount "$loopdev" 2>/dev/null
  losetup -d "$loopdev" 2>/dev/null
  umount "$dev" 2>/dev/null
  rm -rf "$tmp"
  exit 1
}

# device to convert
dev="$1"
# filesystem to convert from
from_fs_type="$2"
# filesystem to convert to
to_fs_type="$3"

if [ "$dev" = "" ]; then
  usage
elif [ ! -b "$dev" ]; then
  echo "No such device \`$dev'."
  exit 1
fi

if [ "$from_fs_type" = "" -o "$to_fs_type" = "" ]; then
  usage
else
  ext2_convert "$from_fs_type" "$to_fs_type" "$dev"
  minix_convert "$from_fs_type" "$dev"
  fs_types "$from_fs_type"
  fs_types "$to_fs_type"
fi

if [ "$to_fs_type" = "ext2" ]; then
  find_mkfs "$from_fs_type" "$to_fs_type" mke2fs
elif [ "$to_fs_type" = "ext3" ]; then
  find_mkfs "$from_fs_type" "$to_fs_type" mke2fs
elif [ "$to_fs_type" = "reiserfs" ]; then
  find_mkfs "$from_fs_type" "$to_fs_type" mkreiserfs
elif [ "$to_fs_type" = "reiser4" ]; then
  find_mkfs "$from_fs_type" "$to_fs_type" mkfs.reiser4
elif [ "$to_fs_type" = "jfs" ]; then
  find_mkfs "$from_fs_type" "$to_fs_type" mkfs.jfs
elif [ "$to_fs_type" = "xfs" ]; then
  find_mkfs "$from_fs_type" "$to_fs_type" mkfs.xfs
elif [ "$to_fs_type" = "minix" ]; then
  find_mkfs "$from_fs_type" "$to_fs_type" mkfs.minix
fi
  
if ! grep -q "loop" /proc/devices; then
  echo "No loop device support in the kernel."
  echo "If it's a module load it \`modprobe loop'."
  exit 1
fi

if [ ! -b /dev/loop7 ]; then
  mknod -m 660 /dev/loop7 b 7 7 || exit
fi

cat /usr/share/doc/convertfs/README.Debian || echo "convertfs - use at your own risk"
echo
echo "Type 'I am using convertfs at my own risk.' and press enter to continue."
read REPLY
if [ "$REPLY" != "I am using convertfs at my own risk." ]; then
  echo "Aborting."
  exit
fi

loopdev=/dev/loop7

tmp=/tmp/convertfs
fs1root="$tmp/fs1root"
fs2root="$tmp/fs2root"

fsimage_name=fsimage
fsindex_name=fsindex
fssuper_name=fssuper
fsimage="$fs1root/$fsimage_name"
fsindex="$fs1root/$fsindex_name"
fssuper="$tmp/$fssuper_name"

mkdir -p "$fs1root" || exit
mkdir -p "$fs2root" || exit

echo "== Creating clone of \`$from_fs_type' filesystem that's on \`$dev'. =="
umount "$dev" 2>/dev/null
mount -t "$from_fs_type" "$dev" "$fs1root" || error
devclone "$dev" "$fsimage" || error

echo "===== Creating destination \`$to_fs_type' filesystem. ====="
umount "$loopdev" 2>/dev/null
losetup -d "$loopdev" 2>/dev/null
losetup "$loopdev" "$fsimage" || error
mk_fs "$to_fs_type" "$loopdev" || error

echo "============== Copying files =============="
mount -t "$to_fs_type" "$loopdev" "$fs2root" || error
ls -1A "$fs1root" | while read file; do
	if [ "x$file" != "x$fsimage_name" ] &&
	   [ "x$file" != "x$fsindex_name" ] &&
	   [ "x$file" != "xlost+found" ] &&
	   [ "x$file" != "x$fssuper_name" ]; then
		#mv -f "$fs1root/$file" "$fs2root/" || error
		ftwmv "$fs1root/$file" "$fs2root/$file" || error
	fi
done
ls -lA "$fs2root"
df -h "$fs2root"
umount "$loopdev" || error
losetup -d "$loopdev" 2>/dev/null

echo "=== Preparing info for block relocation ==="
prepindex "$fsimage" "$fsindex" "$fssuper" || error
umount "$dev" || error
dd if="$fssuper" of="$dev" || exit
sync

echo "============ Relocating blocks ============"
devremap "$dev" || exit

echo "=== Filesystem conversion accomplished! ==="
rm -rf "$tmp"
echo "NOTE: If you want to boot the new filesystem on \`$dev' you"
echo "      should edit \`/etc/fstab' and add something like this."
echo "      $dev   /   $to_fs_type   defaults   1   1"

