bash: สคริปต์แก้ Boot record ของ NTFS

update 2556-12-03

ลองใช้สคริปต์กับพาร์ติชั่นที่ขนาดไม่เท่ากันแล้วปรากฎว่าใช้ไม่ได้ เพราะ NTFS เก็บข้อมูลหลายอย่างมากกว่าแค่จุดเริ่มต้นและขนาด (ดูที่ NTFS Partition Boot Sector)

วิธีที่ได้ผลกว่าคือ ฟอร์แมตไดร์ฟไว้ก่อน -> เก็บ boot sector ไว้ 80 ไบต์ -> ทำ ntfsclone -> เอา boot recort ที่เก็บไว้มาเขียนทับ

ตัวอย่างเช่น จะคัดลอก ไดร์ฟ /dev/sda1 ไปยัง /dev/sdb1 ขั้นตอนจะเป็นดังนี้

$ sudo mkfs.ntfs -f /dev/sdb1
$ sudo dd if=/dev/sdb1 of=sdb1.img bs=80 count=1
$ sudo ntfsclone -O /dev/sdb1 /dev/sda1
$ sudo dd if=sdb1.img of=/dev/sdb1

*** สคริปต์ด้านล่างนี้ ล้าสมัยแล้ว ***

แก้ปัญหาเวลาใช้ ntfsclone ในการ restore พาร์ติชั่น NTFS มาลงในฮาร์ดดิสก์ลูกใหม่ ซึ่งจุดเริ่มต้นและขนาดอาจไม่เท่าของเดิม

วิธีการคือใช้ข้อมูลจากตาราง Master Boot Record ปัจจุบัน มาเขียนทับ boot record ของพาร์ติชั่น NTFS ที่ต้องการ โดยใช้เชลล์สคริปต์

$ vi ntfs_fix_boot_sector.sh
#!/bin/bash

function usage() {
    cat <<EOF
Fix NTFS boot record:
Usage: $0 DEVICE
Example: $0 /dev/sda1
EOF
    exit 1
}

if [ ! "$1" ]; then
    usage
fi

PART=$1

if [ ! -b "$PART" ]; then
    echo -e "$PART not found. Exit.\n"
    usage
fi

BSF="`echo $PART | tr '/' '_'`.img"
BSFB=${BSF}.bak
BSFD="${BSF}_`date +%F`.bak"

function reverse_byte () {  #reverse_byte HEXSTR
    local S=$1  #HEXSTR
    local B
    local C
    while [ "$S" ]; do
        B=${S:(-2)}
        S=${S:0:-2}
        C="${C}\\x${B}"
    done
    echo $C
}

function replace_byte () {  #replace_byte OFFSET LENGTH NUMBER
    local O=$1  #OFFSET
    local L=$2  #LENGTH
    local N=$3  #NUMBER
    let NL=${L}*2
    XN=`printf "%0${NL}x" $N`
    RXN=`reverse_byte $XN`
    #echo "printf $RXN | dd of=$BSF bs=1 seek=$O count=$L conv=notrunc"
    printf $RXN | sudo dd of=$BSF bs=1 seek=$O count=$L conv=notrunc > /dev/null 2>&1
}

sudo dd if=$PART of=$BSF bs=512 count=1 > /dev/null 2>&1

TMP="/tmp/$0_${RANDOM}.txt"

HDD=$PART
while [ "`echo ${HDD:(-1)} | tr '0123456789' ' '`" == " " ]; do
    HDD=${HDD:0:-1}
done

sudo fdisk -l $HDD > $TMP

if ! cat $TMP | grep $PART | grep NTFS > /dev/null 2>&1; then
    echo -e "$PART is not NTFS partition. Exit.\n"
    sudo rm $TMP
    sudo rm $BSF
    usage
fi

if [ ! -f "$BSFB" ]; then
    cp $BSF $BSFB
else
    cp $BSF $BSFD
fi

echo "Fixing $PART ..."

HEADS=`cat $TMP | grep 'sectors/track' | cut -d, -f1 | cut -d\  -f1`
SECTORS=`cat $TMP | grep 'sectors/track' | cut -d, -f2 | cut -d\  -f2`
START=`cat $TMP | grep "${PART} " | awk -F' ' '{ print $2 }'`
END=`cat $TMP | grep "${PART} " | awk -F' ' '{ print $4 }'`
if [ "$START" == "*" ]; then
    START=`cat $TMP | grep "${PART} " | awk -F' ' '{ print $3 }'`
    END=`cat $TMP | grep "${PART} " | awk -F' ' '{ print $4 }'`
fi
let LENGTH=$END-$START

OFFSET_HEADS=26
OFFSET_SECTORS=24
OFFSET_START=28
OFFSET_LENGTH=40
LEN_HEADS=1
LEN_SECTORS=1
LEN_START=4
LEN_LENGTH=4

for i in HEADS SECTORS START LENGTH; do
    A="OFFSET_${i}"
    B="LEN_${i}"
    replace_byte ${!A} ${!B} ${!i}
done

echo "Fix with heads=$HEADS, sectors/track=$SECTORS, start=$START, length=$LENGTH"

sudo dd if=$BSF of=$PART > /dev/null 2>&1

sudo rm $TMP

cat <<EOF

Command used:
sudo dd if=$BSF of=$PART

Revert with command:
sudo dd if=$BSFB of=$PART
EOF

ตัวอย่าง สมมุติว่าพาร์ติชั่นที่ต้องการเป็น /dev/sda1 คำสั่งคือ

$ ntfs_fix_boot_record.sh /dev/sda1

ได้ผลลัพธ์คือ

Fixing /dev/sda1 ...
Fix with heads=255, sectors/track=63, start=2048, length=62914559

Command used:
sudo dd if=_dev_sda1.img of=/dev/sda1

Revert with command:
sudo dd if=_dev_sda1.img.bak of=/dev/sda1

ที่มา:

Topic: 
Creative Commons License ลิขสิทธิ์ของบทความเป็นของเจ้าของบทความแต่ละชิ้น
ผลงานนี้ ใช้สัญญาอนุญาตของครีเอทีฟคอมมอนส์แบบ แสดงที่มา-อนุญาตแบบเดียวกัน 3.0 ที่ยังไม่ได้ปรับแก้