Khôi phục dữ liệu RAID0

1. Tổng quan

Ở bài viết này mình thực hành trên RAID0 tạo bằng mdadm (software RAID) để dễ quan sát và thao tác. Với hardware RAID, nguyên lý striping (dữ liệu chia thành các khối xen kẽ giữa các ổ) hoàn toàn giống nhau, nên về lý thuyết cách khôi phục cũng tương tự: cần xác định đúng chunk size, offset và thứ tự ổ. Tuy nhiên, mỗi hãng controller phần cứng lại có cách lưu metadata riêng, không theo chuẩn mdadm, do đó thao tác cụ thể sẽ khác (thường phải dùng chính controller cùng loại hoặc phần mềm chuyên dụng). Nói cách khác, bạn có thể áp dụng cùng tư duy, nhưng công cụ và bước thực hiện sẽ tùy môi trường.

Bài viết này tổng hợp quy trình tạo RAID0 và phục hồi dữ liệu trong hai kịch bản phổ biến:

  • Còn metadata của RAID (mdadm superblock còn nguyên).
  • Mất metadata của RAID (chỉ còn metadata của file system, ví dụ ext4).

Nội dung gồm kiến thức nền cần nhớ, sơ đồ bố cục dữ liệu, quy trình từng bước, cách dò offset/chunk/order khi mất metadata, mẹo dùng dumpe2fs/mke2fs -n/mount -o sb=...

2. Kiến thức nền (nên nắm trước khi cứu dữ liệu)

  • RAID0 chỉ striping, không có redundancy. Mất layout (offset, chunk, thứ tự đĩa) = rất khó ghép lại đúng → ưu tiên làm việc trên bản backup bằng image.
  • Mdadm metadata (superblock) nắm thông tin layout. Nếu mất, vẫn còn cơ hội nhờ metadata của ext4 (primary/backup superblock).
  • Các tham số quan trọng khi ghép tay:
    • OFFSET: vùng bỏ qua đầu device name hoặc image trước khi bắt đầu data (thường vài MiB tùy cách tạo).
    • CHUNK: là đơn vị dữ liệu nhỏ nhất được ghi xuống một đĩa trong mảng RAID (ví dụ 64 KB).
    • ORDER: thứ tự sắp xếp 2 nhánh (disk0, disk1). Sai order → dữ liệu được xem như không khớp, ext4 không thể mount được.
  • Mount an toàn khi kiểm tra: -o ro,noload (chỉ đọc, không replay journal).

Sơ đồ bố cục (minh họa)

[ Image/disk 0 ]                   [ Image/disk 1 ]
|-- OFFSET --|==== stripe0 ====|   |-- OFFSET --|==== stripe1 ====|
             |==== stripe2 ====|   |==== stripe3 ====|  ...
RAID0 (md9) = stripe0 + stripe1 + stripe2 + stripe3 + ...
ext4 nằm rải đều trên md9 (có primary & backup superblocks)

2. Kịch bản còn metadata RAID (dễ nhất)

Bạn có hai lựa chọn an toàn, tùy mục tiêu:

2.1. Assemble bằng metadata sẵn có (khuyến nghị khi chắc chắn thiết bị/phân vùng đúng)

  • Mdadm sẽ đọc superblock ở đầu đĩa để biết layout (chunk size, offset, thứ tự đĩa, UUID…).
  • Ưu điểm: nhanh, ít rủi ro, chính xác vì dựa trên metadata thật.
  • Nhược điểm: nếu metadata hỏng hoặc không đồng bộ thì không assemble được.

Các command dưới đây để khởi tạo 1 mảng raid0, mount raid0 này, copy dữ liệu vào và sau đó umount, stop và backup nó ra image.

shell> mdadm --create --verbose /dev/md1 --level=0 --raid-devices=2 --chunk=64 /dev/sdb /dev/sdc
mdadm: Defaulting to version 1.2 metadata
mdadm: array /dev/md1 started.

shell> mkfs.ext4 /dev/md1
mke2fs 1.46.5 (30-Dec-2021)
Creating filesystem with 1047040 4k blocks and 262144 inodes
Filesystem UUID: 52eb4278-1ff1-482b-ae0e-5c19528fb585
Superblock backups stored on blocks:
	32768, 98304, 163840, 229376, 294912, 819200, 884736

Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done

