一次真實的遷移記錄:群暉 DSM 7.2 的
volume_1(921G Btrfs)整盤插到飛牛 fnOS 上,全程只讀、不寫源盤一個字節,400GB 數據 7 小時拷完。中間踩過的坑、試錯的死路、以及最終爲什麼只有
btrfs restore能走通,都寫在下面。
一、背景
用了幾年黑群暉,決定換飛牛 fnOS。數據不想走”外接硬盤中轉”那一套(要來回倒兩輪),也不想在中間再搭一臺機器。
最理想的做法是:把群暉的數據盤直接插到飛牛機器裏,在飛牛裏把數據讀出來、拷到飛牛自己的存儲空間,然後群暉這組盤原樣收起來——將來想回去還能插回去接着用。
聽起來很簡單,實際上卡了整整一晚上。
二、環境
| 項目 | 內容 |
|---|---|
| 源 | 群暉 DSM 7.2,單盤 931.5G,volume_1 = 921G Btrfs |
| 目標 | 飛牛 fnOS,系統盤 15G + 數據盤 3.6T |
| 源卷元數據 | label 2024.05.11-14:52:46 v69057,transid 4516191 |
| 源卷實際用量 | bytes_used = 399,898,472,448(約 372.5 GiB) |
| 工具 | btrfs-progs v6.2,內核 6.18.18.c1126-trim |
三、第一步:先認清盤的結構
群暉的存儲是三層套娃,必須一層層剝,跳步就會得到”wrong fs type”這種毫無信息量的報錯:
物理盤 sdb3
└─ mdadm RAID1 md2 ← 群暉的軟 RAID(SHR / Basic 也是 md)
└─ LVM PV vg1
└─ LV volume_1 ← 921G
└─ Btrfs ← 真正的文件系統在這裏
看一眼現狀:
cat /proc/mdstat # md2 是否 active、是否 degraded
pvs; vgs; lvs -a -o +segtype,devices
lsblk -o NAME,SIZE,TYPE,FSTYPE,LABEL,MOUNTPOINT
我的實際輸出(簡化):
sdb 931.5G disk
├─sdb1 8G part linux_raid_member <host>:trim
├─sdb2 2G part linux_raid_member DiskStation:1
└─sdb3 921.3G part linux_raid_member <host>:2
└─md2 921.3G raid1 LVM2_member
├─vg1-syno_vg_reserved_area 12M lvm
└─vg1-volume_1 921G lvm btrfs 2024.05.11-14:52:46 v69057
小提示:
vg1-volume_1顯示出來了不等於能用。LVM 的Attr裏第 5 位是a纔是 active;-wi-a-----表示已激活但沒被打開,-wi-a-----和-wi-ao----只差”有沒有掛載中”。
激活:
vgchange -ay vg1
ls -l /dev/mapper/
四、第二步:爲什麼 mount 一定失敗
這時候所有人(包括我)的第一反應都是:
mount -o ro /dev/mapper/vg1-volume_1 /mnt/syno1
結果:
mount: /mnt/syno1: wrong fs type, bad option, bad superblock on /dev/mapper/vg1-volume_1,
missing codepage or helper program, or other error.
dmesg 裏纔是真相:
BTRFS: device label 2024.05.11-14:52:46 v69057 devid 1 transid 4516191
/dev/mapper/vg1-volume_1 (253:2) scanned by mount (4100)
BTRFS critical (device dm-2 state ES): corrupt leaf: root=1 block=487157137408 slot=0,
invalid root flags, have 0x40000000 expect mask 0x1000000000000001
BTRFS error (device dm-2 state ES): read time tree block corruption detected on logical 487157137408 mirror 1
BTRFS error (device dm-2 state ES): read time tree block corruption detected on logical 487157137408 mirror 2
BTRFS error (device dm-2 state ES): open_ctree failed: -5
翻譯一下:
- 不是壞盤,也不是文件系統壞了。 兩個 mirror 報一模一樣的錯,說明兩邊數據完全一致——真損壞不會這麼整齊。
- 群暉在自己的 Btrfs 裏給 root item 加了一個私有標誌位
0x40000000(用來標記共享文件夾/快照之類的狀態)。 - 主線的 btrfs
tree-checker只認BTRFS_ROOT_SUBVOL_RDONLY/BTRFS_ROOT_SUBVOL_DEAD兩個合法位,看到0x40000000直接判EUCLEAN,中止掛載。 - 這個校驗大約在內核 5.4 之後變嚴格,所以老內核(如 Ubuntu 18.04 的 4.15)能掛,新內核不能。
於是所有”常規補救”都註定無效:
mount -o ro ... # ❌
mount -o ro,rescue=all ... # ❌
mount -o ro,usebackuproot ... # ❌
mount -t btrfs -o sb=1 ... # ❌
它們都繞不過 tree-checker。
順帶記一個坑:
noload是 ext4 的選項,btrfs 對應的叫norecovery/nologreplay。我看網上教程寫了-o ro,noload,內核直接回btrfs: Unknown parameter 'noload'。
五、四條死路(別試,會掉數據)
| 做法 | 後果 |
|---|---|
btrfs check --repair |
真改元數據。論壇上有人跑完,卷徹底報廢 |
btrfs rescue zero-log / super-recover |
都會寫盤;你的問題不在日誌/超級塊,跑了只會更糟 |
| 在飛牛界面點”創建存儲空間”指向這塊盤 | 等於格式化,921G 當場歸零 |
去掉 -o ro 讀寫掛載 |
群暉的 Btrfs 被其他內核寫過之後,插回群暉大概率報”存儲空間已損毀” |
另外一個隱藏坑:網上很多教程的這行命令是筆誤——
mount /dev/mapper/vg1-volume_1 > /mnt/syno1
> 是 shell 重定向,不是參數。你會得到 bash: /mnt/syno1: Is a directory,而且 mount 根本沒執行。
六、第三步:正解是 btrfs restore
核心認知的轉變:
掛載是內核在做(要過 tree-checker),
btrfs restore是 btrfs-progs 用戶態在做。
後者不做那麼嚴的 flags 校驗,而且只讀——它不會往源盤寫一個字節。
它本來是給”文件系統損壞時搶救數據”用的工具,正好解決這種”內核不認但數據完好”的場景。
關鍵參數
btrfs restore -i -m -S -o /dev/mapper/vg1-volume_1 /vol1/1000/<user>/syno_restore
| 參數 | 作用 | 說明 |
|---|---|---|
-i |
忽略讀錯誤,繼續拷 | 遇到壞塊不中斷,能救多少救多少 |
-m |
恢復屬主/權限/時間 | 不加的話拷出來全是 root、權限錯亂 |
-S |
恢復子卷 | 群暉的共享文件夾都是子卷(@syno 等) |
-o |
目標已存在則覆蓋 | 斷點續跑必須加,重跑不會重複佔空間 |
-x |
恢復 xattr | 不要加! 群暉的 xattr 目標端存不了,會刷屏拖慢 |
-d |
幹跑,只打印不寫 | 先驗證路徑正則很有用 |
後臺跑,別讓 SSH 斷了白乾
400G 要跑幾個小時,必須脫離終端:
cd /vol1/1000/<user>
setsid nohup btrfs restore -i -m -S -o \
/dev/mapper/vg1-volume_1 \
/vol1/1000/<user>/syno_restore \
> /vol1/1000/<user>/restore.log 2>&1 &
# 驗證真的脫離了終端:PPID 應該是 1,TTY 是 ?
ps -o pid,ppid,tty,etime,cmd -C btrfs
三個容易踩的點:
- 一定要
sudo -i或sudo bash -c。普通用戶跑nohup會在你的家目錄寫nohup.out,家目錄不存在就直接失敗退出(而且什麼都沒拷)。 - 顯式寫
> restore.log,別依賴默認的nohup.out。 - 日誌別寫到
/var/log,會撐爆根分區。目標盤上有空間,就寫目標盤。
只想拷某幾個目錄
先幹跑確認匹配,再去掉 -d:
btrfs restore -d -i -m -S -o --path-regex '^/(|@syno(/.*)?)$' \
/dev/mapper/vg1-volume_1 /目標目錄
七、實戰時間線
從 2026-10-29 22:12 開始,到 10-30 05:06 結束,約 6 小時 54 分:
| 時間 | 已寫 | 速度 | 備註 |
|---|---|---|---|
| 22:12 | 0 | — | 啓動 |
| 22:39 | 52G | ~26MB/s | 初期,小文件多 |
| 23:14 | 76G | ~3MB/s | 大量小文件(照片/縮略圖),最慢 |
| 00:38 | 129G | ~20MB/s | 起伏 |
| 01:46 | 175G | ~5MB/s | 又一段小文件 |
| 02:59 | 186G | ~1MB/s | 幾乎停滯(懷疑卡在 @eaDir 縮略圖) |
| 03:42 | 237G | ~35MB/s | 開始出現大文件 |
| 03:59 | 321G | ~84MB/s | 大文件段,速度拉滿 |
| 05:06 | 388G | — | 完成 |
結論:btrfs restore 的速度完全取決於文件大小分佈,不是盤慢。小文件多的時候 1~5MB/s,全是大文件能跑到 80~100MB/s。所以預估時間時要看你的數據構成——全是照片和文檔的話,做好跑一晚上的心理準備。
日誌裏還會大量出現:
offset is 4096
offset is 8192
offset is 65536
Skipping snapshot 65e18cc66456f60b2e20137bfbe740371fc629bfff6d89aeafdd3ecb81de1649
這兩個都不是錯誤:
offset is N是它處理每個 extent 的流水信息;Skipping snapshot ...是-S模式下跳過群暉的快照(快照是舊數據的副本,你只要當前數據)。
真正要擔心的是 ERROR / Couldn't read tree root / can't read superblock。
八、第四步:覈對與權限
1. 對賬
# 源端真實佔用量
btrfs inspect-internal dump-super -f /dev/mapper/vg1-volume_1 \
| awk '/^bytes_used/{print $2; exit}'
# 372.5 GiB 級別 → 399898472448
# 目標端
du -sb /vol1/1000/<user>/syno_restore
df -h /vol1
我這裏:du ≈ 377G,源端 bytes_used ≈ 372.5 GiB,基本一致;df 顯示 389G,多出來的是 Btrfs 自身的元數據開銷,屬於正常。
2. 目錄結構
拷完的目錄長這樣:
syno_restore/
└── @syno/ ← 群暉的"卷根",共享文件夾都在這裏面
├── photo/ music/ video/ docker/ homes/ chat/ ...
├── @docker/ @database/ @appconf/ @appdata/ ... ← DSM 內部數據
└── @eaDir/ ← 縮略圖緩存,可以刪
@syno 是群暉的隱藏卷根,@ 開頭的是 DSM 自己的;真正的用戶數據在 @syno/photo、@syno/music 這些目錄裏。
3. 權限(這一步不做,飛牛界面裏進不去)
btrfs restore -m 會原樣保留群暉的權限,而群暉給 @syno 掛的是 0000:
d--------- 1 root root @syno
飛牛的用戶是 uid=1000,所以必須改:
chown -R 1000:1000 /vol1/1000/<user>/syno_restore
chmod -R u+rwX /vol1/1000/<user>/syno_restore
-R在幾十萬文件上會跑十幾到幾十分鐘,屬正常。改完飛牛的文件管理、SMB 就能正常瀏覽了。
九、踩坑清單
mount失敗先看dmesg,mount 自己的報錯永遠是一句”wrong fs type”,沒信息量。- 兩個 mirror 報同樣的錯 = 大概率不是壞盤,是軟件層面的判定。
noload是 ext4 的,btrfs 用norecovery。- 網上教程裏的
mount ... > /目標,>是筆誤。 btrfs restore的-m和-o都要加,前者保權限,後者保斷點續跑。-x不要加,群暉 xattr 會刷屏。- 必須
sudo,否則nohup連日誌都寫不出來,命令直接失敗。 - 要
setsid,否則 SSH 一斷前功盡棄;用ps確認PPID=1。 - 別用
btrfs check --repair,它是寫操作,能把能救的卷改死。 - 別在飛牛界面把群暉的盤”加入存儲空間”,那是格式化。
- 群暉盤不要讀寫掛載,插回群暉會出問題;只讀 +
btrfs restore纔是無損。 @eaDir是縮略圖,佔空間又不重要,遷移完可以刪。
十、遷移之後:要不要把群暉盤徹底轉成飛牛存儲空間
兩條路,看你急不急:
A. 保守路線(我選的)
- 只讀讀出數據 → 拷到飛牛自己建的存儲空間(另一塊盤)
- 群暉那組盤原樣收起來,將來想回群暉,插回去就能開機
- 代價:多佔一塊盤的容量
B. 徹底轉換
- 先按 A 把數據拷出來並校驗
- 確認無誤後,在飛牛裏把這塊盤格成自己的存儲空間
- 但沒有第二塊盤的話,中間沒有任何冗餘,一旦出錯就是雙份丟失
另外要明白一點:飛牛的”存儲空間”是它自己的 Btrfs + mdadm 佈局,沒法直接”導入”群暉的 LVM/Btrfs 卷。所謂”掛載群暉盤到飛牛存儲空間”,實際只能是”掛載成普通目錄”或”拷進飛牛的空間”,沒有第三種。
十一、命令速查
# ---- 1. 認清結構 ----
cat /proc/mdstat
mdadm --assemble --scan
pvs; vgs; lvs -a -o +segtype,devices
vgchange -ay vg1
ls -l /dev/mapper/
# ---- 2. 確認失敗原因(只讀)----
mount -o ro /dev/mapper/vg1-volume_1 /mnt/x
dmesg | tail -30
blkid /dev/mapper/vg1-volume_1
btrfs inspect-internal dump-super -f /dev/mapper/vg1-volume_1 | head -30
# ---- 3. 只讀搶救 ----
setsid nohup btrfs restore -i -m -S -o \
/dev/mapper/vg1-volume_1 /目標目錄/syno_restore \
> /目標目錄/restore.log 2>&1 &
ps -o pid,ppid,tty,etime,cmd -C btrfs
# ---- 4. 對賬 ----
btrfs inspect-internal dump-super -f /dev/mapper/vg1-volume_1 | awk '/^bytes_used/{print $2; exit}'
du -sb /目標目錄/syno_restore
df -h
# ---- 5. 修權限 ----
chown -R 1000:1000 /目標目錄/syno_restore
chmod -R u+rwX /目標目錄/syno_restore
# ---- 6. 收尾,準備把盤還給群暉 ----
umount /mnt/x 2>/dev/null
vgchange -an vg1
mdadm --stop /dev/md2
poweroff # 再拔盤
寫在最後
整個過程最大的教訓不是”命令怎麼敲”,而是先搞清楚失敗發生在哪一層:
mount報錯 → 看dmesg;dmesg報 tree block corruption → 看是”真損壞”還是”校驗不認”;- 兩個 mirror 完全一致 + 錯誤信息裏出現了具體的 flag 值 → 基本可以判定是軟件判定問題,不是數據問題。
一旦確認數據是好的、只是內核不認,思路就從”修文件系統”轉成”繞開內核把它讀出來”——btrfs restore 就是幹這個的。
最後提醒一句:任何”修復”類命令(--repair、zero-log、super-recover)都是寫操作。在數據還沒拷出來之前,唯一正確的動作是”只讀”。