#! /bin/sh
#
#   fine-erase.sh -- 複数回書き込みで念入りにディスクのデータを消去する
#
#	使い方: /bin/sh fine-erase.sh 消し方 ディスク
#		消し方: [0z1fRr]+
#		ディスク: デバイスファイルで指定、先頭の「/dev/」は省く
#		例: /bin/sh fine-erase.sh 0101010 ad0
#
#	0.0: Jun. 27, 2004 by Dai ISHIJIMA
#	0.1: Jul. 11, 2004
#	0.2: Jun. 28, 2006
#	0.3: Aug.  1, 2006 (タイムスタンプ for wpout07)
#	0.4: Feb. 10, 2007 (ログ記録 for wpout08)
#	0.5: Feb. 15, 2007 (エンターキー長押し対策)
#	0.6: Sep. 12, 2007 (wpout09)
#	0.7: Oct.  6, 2007 (計算を32ビット符号付き整数の範囲で行う)
#	0.8: Dec. 23, 2007 (バーグラフ表示の計算修正)
#	0.9: Jan.  2, 2008 (ログ修正)
#	1.0: Mar. 22, 2014 (画面のちらつきを減らす (更新を減らす))
#

ERRORLOG="${ERRORLOG:-/tmp/wperrlog.txt}"
exec 2>> $ERRORLOG
echo "# start $0, $@" 1>&2
echo '# ---------------------------- #' 1>&2

# 単独でも動くように
LOGFIL="${LOGFIL:-/tmp/wpoutlog.txt}"
DMESG="${DMESG:-/var/run/dmesg.boot}"
PATTERNFIL="${PATTERNFIL:-/tmp/pattern.dat}"
dispupd="${DISPUPD:-2}"

echo '# ---------------------------- #' >> $LOGFIL
echo -n "script $0 started on " >> $LOGFIL
date >> $LOGFIL

if [ ! -s $PATTERNFIL ]; then
	stuff -g > $PATTERNFIL
	echo "random pattern file $PATTERNFIL generated" >> $LOGFIL
fi

stuffs="${1:-fz}"
disk="${2:-ad0}"
dmesg="${DMESG:-/var/run/dmesg.boot}"
# 一度に32Mバイトずつ
blklen="${BLKLEN:-33554432}"
# 出力ブロックサイズは1Mバイト
obs="${OBS:-1048576}"
ocnt=$(( $blklen / $obs ))
ounit=$(( $obs / 1048576 ))
# 入力ブロックサイズは1kバイト
ibs="${IBS:-1024}"
icnt=$(( $blklen / $ibs ))

#
resultfil=/tmp/result

#
title="${TITLE:-ハードディスク消去ツール『wipe-out』}"

#
if [ ! -w /dev/$disk ]; then
	dialog	--title "$title" \
		--msgbox "ディスク $disk が存在しないか、書き込みできません" \
		5 64
	exit 1
fi

#
mount -p | sed -n -E "/^.dev.$disk/p" > $resultfil
if [ -s $resultfil ]; then
	dialog	--title "$title" \
	    --msgbox "マウント中のディスク $disk を消去することはできません" \
	    5 64
	exit 1
fi

dname=`sed -n -E "/^${disk}:.*<.*>/p" $dmesg | sed 's/.*\(<.*>\).*/\1/'`
bytes=`disksize -B /dev/$disk`
cap=`disksize -p /dev/$disk`

msg="ディスク ${disk} ${dname} の\n"
msg="${msg}全データ ${bytes}バイトを、\n"

nstuff=0
for stuff in `echo $stuffs | sed 's/\(.\)/\1 /g'` ; do
	case x"$stuff" in
		x[0z])	msg="${msg}0、" ;;
		x[1f])	msg="${msg}1、" ;;
		x[Rr])	msg="${msg}R、" ;;
		x[Pp])	msg="${msg}P、" ;;
		x[Qq])	msg="${msg}Q、" ;;
	esac
	nstuff=$(( $nstuff + 1 ))
done

msg="${msg}の順に\n${nstuff}回上書きして消去します\n"
msg="${msg}\n                      よろしいですか?"

sh dummyread.sh
dialog --title "$title" --yesno "$msg" 10 64

case x"$?" in
	x1)
		dialog	--title "$title" \
			--infobox 'ディスクの消去をキャンセルしました' 3 64
		sleep 2
		exit 1
		;;