shell> mkdir /mnt/raid0
shell> mount /dev/md1 /mnt/raid0
shell> cp -r /usr/bin /mnt/raid0/
shell> cp -r /var /mnt/raid0/
shell> cp -r /etc /mnt/raid0/
shell> sync
shell> umount /mnt/raid0
shell> mdadm --stop /dev/md1
mdadm: stopped /dev/md1

shell>sudo dd if=/dev/sdb of=~/disk0.img bs=4M status=progress
2088763392 bytes (2.1 GB, 1.9 GiB) copied, 7 s, 298 MB/s
512+0 records in
512+0 records out
2147483648 bytes (2.1 GB, 2.0 GiB) copied, 7.46567 s, 288 MB/s

shell> sudo dd if=/dev/sdc of=~/disk1.img bs=4M status=progress
2139095040 bytes (2.1 GB, 2.0 GiB) copied, 13 s, 164 MB/s
512+0 records in
512+0 records out
2147483648 bytes (2.1 GB, 2.0 GiB) copied, 13.4616 s, 160 MB/s

Lưu ý, ở trên mình có sử dụng lệnh sync, mục đích là:

  • Đẩy toàn bộ dữ liệu đã copy trong RAM xuống đĩa thật sự.
  • Tránh tình trạng dữ liệu còn nằm trong cache của kernel nhưng chưa kịp ghi xong đã umount hoặc mdadm --stop, dễ dẫn đến mất hoặc thiếu file.

Chạy mdadm --examine để xem metadata superblock của mdadm (phiên bản 1.2) trên từng disk.

shell> mdadm --examine /dev/sdb /dev/sdc
/dev/sdb:
          Magic : a92b4efc
        Version : 1.2
    Feature Map : 0x0
     Array UUID : fcb162ce:35550071:cdb382d3:2a5b776d
           Name : node77:1  (local to host node77)
  Creation Time : Mon Sep  1 15:15:45 2025
     Raid Level : raid0
   Raid Devices : 2

 Avail Dev Size : 4188160 sectors (2045.00 MiB 2144.34 MB)
    Data Offset : 6144 sectors
   Super Offset : 8 sectors
   Unused Space : before=6064 sectors, after=0 sectors
          State : clean
    Device UUID : 570444d7:49f0cf2d:fe7aa9ca:5f6d9869

    Update Time : Mon Sep  1 15:15:45 2025
  Bad Block Log : 512 entries available at offset 8 sectors
       Checksum : cb315b22 - correct
         Events : 0

     Chunk Size : 64K

   Device Role : Active device 0
   Array State : AA ('A' == active, '.' == missing, 'R' == replacing)

/dev/sdc:
          Magic : a92b4efc
        Version : 1.2
    Feature Map : 0x0
     Array UUID : fcb162ce:35550071:cdb382d3:2a5b776d
           Name : node77:1  (local to host node77)
  Creation Time : Mon Sep  1 15:15:45 2025
     Raid Level : raid0
   Raid Devices : 2

 Avail Dev Size : 4188160 sectors (2045.00 MiB 2144.34 MB)
    Data Offset : 6144 sectors
   Super Offset : 8 sectors
   Unused Space : before=6064 sectors, after=0 sectors
          State : clean
    Device UUID : e817f362:1a7cb393:70e2c0f4:c3e57842

    Update Time : Mon Sep  1 15:15:45 2025
  Bad Block Log : 512 entries available at offset 8 sectors
       Checksum : bfbbda5b - correct
         Events : 0

     Chunk Size : 64K

   Device Role : Active device 1
   Array State : AA ('A' == active, '.' == missing, 'R' == replacing)

Ở output trên chúng ta thấy

  • Mảng RAID0 gồm 2 đĩa /dev/sdb (role 0) và /dev/sdc (role 1).
  • Chunk size = 64 KB, dữ liệu bắt đầu từ offset 3 MiB.
  • Metadata v1.2 nằm ở sector 8.
  • Raid có cả hai đĩa đều active, UUID mảng trùng khớp.

