Linux存储入门:简易数据恢复方案--分区和LVM实战
摘要: 以黑箱方式完全依赖工具来进行数据恢复,所需时间和恢复的结果都难以估计。以专家或者专业方式进行数据恢复,技能和成本又太高。那么,数据恢复有没有较为合适的简易方案呢?我们以处理过的实际案例作答。
数据恢复有没有简易方案?
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