esac


msg="本当に${disk} ($cap) のデータを消去していいですか?"

sh dummyread.sh
dialog	--title "$title" \
	--hline "$dname" \
	--yesno "$msg" \
	5 72

case x"$?" in
	x1)
		dialog	--title "$title" \
			--infobox 'ディスクの消去をキャンセルしました' 3 64
		sleep 2
		exit 1
		;;
esac

# 消去のパラメータ (Mバイト単位で数える)
mbytes=`disksize -m /dev/$disk`
max=$(( $mbytes / $ounit ))

stage=0
xstage=1
startsec=`tinydate '+%s'`
totalrems=''

# 開始時刻
startstamp=`date '+%Y-%m-%d %H:%M'`

#
echo "v ---------------------------- v" >> $LOGFIL
echo -n "erase ($nstuff times) started on " >> $LOGFIL
date >> $LOGFIL
echo "  $disk ($dname) $bytes bytes with $stuffs" >> $LOGFIL

nerrs=0
xcent=100
xmill=1000
mcent=$max
mmill=$max
if [ $max -gt 1048576 ]; then
	xcent=1
	xmill=1
	mcent=$(( ( $max + 99 ) / 100 ))
	mmill=$(( ( $max + 999 ) / 1000 ))
fi
for stuff in `echo $stuffs | sed 's/\(.\)/\1 /g'` ; do
	skip=0
	stagestart=`tinydate '+%s'`
	case "$stuff" in
		[0z])	method="'0'を上書きして" ;;
		[1f])	method="'1'を上書きして" ;;
		[Rr])	method="ランダムデータを上書きして" ;;
		[Pp])	stuff -g > $PATTERNFIL
			echo "random pattern file $PATTERNFIL generated" \
				>> $LOGFIL
			method="ランダムパターンを上書きして" ;;
		[Qq])	method="直前パターンの補数を上書きして" ;;
	esac
	rems=''
	status=''
	status2=''
	errormsg=''
	#
	echo -n "stage: $stage, with writing $stuff " >> $LOGFIL
	echo -n "started on " >> $LOGFIL
	date >> $LOGFIL
	#
	lastdisp=0
	dispintv=0
	while [ $skip -le $max ]; do
		# 計時
		cursec=`tinydate '+%s'`
		# バーグラフ
		p=$(( ( $xcent * $skip ) / $mcent ))
		m=$(( ( $xmill * $skip ) / $mmill ))
		q=$(( $p / 2 ))
		i=0
		bar=''
		while [ $i -lt $q ]; do
			bar="$bar"'#'
			i=$(( $i + 1 ))
		done
		while [ $i -lt 50 ]; do
			bar="$bar"'_'
			i=$(( $i + 1 ))
		done
		# 残り時間
		elapsed=$(( $cursec - $stagestart ))
		if [ $elapsed -gt 10 ]; then
			if [ $m -gt 0 ]; then
				remain=$(( ( 1000 - $m ) * $elapsed / $m + 1 ))
				if [ $remain -gt 120 ]; then
					rems=$(( ( $remain + 59 ) / 60 ))
					rems="残り およそ${rems}分"
				else
					rems="残り およそ${remain}秒"
				fi
			fi
		fi
		if [ $elapsed -gt 120 ]; then
			elas=$(( ( $elapsed + 30 ) / 60 ))
			elas="${elas}分"
		else
			elas="${elapsed}秒"
		fi
		# 全体バーグラフ
		pp=$(( ( $p + 100 * $stage ) / $nstuff ))
		mm=$(( ( $m + 1000 '*' $stage ) / $nstuff ))
		q=$(( $pp / 2 ))
		i=0
		totalbar=''
		while [ $i -lt $q ]; do
			totalbar="$totalbar"'#'
			i=$(( $i + 1 ))
		done
		while [ $i -lt 50 ]; do
			totalbar="$totalbar"'_'
			i=$(( $i + 1 ))
		done
		ee=$(( $cursec - $startsec ))
		if [ $mm -gt 0 ]; then
			rr=$(( ( 1000 - $mm ) * $ee / $mm + 1 ))
			if [ $rr -gt 120 ]; then
				totalrems=$(( ( $rr + 59 ) / 60 ))
				totalrems="残り およそ${totalrems}分"
			else
				totalrems="残り およそ${rr}秒"
			fi
		fi
		if [ $ee -gt 120 ]; then
			totalelas=$(( ( $ee + 30 ) / 60 ))
			totalelas="${totalelas}分"
		else
			totalelas="${ee}秒"
		fi
		#
		text="ディスク ${disk} のデータを消去しています。\n\n"
		text="${text}全 ${nstuff} 工程中の ${xstage} 番め  "
		text="${text} 全体の ${pp}% 終了\n"
		text="${text}${totalelas} 経過  ${totalrems}  \n\n"
		text="${text}        0%|${totalbar}|100%\n\n"
		text="${text}この工程では${method}、データを消去しています。\n"
		text="${text}${p}% ($skip/$max) 終了 ／ "
		text="${text}${elas} 経過  ${rems}  \n\n"
		text="${text}        0%|${bar}|100%\n\n"
		text="${text}\n${errormsg}"
		dispintv=$(( $cursec - $lastdisp ))
		if [ $dispintv -ge $dispupd ]; then
			dialog --title "$title" --infobox "$text" 15 72
			lastdisp=$cursec
		else
			echo -e '\r\c'
		fi
		case "x$progchar" in
			xO) progchar='.' ;;
			xo) progchar='O' ;;
			*)  progchar='o' ;;
		esac
		echo -n "$progchar $status2"
		case x$DEBUG in
			x[Yy]*)
				status2='now in debug mode...'
				errormsg=''
				sleep 1
				;;
			*)
				status=`stuff -$stuff < $PATTERNFIL | dd ibs=$ibs obs=$obs seek=$skip count=$icnt of=/dev/$disk 2>&1`
				status2=`echo "$status" | sed -n '/transfer/p'`
				errormsg=`echo "$status" | sed -n -E '/records (in|out)$/!p' | sed -n '/transferred in/!p'`
				;;
		esac
		# 記録
		case x"$skip" in
		    x0)
			echo "stage: $stage, skip: $skip, $status2" >> $LOGFIL
			;;
		esac
		case x"${nerrs}"x"${errormsg}" in
		    x[0-8]x?*)
			echo "errmesg[${nerrs}]: $errormsg" >> $LOGFIL
			nerrs=$(( $nerrs + 1 ))
			;;
		    x9x?*)
			echo "errmesg[${nerrs}]: $errormsg" >> $LOGFIL
			echo "Too many errors encounterd..." >> $LOGFIL
			nerrs=$(( $nerrs + 1 ))
			;;
		esac
		# 次ループ
		skip=$(( $skip + $ocnt ))

	done
	stage=$(( $stage + 1 ))
	xstage=$(( $xstage + 1 ))
	echo "stage: $stage, skip: $skip, $status2" >> $LOGFIL
