Мне часто приходится затирать жёсткие диски пачками, например при модернизации стораджей, и мне был очень необходим скоростной источник потока данных которыми затирается целевой диск. Чтобы более менее надёжно затереть данные на диске неразрушив сам диск, недостаточно затереть его нулями из /dev/zero. В тоже время поток данных из /dev/urandom слишком медленный чтобы быстро затирать современные жёсткие диски.
Поскольку необходимый мне уровень стирания не претендовал на уровень военной разведки то я решил выкрутиться через большой массив случайных (псевдослучайных) чисел. А чтобы массив этот быстро работал я решил закинуть его в память. И /dev/shm – идеальное место.
Для стирания я использовал raid контроллер чтобы затирать сразу множество дисков. А для автоматизации процесса написал bash-скрипт. Скрипт имеет свой счётчик продвижения и его можно прервать Ctrl+C и затем продожить с той позиции где он был прерван.
Предупреждение! Скриптом можно уничтожить важные данные, поэтому используйте его на свой страх и риск.
#!/bin/bash # # Script to high speed destoy all data on HDD with random data # # randomdatafile in memory fs randomdatafile=/dev/shm/randombaloon.raw # file to continue writing if script was aborted cfgfile=$0.continue # one block will be 4Mb blocksize="$((4*1024*1024))" if [[ -z $1 ]]; then DISKLIST=`fdisk -l 2>/dev/null | grep '^Disk[ \t]\+/dev/' | grep -v mapper` DISKLIST1=`echo -e "${DISKLIST}" | sed -e 's/\://g' -e 's/\,//g' | awk '{print $2" "$3" "$4" "$5" "$6}'` PS3='Please enter block device to destroy data: ' IFS=$'\n' read -d '' -r -a options <<< "${DISKLIST1}" select opt in "${options[@]}" "Quit" do if (( REPLY == 1 + ${#options[@]} )) ; then echo "Exit" exit 1 elif (( REPLY > 0 && REPLY <= ${#options[@]} )) ; then target=`echo ${opt} | awk '{print $1}'` break else echo "Incorrect choice. Try again." fi done else target="$1" fi if [[ ! -b $target ]]; then echo "$target: is not a block device" exit 1 elif [[ ! -w $target ]]; then echo "$target: has no write permission" exit 1 elif grep -q $target /etc/mtab; then echo "$target: is mounted! Script is aborted!" exit 1 fi if [[ -f ${cfgfile} ]] ; then # read previous counter start="$(< ${cfgfile})" read -p "Found prevous conter. Continue from [${start}]?" input if [[ ! -z ${input} ]] ; then if [[ -n ${input//[0-9]/} ]]; then echo "Value must be an integer!" echo "Aborting." exit 1 else start=${input} fi fi else start=0 fi if [[ ! -e ${randomdatafile} ]] ; then echo "Generate initial random data array and write 16x4M blocks to /dev/shm/" # with /dev/urandom pseudorandom generator # for i in `seq 0 15` ; do echo -en "Write block:\t$i\t-\t"; head -c ${blocksize} < /dev/urandom | 2> /dev/null | dd of=${randomdatafile} bs=4M seek=$i conv=notrunc 2>&1 | grep copied | awk '{print $8" "$9}' ; done # with openssl pseudorandom generator for i in `seq 0 15` ; do echo -en "Write block:\t$i\t-\t"; openssl rand -rand /dev/urandom ${blocksize} 2> /dev/null | dd of=${randomdatafile} bs=4M seek=$i conv=notrunc 2>&1 | grep copied | awk '{print $8" "$9}' ; done else echo "${randomdatafile} file exists. Ok." fi filesize=`stat -c%s ${randomdatafile}` if [[ ${filesize} -ne $(($blocksize*16)) ]] ; then echo "File size ${filesize} is not equal $(($blocksize*16))" exit 1 fi echo "${randomdatafile} file size is Ok." echo "Ready to write data to ${target}" fdisk_str=`fdisk -l ${target} 2>/dev/null | grep ${target}` echo -e "${fdisk_str}" echo echo "WARNING: IT WILL DESTROY ALL DATA ON THE DISK" echo "Are you sure you want to proceed?" read -p 'Type YES if you really want to proceed: ' input echo if [[ ${input} != YES ]]; then echo "Ok. Aborting." exit 1 fi # calculate disk size disksize=`echo -e "${fdisk_str}" | grep '^Disk' | grep 'bytes$' | awk '{print $(NF-1)}'` disksizeinblocks=$(($disksize / $blocksize)) # write data for ((i=${start}; 1; i++)); do position=$((${i}*16)) size=$((${i}*64)) echo -en "Writed from ${size}M\tpos:${position} of ${disksizeinblocks}(BLKs)\ti:${i}\t" dd if=${randomdatafile} of=${target} bs=4M seek=${position} oflag=direct 2>&1 | grep copied | awk '{print $8" "$9}' A=("${PIPESTATUS[@]}") if [[ "${A[0]}" -ne "0" ]] ; then echo -e "dd has returned an error ${A[0]}." exit ${A[0]} fi if [[ "${position}" -ge "${disksizeinblocks}" ]] ; then echo -e "Something wrond. dd sould return an error because the end is reached on destenation device." exit 1 fi echo "$i" > "${cfgfile}.new" mv "${cfgfile}.new" "${cfgfile}" done echo "Done..."
UPDATE
Первая версия скрипта была с pipefail от которых я решил избавиться так как меня интересует не любая ошибка в цепочке команд, а только ошибка первой команды dd. Я сделал это через массив ${PIPESTATUS[@] который содержит результат последего выполнения цепочки команд.