Thông số layout quan trọng

  • Chunk Size : 64K
    → Đây là kích thước stripe unit (chunk). Mỗi block dữ liệu 64 KB được ghi lần lượt xen kẽ sang từng đĩa.
  • Device Role : Active device 0 / 1
    → Vị trí thứ tự của disk trong raid: /dev/sdb là ổ số 0, /dev/sdc là ổ số 1.
  • Array State : AA
    → Có 2 ký tự ứng với 2 đĩa trong mảng. “A” = active, “.” = missing. Ở đây “AA” nghĩa là cả hai đĩa đều có mặt và đang active.

Ghép lại mảng RAID0 từ hai ổ, ở chế độ chỉ-đọc, không thay đổi metadata.

shell> mdadm --assemble --readonly /dev/md1 /dev/sdb /dev/sdc
mdadm: /dev/md1 has been started with 2 drives.

Gắn /dev/md1 ở chế độ chỉ-đọc, bỏ qua journal, đảm bảo an toàn dữ liệu.

shell> mkdir /mnt/recovered
shell> mount -o ro,noload /dev/md1 /mnt/recovered

shell> df -h
Filesystem                         Size  Used Avail Use% Mounted on
tmpfs                              1.6G  1.2M  1.6G   1% /run
/dev/mapper/ubuntu--vg-ubuntu--lv   47G   12G   34G  25% /
tmpfs                              7.9G     0  7.9G   0% /dev/shm
tmpfs                              5.0M     0  5.0M   0% /run/lock
/dev/sda2                          2.0G  126M  1.7G   7% /boot
tmpfs                              1.6G  4.0K  1.6G   1% /run/user/1001
/dev/md1                           3.9G  997M  2.7G  27% /mnt/recovered

shell> ls /mnt/recovered
bin  etc  lost+found  var

Nếu mount OK → sao chép dữ liệu.

2.2. Bỏ qua metadata, ghép tay (dùng khi muốn khóa cứng OFFSET/CHUNK)

Cách này sẽ bỏ qua hoàn toàn metadata mdadm, bạn phải tự chỉ định offset, chunk size và order.

Ưu điểm: kiểm soát tuyệt đối, dùng được cả khi metadata mất/hỏng.

Nhược điểm: phải đoán hoặc dò layout; sai thông số thì không mount được hoặc dữ liệu sai.

Trong demo trên, mình đang dùng offset 3 MiB cho mỗi image để đi thẳng vào vùng data:

Gắn loop, mỗi loop bỏ qua đúng 3 MiB.

shell> L0=$(sudo losetup -f --show -o 3145728 ~/disk0.img)
shell> L1=$(sudo losetup -f --show -o 3145728 ~/disk1.img)

shell> echo $L0
/dev/loop4

shell>echo $L1
/dev/loop5

Build RAID0 mới (không đụng metadata), chunk 64 KB

shell> sudo mdadm --build /dev/md2 --level=0 --raid-devices=2 --chunk=64 \
  --assume-clean "$L0" "$L1"
mdadm: array /dev/md2 built and started.

Umount /mnt/recovered vì ở ví dụ trên chúng ta đang sử dụng nó để mount.

umount -l /mnt/recovered

Mount chỉ-đọc, không replay journal.

shell> sudo mount -o ro,noload /dev/md2 /mnt/recovered
shell> ls /mnt/recovered
bin  etc  lost+found  var
  • Ưu điểm: kiểm soát rõ ràng OFFSET/CHUNK
  • Nhược điểm: phải biết/đoán đúng ORDER.

Nếu ext4 hợp lệ, đọc 2 byte magic tại offset 0x438 (1080) của /dev/md9 sẽ là 53 EF:

sudo dd if=/dev/md9 bs=1 skip=1080 count=2 status=none | hexdump -C

3. Kịch bản mất metadata RAID (còn metadata của ext4)

Mục tiêu là phải dò đúng bộ ba OFFSET–ORDER–CHUNK để ext4 mount được.

3.1. Chuẩn bị

Nhìn vào kết quả lsblk chúng ta thấy đang còn các device name và thư mục mount đang được sử dụng ở ví dụ trên.

