#!/bin/bash
#
# $Id$
#
# Change a partition to aufs
#
# Parameters:
#	$1	The mountpoint
#	$2	tmpfs size or auto
#

BASE0="/fsprotect"
BASE="/fsprotect/fs"

if [ "x$1" = "x" ] || [ "x$2" = "x" ] ; then
	echo "Usage $0 [mountpoint] [tmpfs size]"
	exit 1
fi

if ! [ -d "$1" ] ; then
	echo "$1: Not a directory"
fi

# Remove trailing /
MP="${1%/}"

# Check whether a path ($1) is a mounted mountpoint
is_mounted()
{
	# Forbid relative paths
	[ "x${1#/}" = "x$1" ] && return 1

	# Forbid /
	[ "x$1" = "x/" ] && return 1

	# Look mtab
	T=$(grep " $1/* " /etc/mtab)

	[ -z "$T" ] && return 1

	# Check for virtual filesystems
	T2=$(echo "$T" | grep -E " (tmpfs|rootfs|devpts|usbfs|sysfs|proc) ")

	[ -z "$T2" ] || return 1

	return 0
}

# Assumning that $1 was moved to $2 and $1 is now an aufs of $2,
# restore all mount points by moving them back from $2 to $1.
restore_dir_mp()
{
	# Don't use head, awk and sort. They are in /usr/bin
	#T=$(cat /proc/mounts | grep -E "^[^[:space:]]* $2/" | awk '{print $2}' | sort -u | head -1)

	CNT=1000
	T=$(cat /proc/mounts | grep -E "^[^[:space:]]* $2/" | \
		sed "s,^[^[:space:]]* \($2/[^[:space:]]*\) .*,\1,g" | ( \
		while read line ; do
			TCNT=${#line}
			if [ "$TCNT" -lt "$CNT" ] ; then
				CNT="$TCNT"
				T="$line"
			fi
		done

		[ "$CNT" -lt "1000" ] && echo "$T"

		# Always be successful
		true
		)
	   )

	[ -z "$T" ] && return

	TDST=$(echo $1/${T#$2} | sed 's,//,/,g')
	if [ -d "$TDST" ] ; then
		echo "mount --move $T $TDST"
		mount --move $T $TDST
		restore_dir_mp "$1" "$2"
	else
		echo "$TDST doesn not exist!!!"
	fi
}

# Check whether the kernel supports aufs
has_aufs()
{
	[ -e /proc/filesystems ] || return 1
	
	grep "\<aufs\>" /proc/filesystems > /dev/null 2>&1 && return 0

	return 1
}

if ! is_mounted "$MP" ; then
	echo "$1: Doesn't look like a supported/valid/mounted filesystem"
	exit 1
fi

if is_aufs "$MP" ; then
	echo "$1: Seems to be already protected"
	exit 1
fi

echo "Proceeding with $MP"

if ! [ -d "$BASE0" ] ; then
	echo "$BASE0 is not a directory!"
	echo "Something is wrong!"
	exit 1
fi

[ -d "$BASE" ] || mkdir "$BASE"

DST0=$(echo "${MP#/}" | sed 's,/,-,g')

DST="$BASE/$DST0"

DSTORIG="$DST/orig"
DSTMOVE="$DST/move"
DSTTMP="$DST/tmp"
DSTAUFS="$DST/aufs"

[ -d "$DST" ] || mkdir "$DST"
[ -d "$DSTORIG" ] || mkdir "$DSTORIG"
[ -d "$DSTMOVE" ] || mkdir "$DSTMOVE"
[ -d "$DSTTMP" ] || mkdir "$DSTTMP"
[ -d "$DSTAUFS" ] || mkdir "$DSTAUFS"

# All directories are created. Go-on

set -e

# If size is "auto" then use 50% of memory which is the default
# for tmpfs
SZ="$2"
if [ "x$SZ" = "xauto" ] ; then
	SZ="50%"
fi

# Do this first to catch errors
if ! mount -t tmpfs -o size=$SZ none "$DSTTMP" ; then
	echo "Failed to create tmpfs. Is '$2' a correct tmpfs size parameter?"
	echo "Examples: 10M, 2G, 50%"
	exit 1
fi
# Copy permissions
chown --reference="$MP" "$DSTTMP"
chmod --reference="$MP" "$DSTTMP"

mount -o bind "${MP}" "$DSTORIG"
mount -t aufs -o "dirs=$DSTTMP=rw:$DSTORIG=ro" none "$DSTAUFS"
# Move this here, in case it has other filesystems mounted beneath it

# Ensure it's a private mount
PROPAGATION=$(findmnt -o PROPAGATION -n -M /)
if [ ! "$PROPAGATION" = "private" ] ; then
	mount --make-private /
fi
# Move it
mount --move "$MP" "$DSTMOVE"
# Restore propagation
mount --make-$PROPAGATION /

mount -o bind "$DSTAUFS" "$MP"
umount "$DSTAUFS"
# Move filesystems outside of it
restore_dir_mp "$MP" "$DSTMOVE"
# Umount it
umount "$DSTMOVE"
mount -o remount,ro "$DSTORIG"

echo "Done."

exit 0