done

sleep 2

#
echo "number of errmessages: $nerrs" >> $LOGFIL
echo -n "erase finished on " >> $LOGFIL
date >> $LOGFIL
echo "  $disk ($dname, $cap) with overwriting $nstuff times" >> $LOGFIL
echo "^ ---------------------------- ^" >> $LOGFIL

cursec=`tinydate '+%s'`
elapsed=$(( $cursec - $startsec ))
if [ $elapsed -gt 120 ]; then
	min=$(( $elapsed / 60 ))
	sec=$(( $elapsed - $min * 60 ))
	elas="${min}分${sec}秒"
else
	elas="${elapsed}秒"
fi

# 終了時刻
endstamp=`date '+%H:%M'`

# ログ保存
echo 'The contents of this storage device' > /tmp/erase-log.$disk
echo "$disk ($cap) '$dname'" >> /tmp/erase-log.$disk
echo -n 'were erased with ' >> /tmp/erase-log.$disk
echo 'hard disk wiping tool "wipe-out."' >> /tmp/erase-log.$disk
echo "Erase $nstuff times ($stuffs)" >> /tmp/erase-log.$disk
echo " $startstamp -> $endstamp" >> /tmp/erase-log.$disk

echo "but NOT verified" > /tmp/verify-log.$disk

sh dummyread.sh
dialog	--title "$title" \
	--msgbox "ディスクのデータ消去が終了しました (${elas})" 5 72

#
echo '# ---------------------------- #' 1>&2

exit 0

# EOF