shell> lsblk
NAME                      MAJ:MIN RM  SIZE RO TYPE  MOUNTPOINTS
loop0                       7:0    0   62M  1 loop  /snap/core20/1587
loop1                       7:1    0 79.9M  1 loop  /snap/lxd/22923
loop2                       7:2    0   47M  1 loop  /snap/snapd/16292
loop3                       7:3    0 89.4M  1 loop  /snap/lxd/31333
loop4                       7:4    0    2G  0 loop
└─md2                       9:2    0    4G  0 raid0 /mnt/recovered
loop5                       7:5    0    2G  0 loop
└─md2                       9:2    0    4G  0 raid0 /mnt/recovered
sda                         8:0    0   50G  0 disk
├─sda1                      8:1    0    1M  0 part
├─sda2                      8:2    0    2G  0 part  /boot
└─sda3                      8:3    0   48G  0 part
  └─ubuntu--vg-ubuntu--lv 253:0    0   48G  0 lvm   /
sdb                         8:16   0    2G  0 disk
└─md1                       9:1    0    4G  1 raid0
sdc                         8:32   0    2G  0 disk
└─md1                       9:1    0    4G  1 raid0
sdd                         8:48   0    2G  0 disk
sr0                        11:0    1 1024M  0 rom

Dọn dẹp sạch sẽ.

shell> sudo umount -f /mnt/recovered 2>/dev/null || true
shell> sudo mdadm --stop /dev/md1 2>/dev/null || true
shell> sudo mdadm --stop /dev/md2 2>/dev/null || true
shell> sudo losetup -D 2>/dev/null || true

shell>  lsblk
NAME                      MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
loop0                       7:0    0   62M  1 loop /snap/core20/1587
loop1                       7:1    0 79.9M  1 loop /snap/lxd/22923
loop2                       7:2    0   47M  1 loop /snap/snapd/16292
loop3                       7:3    0 89.4M  1 loop /snap/lxd/31333
sda                         8:0    0   50G  0 disk
├─sda1                      8:1    0    1M  0 part
├─sda2                      8:2    0    2G  0 part /boot
└─sda3                      8:3    0   48G  0 part
  └─ubuntu--vg-ubuntu--lv 253:0    0   48G  0 lvm  /
sdb                         8:16   0    2G  0 disk
sdc                         8:32   0    2G  0 disk
sdd                         8:48   0    2G  0 disk
sr0                        11:0    1 1024M  0 rom

Sao lưu image để giữ bản gốc (tùy chọn).

cp ~/disk0.img ~/d0_demo.img
cp ~/disk1.img ~/d1_demo.img

Xóa metadata mdadm trong image. Với mdadm v1.2 của raid0 trên có Data Offset = 6144 sector = đúng 3 MiB. Vậy zero sạch 3 MiB đầu image là chắc chắn xoá metadata, KHÔNG đụng vùng data.

shell> sudo dd if=/dev/zero of=~/d0_demo.img bs=1M count=3 conv=notrunc status=progress
3+0 records in
3+0 records out
3145728 bytes (3.1 MB, 3.0 MiB) copied, 0.00189227 s, 1.7 GB/s

shell> sudo dd if=/dev/zero of=~/d1_demo.img bs=1M count=3 conv=notrunc status=progress
3+0 records in
3+0 records out
3145728 bytes (3.1 MB, 3.0 MiB) copied, 0.00174413 s, 1.8 GB

Kiểm chứng không còn md superblock, kỳ vọng output là No md superblock detected.

shell> L0=$(sudo losetup -f --show ~/d0_demo.img)
shell> L1=$(sudo losetup -f --show ~/d1_demo.img)

shell> echo $L0
/dev/loop4
shell> echo $L1
/dev/loop5

shell> sudo mdadm --examine "$L0" || true
mdadm: No md superblock detected on /dev/loop4.
shell> sudo mdadm --examine "$L1" || true
mdadm: No md superblock detected on /dev/loop5.

Tháo loop.

shell> losetup -d "$L0" "$L1"

shell> lsblk
NAME                      MAJ:MIN RM  SIZE RO TYPE MOUNTPOINTS
loop0                       7:0    0   62M  1 loop /snap/core20/1587
loop1                       7:1    0 79.9M  1 loop /snap/lxd/22923
loop2                       7:2    0   47M  1 loop /snap/snapd/16292
loop3                       7:3    0 89.4M  1 loop /snap/lxd/31333
sda                         8:0    0   50G  0 disk
├─sda1                      8:1    0    1M  0 part
├─sda2                      8:2    0    2G  0 part /boot
└─sda3                      8:3    0   48G  0 part
  └─ubuntu--vg-ubuntu--lv 253:0    0   48G  0 lvm  /
