数据恢复有没有简易方案?
IT工程师一般都知道如何操作和使用文件和目录。但是,对于系统如何构建出、抽象出文件和目录,一般就不熟悉了。至于更下层的概念,可能大家知道最多的就是驱动了。所以,为了规避这点,可行的简易方案之一,就是以黑箱方式使用testdisk等工具,在我们在对底层了解不多甚至一无所知的情况下,进行数据恢复(商业工具,恢复效果估计更好,当然商业工具的价格也更好)。但是,对于工程师而言,多数时候,仅仅以黑箱方式依赖某些工具进行数据恢复是不够的。
数据恢复,经常是突发事故响应中关键而又耗时的一步。多数情况下,工程师往往并非专司数据恢复,操作环境往往是生产环境,趁手工具难以部署,执行操作要遵循种种约束,加之业务中断的压力。这种情形下,工程师很可能还需要推定数据恢复的结果/耗时等信息,提供数据供决策者使用。很明显,如果你准备考验一下自己的细致、耐心、知识和技能,数据恢复将是个不错的课题。当然,有一点是明确的,只是以黑箱方式使用testdisk等工具进行数据恢复,解决以上问题是不可能的。那么,有没有其他简易方案呢?
这里,我们以一个实际的case为例,讨论一下,在只使用UNIX常见工具(dd/grep/strace等)的情况下,如何简单、快捷的恢复数据。
预先准备
工具
我们要用到以下工具
工具 | 功能 | 示例 |
---|---|---|
bc | 计算器 | echo 'ibase=2^3;i=0107000;ibase=2^3+2;i/512' | bc -l |
dd | 检查或者拷贝磁盘/分区的内容,可以是几个扇区,也可以是几个字节 | dd if=/dev/sdb bs=1 count=64 skip=64 2>/dev/null | od -v -tx1 |
grep | 搜索制定字符串 | |
od | 把二进制内容以ASCII或者16进制显示出来,搭配dd使用可以代替二进制编辑器 | dd if=/dev/sdb bs=1 count=64 skip=64 2>/dev/null | od -v -tx1 |
strace | 追踪应用的执行路径和对数据处理的流程 | strace ps |
排查和诊断就是数据处理
如果对数据处理了解不多,请参考OSEMN
- obtaining data/获取数据
- crubbing data/清洗数据
- exploring data/探索数据
- modeling data/建模数据
- interpreting data/解释数据
测试环境
使用Virtualbox,基于CentOS/Fedora/debian/Ubuntu搭建Linux实验环境。只需要学会strace工具和如下系统调用,就足以追踪系统如何处理诸如LVM物理卷元数据这样过的问题。
name | 功能 |
---|---|
open | 打开一个文件,返回一个文件描述符供后续读写操作使用 |
dup/dup2 | 复制文件描述符 |
lseek | 将读写指针移动到指定位置,后续操作从此指定位置读写 |
close | 关闭文件描述符 |
read | 读操作 |
数据恢复的原理和流程
什么是元数据?
我们以大家都熟悉的磁盘作为存储设备的例子。
现代操作系统都会在磁盘上建立多个分层结构来管理和控制磁盘。比如,磁盘分区,分区上建立物理卷,物理卷上建立卷组,卷组上建立逻辑卷,逻辑卷上建立文件系统,就是这样的一个例子。如果你不熟悉LVM,请参考Logical Volume Manager。
这些分层的结构都是很类似的。以磁盘分区为例。所谓分区,以大家最为熟悉的MBR: Master Boot Record结构为例。其实是第一个扇区记录了各个分区的起始扇区,大小和类型。系统需要时,比如启动过程中,系统只要从磁盘的第一个扇区读取这些数据即能拿到各个分区的数据。
具体看看分区的数据结构。以fdisk为例,分区的数据结构定义为
struct dos_partition {
unsigned char boot_ind; /* 0x80 - active */
unsigned char bh, bs, bc; /* begin CHS */
unsigned char sys_ind;
unsigned char eh, es, ec; /* end CHS */
unsigned char start_sect[4]; /* 分区开始扇区 */
unsigned char nr_sects[4]; /* 分区包含的扇区数量 */
} __attribute__((packed));
我们看看具体分区的例子,验证一下数据结构
分区使得磁盘上的扇区有了差别。第一个扇区(其实其编号是0),因分区数据记录其上而扮演特殊角色。明显,对系统而言,管理和操作分区实际上就是读写第一扇区上的对应记录而已。
类似分区信息这种系统用以管理某层资源的数据就是元数据。
系统在磁盘上建立的各个分层结构,都有类似分区结构的数据结构。以LVM结构为例,我们可以把磁盘记录和LVM工具报告的数据做一对比。LVM数据的从第2个扇区开始,卷组数据在第8个扇区中,可以用dd命令提取相关扇区来验证LVM的数据结构。
下面是一份完整的LVM元数据信息,有兴趣者可以逐一清点各个对象。
[root@pusf ~]# pvdisplay;vgdisplay;lvdisplay
--- Physical volume ---
PV Name /dev/sda2
VG Name cl
PV Size 39.00 GiB / not usable 3.00 MiB
Allocatable yes
PE Size 4.00 MiB
Total PE 9983
Free PE 1
Allocated PE 9982
PV UUID TIcs1T-Jksu-HKrn-fKqK-QF4K-ao1S-3PVBGI
--- Volume group ---
VG Name cl
System ID
Format lvm2
Metadata Areas 1
Metadata Sequence No 5
VG Access read/write
VG Status resizable
MAX LV 0
Cur LV 2
Open LV 2
Max PV 0
Cur PV 1
Act PV 1
VG Size 39.00 GiB
PE Size 4.00 MiB
Total PE 9983
Alloc PE / Size 9982 / 38.99 GiB
Free PE / Size 1 / 4.00 MiB
VG UUID KTVFwl-2QRE-ehf3-3dJb-bIfG-bpn0-8FnH7l
--- Logical volume ---
LV Path /dev/cl/swap
LV Name swap
VG Name cl
LV UUID Kc14dR-vFda-qdWJ-zUYo-lsDl-4oKq-RjjrBb
LV Write Access read/write
LV Creation host, time localhost, 2017-04-18 13:23:08 +0800
LV Status available
# open 2
LV Size 2.00 GiB
Current LE 512
Segments 1
Allocation inherit
Read ahead sectors auto
- currently set to 8192
Block device 253:1
--- Logical volume ---
LV Path /dev/cl/root
LV Name root
VG Name cl
LV UUID PWaI2g-t2Kq-h3aa-HgMC-cBp1-FjBp-dmaGeR
LV Write Access read/write
LV Creation host, time localhost, 2017-04-18 13:23:09 +0800
LV Status available
# open 1
LV Size 36.99 GiB
Current LE 9470
Segments 1
Allocation inherit
Read ahead sectors auto
- currently set to 8192
Block device 253:0
[root@pusf ~]# dd if=/dev/sda2 bs=512 count=16 skip=1 2>/dev/null | od -tc
0000000 L A B E L O N E 001 \0 \0 \0 \0 \0 \0 \0
0000020 270 177 251 K \0 \0 \0 L V M 2 0 0 1
0000040 T I c s 1 T J k s u H K r n f K
0000060 q K Q F 4 K a o 1 S 3 P V B G I
0000100 \0 \0 360 277 \t \0 \0 \0 \0 \0 020 \0 \0 \0 \0 \0
0000120 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
0000140 \0 \0 \0 \0 \0 \0 \0 \0 \0 020 \0 \0 \0 \0 \0 \0
0000160 \0 360 017 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
0000200 \0 \0 \0 \0 \0 \0 \0 \0 002 \0 \0 \0 001 \0 \0 \0
0000220 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
*
0007000 252 314 344 w L V M 2 x [ 5 A % r
0007020 0 N * > 001 \0 \0 \0 \0 020 \0 \0 \0 \0 \0 \0
0007040 \0 360 017 \0 \0 \0 \0 \0 \0 026 \0 \0 \0 \0 \0 \0
0007060 027 005 \0 \0 \0 \0 \0 \0 026 G 205 374 \0 \0 \0 \0
0007100 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
*
0010000 c l { \n i d = " K T V F w
0010020 l - 2 Q R E - e h f 3 - 3 d J b
0010040 - b I f G - b p n 0 - 8 F n H 7
0010060 l " \n s e q n o = 1 \n f o r
0010100 m a t = " l v m 2 " \n s t a
0010120 t u s = [ " R E S I Z E A B
0010140 L E " , " R E A D " , " W R
0010160 I T E " ] \n f l a g s = [ ]
0010200 \n e x t e n t _ s i z e = 8
0010220 1 9 2 \n m a x _ l v = 0 \n m
0010240 a x _ p v = 0 \n m e t a d a
0010260 t a _ c o p i e s = 0 \n \n p
0010300 h y s i c a l _ v o l u m e s
0010320 { \n \n p v 0 { \n i d = " T
0010340 I c s 1 T - J k s u - H K r n -
0010360 f K q K - Q F 4 K - a o 1 S - 3
0010400 P V B G I " \n d e v i c e =
0010420 " / d e v / s d a 2 " \n \n s t a
0010440 t u s = [ " A L L O C A T A
0010460 B L E " ] \n f l a g s = [ ]
0010500 \n d e v _ s i z e = 8 1 7 8
0010520 6 8 8 0 \n p e _ s t a r t =
0010540 2 0 4 8 \n p e _ c o u n t =
0010560 9 9 8 3 \n } \n } \n \n \n } \n # G
0010600 e n e r a t e d b y L V M 2
0010620 v e r s i o n 2 . 0 2 . 1 6
0010640 6 ( 2 ) - R H E L 7 ( 2 0 1 6
0010660 - 0 9 - 2 8 ) : T u e A p r
0010700 1 8 0 5 : 2 3 : 0 8 2 0 1
0010720 7 \n \n c o n t e n t s = " T
0010740 e x t F o r m a t V o l u m
0010760 e G r o u p " \n v e r s i o n
0011000 = 1 \n \n d e s c r i p t i o
0011020 n = " " \n \n c r e a t i o n
0011040 _ h o s t = " l o c a l h o
0011060 s t " \t # L i n u x l o c a
0011100 l h o s t 3 . 1 0 . 0 - 5 1 4
0011120 . e l 7 . x 8 6 _ 6 4 # 1 S
0011140 M P T u e N o v 2 2 1 6
0011160 : 4 2 : 4 1 U T C 2 0 1 6
0011200 x 8 6 _ 6 4 \n c r e a t i o n _
0011220 t i m e = 1 4 9 2 4 9 2 9 8
0011240 8 \t # T u e A p r 1 8 0
0011260 5 : 2 3 : 0 8 2 0 1 7 \n \n \0 \0
0011300 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
*
0012000 c l { \n i d = " K T V F w
0012020 l - 2 Q R E - e h f 3 - 3 d J b
0012040 - b I f G - b p n 0 - 8 F n H 7
0012060 l " \n s e q n o = 2 \n f o r
0012100 m a t = " l v m 2 " \n s t a
0012120 t u s = [ " R E S I Z E A B
0012140 L E " , " R E A D " , " W R
0012160 I T E " ] \n f l a g s = [ ]
0012200 \n e x t e n t _ s i z e = 8
0012220 1 9 2 \n m a x _ l v = 0 \n m
0012240 a x _ p v = 0 \n m e t a d a
0012260 t a _ c o p i e s = 0 \n \n p
0012300 h y s i c a l _ v o l u m e s
0012320 { \n \n p v 0 { \n i d = " T
0012340 I c s 1 T - J k s u - H K r n -
0012360 f K q K - Q F 4 K - a o 1 S - 3
0012400 P V B G I " \n d e v i c e =
0012420 " / d e v / s d a 2 " \n \n s t a
0012440 t u s = [ " A L L O C A T A
0012460 B L E " ] \n f l a g s = [ ]
0012500 \n d e v _ s i z e = 8 1 7 8
0012520 6 8 8 0 \n p e _ s t a r t =
0012540 2 0 4 8 \n p e _ c o u n t =
0012560 9 9 8 3 \n } \n } \n \n l o g i c a
0012600 l _ v o l u m e s { \n \n s w a
0012620 p { \n i d = " K c 1 4 d R
0012640 - v F d a - q d W J - z U Y o -
0012660 l s D l - 4 o K q - R j j r B b
0012700 " \n s t a t u s = [ " R E A
0012720 D " , " W R I T E " , " V I
0012740 S I B L E " ] \n f l a g s =
0012760 [ ] \n c r e a t i o n _ t i m e
0013000 = 1 4 9 2 4 9 2 9 8 8 \n c r
0013020 e a t i o n _ h o s t = " l
0013040 o c a l h o s t " \n s e g m e n
0013060 t _ c o u n t = 1 \n \n s e g
0013100 m e n t 1 { \n s t a r t _ e x
0013120 t e n t = 0 \n e x t e n t _
0013140 c o u n t = 5 1 2 \n \n t y p
0013160 e = " s t r i p e d " \n s t
0013200 r i p e _ c o u n t = 1 \n \n
0013220 s t r i p e s = [ \n " p v 0
0013240 " , 0 \n ] \n } \n } \n } \n \n } \n
0013260 # G e n e r a t e d b y L
0013300 V M 2 v e r s i o n 2 . 0 2
0013320 . 1 6 6 ( 2 ) - R H E L 7 ( 2
0013340 0 1 6 - 0 9 - 2 8 ) : T u e
0013360 A p r 1 8 0 5 : 2 3 : 0 8
0013400 2 0 1 7 \n \n c o n t e n t s =
0013420 " T e x t F o r m a t V o
0013440 l u m e G r o u p " \n v e r s
0013460 i o n = 1 \n \n d e s c r i p
0013500 t i o n = " " \n \n c r e a t
0013520 i o n _ h o s t = " l o c a
0013540 l h o s t " \t # L i n u x l
0013560 o c a l h o s t 3 . 1 0 . 0 -
0013600 5 1 4 . e l 7 . x 8 6 _ 6 4 #
0013620 1 S M P T u e N o v 2 2
0013640 1 6 : 4 2 : 4 1 U T C 2 0
0013660 1 6 x 8 6 _ 6 4 \n c r e a t i
0013700 o n _ t i m e = 1 4 9 2 4 9
0013720 2 9 8 8 \t # T u e A p r 1
0013740 8 0 5 : 2 3 : 0 8 2 0 1 7 \n
0013760 \n \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
0014000 c l { \n i d = " K T V F w
0014020 l - 2 Q R E - e h f 3 - 3 d J b
0014040 - b I f G - b p n 0 - 8 F n H 7
0014060 l " \n s e q n o = 3 \n f o r
0014100 m a t = " l v m 2 " \n s t a
0014120 t u s = [ " R E S I Z E A B
0014140 L E " , " R E A D " , " W R
0014160 I T E " ] \n f l a g s = [ ]
0014200 \n e x t e n t _ s i z e = 8
0014220 1 9 2 \n m a x _ l v = 0 \n m
0014240 a x _ p v = 0 \n m e t a d a
0014260 t a _ c o p i e s = 0 \n \n p
0014300 h y s i c a l _ v o l u m e s
0014320 { \n \n p v 0 { \n i d = " T
0014340 I c s 1 T - J k s u - H K r n -
0014360 f K q K - Q F 4 K - a o 1 S - 3
0014400 P V B G I " \n d e v i c e =
0014420 " / d e v / s d a 2 " \n \n s t a
0014440 t u s = [ " A L L O C A T A
0014460 B L E " ] \n f l a g s = [ ]
0014500 \n d e v _ s i z e = 8 1 7 8
0014520 6 8 8 0 \n p e _ s t a r t =
0014540 2 0 4 8 \n p e _ c o u n t =
0014560 9 9 8 3 \n } \n } \n \n l o g i c a
0014600 l _ v o l u m e s { \n \n s w a
0014620 p { \n i d = " K c 1 4 d R
0014640 - v F d a - q d W J - z U Y o -
0014660 l s D l - 4 o K q - R j j r B b
0014700 " \n s t a t u s = [ " R E A
0014720 D " , " W R I T E " , " V I
0014740 S I B L E " ] \n f l a g s =
0014760 [ ] \n c r e a t i o n _ t i m e
0015000 = 1 4 9 2 4 9 2 9 8 8 \n c r
0015020 e a t i o n _ h o s t = " l
0015040 o c a l h o s t " \n s e g m e n
0015060 t _ c o u n t = 1 \n \n s e g
0015100 m e n t 1 { \n s t a r t _ e x
0015120 t e n t = 0 \n e x t e n t _
0015140 c o u n t = 5 1 2 \n \n t y p
0015160 e = " s t r i p e d " \n s t
0015200 r i p e _ c o u n t = 1 \n \n
0015220 s t r i p e s = [ \n " p v 0
0015240 " , 0 \n ] \n } \n } \n \n r o o t
0015260 { \n i d = " P W a I 2 g -
0015300 t 2 K q - h 3 a a - H g M C - c
0015320 B p 1 - F j B p - d m a G e R "
0015340 \n s t a t u s = [ " R E A D
0015360 " , " W R I T E " , " V I S
0015400 I B L E " ] \n f l a g s = [
0015420 ] \n c r e a t i o n _ t i m e
0015440 = 1 4 9 2 4 9 2 9 8 9 \n c r e
0015460 a t i o n _ h o s t = " l o
0015500 c a l h o s t " \n s e g m e n t
0015520 _ c o u n t = 1 \n \n s e g m
0015540 e n t 1 { \n s t a r t _ e x t
0015560 e n t = 0 \n e x t e n t _ c
0015600 o u n t = 9 4 7 0 \n \n t y p
0015620 e = " s t r i p e d " \n s t
0015640 r i p e _ c o u n t = 1 \n \n
0015660 s t r i p e s = [ \n " p v 0
0015700 " , 5 1 2 \n ] \n } \n } \n } \n \n
0015720 } \n # G e n e r a t e d b y
0015740 L V M 2 v e r s i o n 2 .
0015760 0 2 . 1 6 6 ( 2 ) - R H E L 7
0016000 ( 2 0 1 6 - 0 9 - 2 8 ) : T u
0016020 e A p r 1 8 0 5 : 2 3 : 0
0016040 9 2 0 1 7 \n \n c o n t e n t s
0016060 = " T e x t F o r m a t
0016100 V o l u m e G r o u p " \n v e
0016120 r s i o n = 1 \n \n d e s c r
0016140 i p t i o n = " " \n \n c r e
0016160 a t i o n _ h o s t = " l o
0016200 c a l h o s t " \t # L i n u x
0016220 l o c a l h o s t 3 . 1 0 .
0016240 0 - 5 1 4 . e l 7 . x 8 6 _ 6 4
0016260 # 1 S M P T u e N o v
0016300 2 2 1 6 : 4 2 : 4 1 U T C
0016320 2 0 1 6 x 8 6 _ 6 4 \n c r e a
0016340 t i o n _ t i m e = 1 4 9 2
0016360 4 9 2 9 8 9 \t # T u e A p r
0016400 1 8 0 5 : 2 3 : 0 9 2 0 1
0016420 7 \n \n \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
0016440 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
*
0017000 c l { \n i d = " K T V F w
0017020 l - 2 Q R E - e h f 3 - 3 d J b
0017040 - b I f G - b p n 0 - 8 F n H 7
0017060 l " \n s e q n o = 4 \n f o r
0017100 m a t = " l v m 2 " \n s t a
0017120 t u s = [ " R E S I Z E A B
0017140 L E " , " R E A D " , " W R
0017160 I T E " ] \n f l a g s = [ ]
0017200 \n e x t e n t _ s i z e = 8
0017220 1 9 2 \n m a x _ l v = 0 \n m
0017240 a x _ p v = 0 \n m e t a d a
0017260 t a _ c o p i e s = 0 \n \n p
0017300 h y s i c a l _ v o l u m e s
0017320 { \n \n p v 0 { \n i d = " T
0017340 I c s 1 T - J k s u - H K r n -
0017360 f K q K - Q F 4 K - a o 1 S - 3
0017400 P V B G I " \n d e v i c e =
0017420 " / d e v / s d a 2 " \n \n s t a
0017440 t u s = [ " A L L O C A T A
0017460 B L E " ] \n f l a g s = [ ]
0017500 \n d e v _ s i z e = 8 1 7 8
0017520 6 8 8 0 \n p e _ s t a r t =
0017540 2 0 4 8 \n p e _ c o u n t =
0017560 9 9 8 3 \n } \n \n p v 1 { \n i d
0017600 = " R c p o 7 Z - Q I e g -
0017620 F G V D - T S Z I - s o Q O - I
0017640 g a T - r Q w D 4 Y " \n d e v i
0017660 c e = " / d e v / s d b 6 "
0017700 \n \n s t a t u s = [ " A L L
0017720 O C A T A B L E " ] \n f l a g s
0017740 = [ ] \n d e v _ s i z e =
0017760 4 0 9 6 0 1 \n p e _ s t a r t
0020000
[root@pusf ~]#
元数据和数据:数据损坏分类
系统把磁盘的扇区分成两种来支持分区:第一扇区和所有其他非第一扇区。并且在第一个扇区上记录分区信息,即元数据。而其他非第一扇区则供分区层使用,从磁盘的视角看,其他非第一扇区则是数据部分。我们逐层考察下磁盘、分区和LVM结构
系统启动时,会逐层读取各层元数据,创建各层数据结构。如果某一层元数据损坏或者丢失,那么系统就没有办法完成创建各层数据结构的任务。这种情况下,从客户角度看,很可能就是数据损坏了。比如,如果你把第一个扇区用\0覆盖一遍,那么系统就识别不到分区内容了。当然这种情况下分区层以上的内容,比如物理卷信息,系统也无法处理了。因此,对于数据恢复任务而言,如果元数据损坏,则修复元数据总是必须的,而且往往是第一步。
当然,如果数据损坏了,即使元数据完好无缺,那么数据也是损坏了。比如,你误删了一个文件,那么,分区结构再完好对于文件被删也于事无补。
基于以上分析,我们可以把数据损坏简单分三类类:元数据损坏、数据损坏或者前面两种损坏类型的混合型损坏。
元数据修复可以简易处理
以基于磁盘的分区、LVM以及文件系统为例。分层结构的数据格式都有严格的格式(比如分区的数据结构就是一个C的struct),出现位置也固定(有关分区的元数据记录在第一个扇区的446~462字节之间),而且这些数据结构往往都带有魔数(比如,分区的类型83),而且常用的分层结构,也不外乎分区、LVM以及文件系统等几种。因此,对于元数据以及系统如何处理元数据,我们都容易追踪和检查。因此,可以预期,修复元数据,有简易方案。
原理
如果有数据损坏,那么除非有日志、备份,或者数据本身有逻辑可供使用,否则数据是不能恢复了。比如,通常的文件删除操作,系统只是解除了文件名称和文件内容相关间的联系而已。文件本身的内容还是记录再磁盘上。这种情况下,只要重建文件名称和文件内容间的联系即可恢复文件。
相对而言,简单情形的是元数据损坏。如果只是元数据损坏,而且我们知道正确的元数据。因为元数据操作,不会触及数据部分,因此,我们只要重建元数据部分即可恢复数据。如果涉及到多层,则逐层恢复即可。以分区丢失为例。
比如我们有一块数据盘,整盘我们只是用fdisk分了一个区,现在分区丢失了。这种情形下,只要用fdsik,按照默认情形,重新分区就能恢复分区。
就这种情形,我们给出一个可用的分析流程。
症状和初步排查
症状
客户反馈
降配重启后,系统无法启动
排查发现客户一逻辑卷无法挂载导致重启失败。在/etc/fstab中注释掉逻辑卷的挂载配置,系统启动成功。
但是客户的逻辑卷上有重要数据。此逻辑卷在数据盘上,数据盘大小是2TB。此磁盘全部2TB全部分配给一个分区,此分区上创建有LVM结构。
分区数据如下
[root@localhost ~]# fdisk -l -u /dev/vdb
Disk /dev/vdb: 2199.0 GB, 2199023255552 bytes
5 heads, 3 sectors/track, 286331153 cylinders, total 4294967296 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0xde220917
Device Boot Start End Blocks Id System
/dev/vdb1 2048 4294967294 2147482623+ 83 Linux
[root@localhost ~]#
初步排查
首先确定分区上是否有数据,通过查看一些扇区,我们就会有很大的概率确认这一点。当然也可以逐扇区确认。
逐扇区确认,可以用如下命令办理。假设磁盘是/dev/vdb。
max_sector_to_check=nrsector
for i in $(seq 0 ${max_sector_to_check});do dd if=/dev/vdb bs=512 count=1 skip=${i} 2>/dev/null | sha256sum;done | sort -n | uniq;
当然,也可以通过抽样检查来确认。这种方法通常是检查磁盘分区的前面一部分扇区。比如,下面的例子,通过检查前面几十个扇区,我们可以确认磁盘上确有数据。
[root@localhost ~]# dd if=/dev/vdb1 bs=512 count=60 2>/dev/null | od -tx1
0000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
0007000 f9 80 00 00 f9 80 01 00 f9 80 02 00 f9 80 03 00
0007020 f9 80 04 00 f9 80 0c 00 f9 80 0d 00 f9 80 18 00
0007040 f9 80 28 00 f9 80 3e 00 f9 80 79 00 f9 80 ab 00
0007060 f9 80 38 01 f9 80 6c 01 f9 80 45 04 f9 80 b0 04
0007100 f9 80 1a 06 f9 80 d0 0c f9 80 84 1e 00 00 00 00
0007120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
0017000 fa 80 00 00 fa 80 01 00 fa 80 02 00 fa 80 03 00
0017020 fa 80 04 00 fa 80 0c 00 fa 80 0d 00 fa 80 18 00
0017040 fa 80 28 00 fa 80 3e 00 fa 80 79 00 fa 80 ab 00
0017060 fa 80 38 01 fa 80 6c 01 fa 80 45 04 fa 80 b0 04
0017100 fa 80 1a 06 fa 80 d0 0c fa 80 84 1e 00 00 00 00
0017120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
0027000 fb 80 00 00 fb 80 01 00 fb 80 02 00 fb 80 03 00
0027020 fb 80 04 00 fb 80 0c 00 fb 80 0d 00 fb 80 18 00
0027040 fb 80 28 00 fb 80 3e 00 fb 80 79 00 fb 80 ab 00
0027060 fb 80 38 01 fb 80 6c 01 fb 80 45 04 fb 80 b0 04
0027100 fb 80 1a 06 fb 80 d0 0c fb 80 84 1e 00 00 00 00
0027120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
0037000 fc 80 00 00 fc 80 01 00 fc 80 02 00 fc 80 03 00
0037020 fc 80 04 00 fc 80 0c 00 fc 80 0d 00 fc 80 18 00
0037040 fc 80 28 00 fc 80 3e 00 fc 80 79 00 fc 80 ab 00
0037060 fc 80 38 01 fc 80 6c 01 fc 80 45 04 fc 80 b0 04
0037100 fc 80 1a 06 fc 80 d0 0c fc 80 84 1e 00 00 00 00
0037120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
0047000 fd 80 00 00 fd 80 01 00 fd 80 02 00 fd 80 03 00
0047020 fd 80 04 00 fd 80 0c 00 fd 80 0d 00 fd 80 18 00
0047040 fd 80 28 00 fd 80 3e 00 fd 80 79 00 fd 80 ab 00
0047060 fd 80 38 01 fd 80 6c 01 fd 80 45 04 fd 80 b0 04
0047100 fd 80 1a 06 fd 80 d0 0c fd 80 84 1e 00 00 00 00
0047120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
0057000 fe 80 00 00 fe 80 01 00 fe 80 02 00 fe 80 03 00
0057020 fe 80 04 00 fe 80 0c 00 fe 80 0d 00 fe 80 18 00
0057040 fe 80 28 00 fe 80 3e 00 fe 80 79 00 fe 80 ab 00
0057060 fe 80 38 01 fe 80 6c 01 fe 80 45 04 fe 80 b0 04
0057100 fe 80 1a 06 fe 80 d0 0c fe 80 84 1e 00 00 00 00
0057120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
0067000 ff 80 00 00 ff 80 01 00 ff 80 02 00 ff 80 03 00
0067020 ff 80 04 00 ff 80 0c 00 ff 80 0d 00 ff 80 18 00
0067040 ff 80 28 00 ff 80 3e 00 ff 80 79 00 ff 80 ab 00
0067060 ff 80 38 01 ff 80 6c 01 ff 80 45 04 ff 80 b0 04
0067100 ff 80 1a 06 ff 80 d0 0c ff 80 84 1e 00 00 00 00
0067120 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
*
0074000
[root@localhost ~]#
接下来使用testdisk工具恢复数据。尝试数次,testdisk工具总是在扫描到2%时停滞,处理过程不能继续。
初次恢复尝试
分区还在,但是LVM结构丢失,经检查,由LVM工具链维护的备份数据/etc/lvm/backup/vg_xxxxxx文件还在。因此,这种情形下,按照我们的恢复流程,只要在分区之上,尝试重建LVM和文件系统,应该就可以解决问题。
[root@localhost ~]# pvdisplay /dev/vdb
Failed to find device for physical volume "/dev/vdb".
[root@localhost ~]# ls /etc/lvm/backup/vg_xxxxxx
/etc/lvm/backup/vg_xxxxxx
[root@localhost ~]#
根据备份数据恢复LVM结构,可以参考Recovering Physical Volume Metadata。可惜的是,我们第一步就折戟沉沙了。
[root@localhost ~]# pvcreate --uuid "X1cHlO-kdFk-RZIM-1L12-qHit-0QA5-C1fZxm" --restorefile /etc/lvm/backup/vg_xxxxxx /dev/vdb1
WARNING: Device /dev/vdb1 has size of 4294965247 sectors which is smaller than corresponding PV size of 4294966977 sectors. Was device resized?
One or more devices used as PVs in VG vg_xxxxxx have changed sizes.
Can't initialize physical volume "/dev/vdb1" of volume group "vg_xxxxxx" without -ff
[root@localhost ~]#
看样子,分区的数据有些地方出错了。根据上面命令报错的信息,对比LVM的备份数据和分区数据,很快我们就发现了问题。现有分区记录的其拥有的扇区数目,少于其上LVM卷组记录的扇区数量。
问题出在哪里?
因为种种原因,我们不能确认分区信息和LVM备份数据为何不一致。但是,我们可以进一步从磁盘上提取、分析数据。因为有关分区的元数据在(分区在),所以我们进一步检查磁盘上还有没有有关LVM的元数据?这只要使用下面的命令行
dd if=/dev/vdb1 bs=512 count=128 2>/dev/null | od -tc
结果及其结果分析如下
所以,磁盘上还有有关LVM的元数据,但是为什么系统没有凭借这些数据构建出LVM结构呢?我们创建一个测试环境,用strace追踪下系统处理LVM物理卷元数据的执行路径。如下命令即可
strace -s 512 pvdisplay
当然,更好的办法是把strace记录放置到文件中,以备仔细检查
starce -o path_to_strace_log -s 512 -f -ff pvdisplay
我们组合使用strace和grep命令来确认系统默认的LVM物理卷位置。如果你没有耐心分析下面的数据,请跳过直接看后面的截图
strace -o /tmp/pvdisplay-strace.log -s 512 pvdisplay
grep -E '^open|^lseek|^read' /tmp/pvdisplay-strace.log
数据清洗结果如下。如果没有耐心分析,请跳过直接看下面的分析截图
[root@localhost ~]# grep -E '^open|^lseek|^read|^close' /tmp/pvdisplay-strace.log
…
close(4) = 0
open("/dev/vdb1", O_RDONLY|O_DIRECT|O_NOATIME) = 4
close(4) = 0
open("/dev/vdb1", O_RDONLY|O_DIRECT|O_NOATIME) = 4
lseek(4, 21037056, SEEK_SET) = 21037056
read(4, "…", 512) = 512
lseek(4, 21118976, SEEK_SET) = 21118976
read(4, "…", 512) = 512
lseek(4, 0, SEEK_SET) = 0
read(4, "…", 512) = 512
lseek(4, 4096, SEEK_SET) = 4096
read(4, "\26\326\216\333 LVM2 x[5A%r0N*>\1\0\0…", 512) = 512
close(4) = 0
…
[root@localhost ~]#
很明显,系统预期LVM元数据是在分区的第8个扇区,但是在需要做数据恢复的磁盘上,LVM的元数据却是在第71个扇区,而分区的起始扇区是2048,因此,LVM数据根本不在分区内。这就是为什么磁盘上还有LVM元数据,系统却没有识别出来LVM的原因。
既然系统是因为有关LVM的元数据所在扇区不对而导致系统无法识别LVM结构,设想通过重新分区,我们把有关LVM元数据调整到分区的第8个扇区。稍加计算,就会发现,只要把分区的起始扇区从第2048个扇区调整到第63个扇区即可。不仅如此,通过调整分区大小,我们同样也解决了磁盘分区扇区数不足的问题
数据恢复
较新的fdisk工具,不允许起始扇区小于2048,因此,我们用parted工具来调整分区的起始扇区。
调整过程是先删掉扇区,而后再创建之。而结果正如我们所预期的,分区调整完成,客户的数据立刻恢复了。物理卷、卷组、逻辑卷、文件系统以及数据,都完好无损。
结语
从处理这个实际case可以看出,如果知道如何识别各层元数据,比如分区,LVM和文件系统;能够追踪系统处理各层元数据的逻辑,那么,组合使用UNIX常用的dd、od等工具,足以简单有效的处理元数据损坏的情形,快速恢复数据。如果掌握了常见的系统调用,并且掌握了strace工具,那么对于如何识别元数据以及系统如何处理元数据,完全可以通过简单分析strace输出拿到相应答案。
除了易学、简单、快捷、高效,元数据修复方案还有一个优点,就是可以确保不会破坏数据。这可能是这个方案的最大亮点。
参考
本文提供链接,优先链接内容的严谨与可靠性,而非便利性。如链接无法访问,请按照文字自行检索资料和图书。