sdb                         8:16   0    2G  0 disk
sdc                         8:32   0    2G  0 disk
sdd                         8:48   0    2G  0 disk
sr0                        11:0    1 1024M  0 rom

3.2. Dò layout tự động (script brute-force)

Sử dụng script raid0_bruteforce.sh để thử OFFSETS × ORDER × CHUNKS, kiểm tra ext4 magic và thử mount bằng primary hoặc backup superblock.

cat > raid0_bruteforce.sh << 'OEF'
#!/usr/bin/env bash
set -euo pipefail

IMG0=${1:-~/d0_demo.img}
IMG1=${2:-~/d1_demo.img}

OFFSETS=(0 1048576 2097152 3145728 4194304 8388608) # 0,1M,2M,3M,4M,8M
CHUNKS=(32 64 128 256)                               # KB
BACKUP_SB_4K=(32768 98304 163840 229376 294912 819200 884736)  # mkfs.ext4 in ra
MNT=/mnt/recovered

cleanup() {
  sudo umount -f "$MNT" 2>/dev/null || true
  sudo mdadm --stop /dev/md9 2>/dev/null || true
  sudo losetup -D 2>/dev/null || true
}
trap cleanup EXIT
cleanup

sudo mkdir -p "$MNT"

for OFF in "${OFFSETS[@]}"; do
  # gắn loop với offset đang thử
  L0=$(sudo losetup -f --show -o $OFF "$IMG0")
  L1=$(sudo losetup -f --show -o $OFF "$IMG1")

  for ORDER in "$L0 $L1" "$L1 $L0"; do
    for C in "${CHUNKS[@]}"; do
      echo "=== Try: offset=$OFF order=[$ORDER] chunk=${C}KB ==="
      sudo mdadm --stop /dev/md9 2>/dev/null || true
      sudo mdadm --build /dev/md9 --level=0 --raid-devices=2 --chunk=$C --assume-clean $ORDER

      # 1) check magic primary 0xEF53 ở byte 1080
      if sudo dd if=/dev/md9 bs=1 skip=1080 count=2 status=none | grep -q $'\x53\xEF'; then
        echo ">>> Primary superblock found (53 ef). Mounting ro..."
        if sudo mount -o ro,noload /dev/md9 "$MNT" 2>/dev/null; then
          echo "OK -> offset=$OFF order=[$ORDER] chunk=${C}KB"
          ls -l "$MNT"
          exit 0
        fi
      fi

      # 2) nếu primary ko thấy/ko mount, thử backup SB
      for B in "${BACKUP_SB_4K[@]}"; do
        SB_1K=$((B*4)) # sb= dùng block 1KB
        if sudo mount -o ro,noload,sb=$SB_1K /dev/md9 "$MNT" 2>/dev/null; then
          echo ">>> MOUNT OK with backup SB=$B (sb=$SB_1K 1KB-blocks)"
          echo "-> offset=$OFF order=[$ORDER] chunk=${C}KB"
          ls -l "$MNT"
          exit 0
        fi
      done
    done
  done

  # tháo loop trước khi thử offset tiếp theo
  sudo mdadm --stop /dev/md9 2>/dev/null || true
  sudo losetup -d "$L0" "$L1"
done

echo "❌ Không dò được layout. Mở rộng phạm vi OFFSETS/CHUNKS hoặc kiểm tra image."
exit 1
OEF
chmod +x raid0_bruteforce.sh

Khung dò cơ bản như sau:

  • OFFSETS ví dụ: 0, 1M, 2M, 3M, 4M, 8M
  • CHUNKS ví dụ: 32, 64, 128, 256 (KB)
  • ORDER: cả hai hướng L0 L1L1 L0
  • Kiểm tra:
    • Đọc 2 byte ở 1080 để kiếm 53 EF
    • Nếu chưa mount được, thử lần lượt backup superblock (danh sách lấy từ mke2fs -n hoặc dumpe2fs)

Kết quả nếu tìm ra offset=3145728 (3 MiB), order=[L0 L1], chunk=32KB, mount thành công ro,noload và liệt kê được bin/ etc/ var/.

shell> ./raid0_bruteforce.sh ~/d0_demo.img ~/d1_demo.img
=== Try: offset=0 order=[/dev/loop2 /dev/loop4] chunk=32KB ===
mdadm: array /dev/md9 built and started.
=== Try: offset=0 order=[/dev/loop2 /dev/loop4] chunk=64KB ===
mdadm: array /dev/md9 built and started.
=== Try: offset=0 order=[/dev/loop2 /dev/loop4] chunk=128KB ===

<đã lược bỏ bớt logs>

mdadm: array /dev/md9 built and started.
=== Try: offset=3145728 order=[/dev/loop2 /dev/loop4] chunk=32KB ===
mdadm: array /dev/md9 built and started.
>>> Primary superblock found (53 ef). Mounting ro...
OK -> offset=3145728 order=[/dev/loop2 /dev/loop4] chunk=32KB
total 52
drwxr-xr-x  2 root root 28672 Aug 26 15:17 bin
drwxr-xr-x 97 root root  4096 Aug 26 15:17 etc
drwx------  2 root root 16384 Aug 26 15:17 lost+found
drwxr-xr-x 13 root root  4096 Aug 26 15:17 var

Đây là bằng chứng layout đã khớp.

5. Một vài ví dụ tình huống thường gặp

Biết chunk (64 KB), không chắc ORDER

Giữ OFFSET cố định (ví dụ 3 MiB), thử hai thứ tự:
--build ... diskA diskB rồi --build ... diskB diskA.

Kiểm tra 53 EF và thử mount ro,noload. Thành công → đúng ORDER.

Image từ phân vùng thay vì nguyên đĩa

OFFSET có thể khác do vị trí bắt đầu phân vùng (alignment). Hãy mở rộng mảng OFFSETS (ví dụ thêm 2048*512 = 1 MiB, 4096*512 = 2 MiB, … theo LBA bắt đầu).

Ext4 blocksize 1 KB vs 4 KB

Khi dùng sb= phải quy đổi đúng đơn vị 1 KB. Nếu bạn thấy danh sách backup theo 4 KB, đừng quên nhân 4.

6. Lời khuyên (best practices)

  • Sao lưu sector-level trước: dùng ddrescue/dd để build image read-only; thao tác cứu dữ liệu trên bản sao.
  • Đảm bảo chỉ-đọc: --readonly, -o ro,noload, tránh bất kỳ ghi đè nào lên image gốc.
  • Ghi chép layout ngay khi tạo RAID: chunk, level, thứ tự đĩa, offset; lưu mdadm --detail --scan vào note.
  • Cẩn trọng với --build: chỉ dùng khi không còn metadata; luôn kèm --assume-clean.
  • Mở rộng phạm vi dò nếu chưa ra: thêm OFFSETS (0/1M/2M/3M/4M/8M/16M…), CHUNKS (16–1024 KB) và cả ORDER.
  • Kiểm tra magic ext4 (0xEF53) để lọc nhanh tổ hợp sai; sau đó thử mount bằng backup SB trước khi kết luận hỏng.
  • Phân biệt 512e vs 4Kn: sai giả định size sector đôi khi làm lệch OFFSET; kiểm tra thông tin thiết bị/image.
  • Không tin tuyệt đối vào số đẹp: OFFSET 1–4 MiB là phổ biến nhưng không bắt buộc; luôn xác nhận bằng thực nghiệm.

8. Kết luận

  • Khi còn metadata mdadm, phục hồi RAID0 hầu như là thao tác assemble/mount chỉ-đọc.
  • Khi mất metadata mdadm, vẫn còn đường cứu nhờ metadata ext4: brute-force OFFSET–ORDER–CHUNK, xác thực bằng ext4 magic và backup superblock, rồi mount ro,noload.
  • Luôn làm trên bản sao, mở rộng phạm vi dò khi cần và ưu tiên các bước an toàn để bảo toàn dữ liệu.

Bài viết gần đây

spot_img

Related Stories

Leave A Reply

Please enter your comment!
Please enter your name here

Đăng ký nhận thông tin bài viết qua email