JBD日志的定位、分析和恢复
在上一篇中,我们介绍了Ext3文件系统的日志可以看做一个文件,由JBD进行管理。自然而然引出如下这些问题:
1)如何定位ext3日志文件和查看日志文件的裸数据?
2)ext3日志文件数据在物理上是如何布局的?
3)JBD如何利用日志文件进行数据恢复?
带着这些问题,开始这次探索之旅。
JBD日志定位
首先找到ext3 日志文件的inode
/ # /dumpe2fs /dev/sda1
dumpe2fs 1.42 (29-Nov-2011)
Filesystem volume name: shouxian<none>
Last mounted on: <not available>
Filesystem UUID: 513c186a-108b-4840-ad98-aa8e8f62d4c8
Filesystem magic number: 0xEF53
Filesystem revision #: 1 (dynamic)
Filesystem features: has_journal ext_attr resize_inode dir_index filetype sparse_super large_file
Filesystem flags: signed_directory_hash
Default mount options: user_xattr acl
Filesystem state: clean
Errors behavior: Continue
Filesystem OS type: Linux
Inode count: 513072
Block count: 2048276
Reserved block count: 102413
Free blocks: 1978758
Free inodes: 513061
First block: 0
Block size: 4096
Fragment size: 4096
Reserved GDT blocks: 500
Blocks per group: 32768
Fragments per group: 32768
Inodes per group: 8144
Inode blocks per group: 509
Filesystem created: Sat Mar 21 11:39:13 2015
Last mount time: n/a
Last write time: Sat Mar 21 11:39:14 2015
Mount count: 0
Maximum mount count: -1
Last checked: Sat Mar 21 11:39:13 2015
Check interval: 0 (<none>)
Reserved blocks uid: 0 (user root)
Reserved blocks gid: 0 (group root)
First inode: 11
Inode size: 256
Required extra isize: 28
Desired extra isize: 28
Journal inode: 8
Default directory hash: half_md4
Directory Hash Seed: 08091c3c-7a35-4f5b-8477-7af597c5eb50
Journal backup: inode blocks
Journal features: (none)
Journal size: 128M
Journal length: 32768
Journal sequence: 0x00000001
Journal start: 0
然后找到inode所在的数据块
# /debugfs -R 'imap <8>' /dev/sda1
debugfs 1.42 (29-Nov-2011)
Inode 8 is part of block group 0
located at block 504, offset 0x0700
根据块号、块大小和偏移,换算出inode所在数据块的物理偏移(以256B为单位)
>>> (504*4096+0x700)/256.
8071.0
读出这个inode的数据
# dd if=/dev/sda1 of=inode.8 bs=256 count=1 skip=8071
1+0 records in
1+0 records out
256 bytes (256B) copied, 0.000955 seconds, 261.8KB/s /usr/lib # hexdump inode.8
0000000 8081 0000 0000 0008 6258 0d55 6258 0d55
0000010 6258 0d55 0000 0000 0000 0100 0801 0400
0000020 0000 0000 0000 0000 ff01 0f00 0002 0f00
0000030 0102 0f00 0202 0f00 0302 0f00 0402 0f00
0000040 0502 0f00 0602 0f00 0702 0f00 0802 0f00
0000050 0902 0f00 0a02 0f00 0b02 0f00 0c06 0f00
0000060 0000 0000 0000 0000 0000 0000 0000 0000
*
0000080 1c00 0000 0000 0000 0000 0000 0000 0000
0000090 6258 0d55 0000 0000 0000 0000 0000 0000
00000a0 0000 0000 0000 0000 0000 0000 0000 0000
*
0000100
对照inode数据结构,找到其管理的数据块块号(蓝色标示),前12映射槽是直接映射,直接管理的数据块块号从0x0f01ff(983551)到0x0f020b,属于group 30。
Group 30: (Blocks 983040-1015807)
Block bitmap at 983040 (+0), Inode bitmap at 983041 (+1)
Inode table at 983042-983550 (+2)
0 free blocks, 8144 free inodes, 0 directories
Free blocks:
Free inodes: 244321-252464
至此,终于可以dump出日志文件数据块内容。
# dd if=/dev/sda1 of=inode.8.data bs=4096 count=12 skip=983551
1+0 records in
1+0 records out
4096 bytes (4.0KB) copied, 0.000942 seconds, 4.1MB/s 0000000 c03b 3998 0000 0004 0000 0000 0000 1000
0000010 0000 8000 0000 0001 0000 0002 0000 0001
0000020 0000 0000 0000 0000 0000 0000 0000 0000
0000030 513c 186a 108b 4840 ad98 aa8e 8f62 d4c8
0000040 0000 0001 0000 0000 0000 0000 0000 0000
0000050 0000 0000 0000 0000 0000 0000 0000 0000
*
0001000 c03b 3998 0000 0001 0000 0002 0000 01f8
0001010 0000 0008 0000 0000 0000 0000 0000 0000
0001020 0000 0000 0000 0000 0000 0000 0000 0000
*
0002000 0000 0000 0000 0000 6158 0d55 6158 0d55
0002010 6158 0d55 0000 0000 0000 0000 0000 0000
0002020 0000 0000 0000 0000 0000 0000 0000 0000
*
0002100 ed41 0000 0010 0000 d75d 0d55 6158 0d55
0002110 6158 0d55 0000 0000 0000 0300 0800 0000
0002120 0000 0000 0000 0000 f503 0000 0000 0000
0002130 0000 0000 0000 0000 0000 0000 0000 0000
*
0002180 1c00 0000 0000 0000 0000 0000 0000 0000
0002190 6158 0d55 0000 0000 0000 0000 0000 0000
00021a0 0000 0000 0000 0000 0000 0000 0000 0000
*
0002600 8081 0000 00c0 4000 6158 0d55 6158 0d55
0002610 6158 0d55 0000 0000 0000 0100 a88c 0000
0002620 0000 0000 0000 0000 0000 0000 0000 0000
*
0002650 0000 0000 0000 0000 0000 0000 fa03 0000
0002660 0000 0000 0000 0000 0000 0000 0100 0000
0002670 0000 0000 0000 0000 0000 0000 0000 0000
0002680 1c00 0000 0000 0000 0000 0000 0000 0000
0002690 6158 0d55 0000 0000 0000 0000 0000 0000
00026a0 0000 0000 0000 0000 0000 0000 0000 0000
*
0002700 8081 0000 0000 0008 6258 0d55 6258 0d55
0002710 6258 0d55 0000 0000 0000 0100 0801 0400
0002720 0000 0000 0000 0000 ff01 0f00 0002 0f00
0002730 0102 0f00 0202 0f00 0302 0f00 0402 0f00
0002740 0502 0f00 0602 0f00 0702 0f00 0802 0f00
0002750 0902 0f00 0a02 0f00 0b02 0f00 0c06 0f00
0002760 0000 0000 0000 0000 0000 0000 0000 0000
*
0002780 1c00 0000 0000 0000 0000 0000 0000 0000
0002790 6258 0d55 0000 0000 0000 0000 0000 0000
00027a0 0000 0000 0000 0000 0000 0000 0000 0000
*
0002a00 c041 0000 0040 0000 6158 0d55 6158 0d55
0002a10 6158 0d55 0000 0000 0000 0200 2000 0000
0002a20 0000 0000 0000 0000 f603 0000 f703 0000
0002a30 f803 0000 f903 0000 0000 0000 0000 0000
0002a40 0000 0000 0000 0000 0000 0000 0000 0000
*
0002a80 1c00 0000 0000 0000 0000 0000 0000 0000
0002a90 6158 0d55 0000 0000 0000 0000 0000 0000
0002aa0 0000 0000 0000 0000 0000 0000 0000 0000
*
0003000 c03b 3998 0000 0002 0000 0002 0000 0000
0003010 0000 0000 0000 0000 0000 0000 0000 0000
*
以下略
JBD日志分析
0000000 -- 0001000 数据块:
日志超级块段,存储的是journal_superblock_t数据,从中可以看出块类型为JFS_SUPERBLOCK_V2(4),日志块大小: 0x1000; 日志块个数: 0x8000。进而可以计算出日志总大小为:0x1000 * 0x8000 = 128M。
/*
* Standard header for all descriptor blocks:
*/
typedef struct journal_header_s
{
__be32 h_magic;
__be32 h_blocktype;
__be32 h_sequence;
} journal_header_t; /*
* The journal superblock. All fields are in big-endian byte order.
*/
typedef struct journal_superblock_s
{
/* 0x0000 */
journal_header_t s_header; /* 0x000C */
/* Static information describing the journal */
__be32 s_blocksize; /* journal device blocksize */
__be32 s_maxlen; /* total blocks in journal file */
__be32 s_first; /* first block of log information */ /* 0x0018 */
/* Dynamic information describing the current state of the log */
__be32 s_sequence; /* first commit ID expected in log */
__be32 s_start; /* blocknr of start of log */ /* 0x0020 */
/* Error value, as set by journal_abort(). */
__be32 s_errno; /* 0x0024 */
/* Remaining fields are only valid in a version-2 superblock */
__be32 s_feature_compat; /* compatible feature set */
__be32 s_feature_incompat; /* incompatible feature set */
__be32 s_feature_ro_compat; /* readonly-compatible feature set */
/* 0x0030 */
__u8 s_uuid[16]; /* 128-bit uuid for journal */ /* 0x0040 */
__be32 s_nr_users; /* Nr of filesystems sharing log */ __be32 s_dynsuper; /* Blocknr of dynamic superblock copy*/ /* 0x0048 */
__be32 s_max_transaction; /* Limit of journal blocks per trans.*/
__be32 s_max_trans_data; /* Limit of data blocks per trans. */ /* 0x0050 */
__u32 s_padding[44]; /* 0x0100 */
__u8 s_users[16*48]; /* ids of all fs'es sharing the log */
/* 0x0400 */
} journal_superblock_t;
0001000 -- 0002000 数据块:
日志描述符块,开头存储的是journal_header_t,然后紧跟着journal_block_tag_t数据表。从中可以看出块类型为JFS_DESCRIPTOR_BLOCK(1), transaction id为2。只有一个tag,tag的block号为0x1f8, flag为JFS_FLAG_LAST_TAG(8)。此外,第一个tag后面会额外跟着一个16个字节的j_uuid,具有相同uuid的tag以JFS_FLAG_SAME_UUID(2)标示,最后一个tag flag以JFS_FLAG_LAST_TAG置位标示。
/*
* The block tag: used to describe a single buffer in the journal
*/
typedef struct journal_block_tag_s
{
__be32 t_blocknr; /* The on-disk block number */
__be32 t_flags; /* See below */
} journal_block_tag_t;
0002000 -- 0003000 数据块:
元数据块,紧跟着DESCRIPTOR_BLOCK,一个tag对应一个块,块的个数由tag的个数决定。此示例中只有一个tag,所以只有一个数据块。这个块中的数据,在日志恢复或journal load时会写到存储设备文件系统的真实物理块(即tag的t_blocknr)上。
0003000 -- 0004000 数据块:
日志提交块。表示一个完整的transaction。从中可以看出块类型为JFS_COMMIT_BLOCK(2), transaction id为2,与前面的日志描述块匹配。
JBD日志恢复
如果文件系统崩溃或者unclean umount 重启造成文件系统损坏,当再次mount日志文件系统后,ext3文件系统会根据JBD日志文件进行日志恢复动作。
JBD的日志恢复分3个阶段:
第一个阶段PASS_SCAN:
完成日志一致性检查,查找日志尾部,获取start_transaction和end_transaction ID等信息;
第二个阶段PASS_REVOKE:
搜索revoke块(revoke块中存储需要丢弃的块号),统计nr_revokes个数,构造revoke块hash表;避免回放revoke块,破坏数据块内容,造成文件系统损坏;
第三个阶段PASS_REPLAY:
对日志中满足条件的数据块进行回放;拷贝日志中的数据块到文件系统真实位置:
1)块号不在revoke块hash表中;
2)此数据块所在的描述块commit ID大于revoke块的commit ID,即在revoke块后面;
然后设置日志的j_transaction_sequence为下个transaction ID,保证日志中已存在的日志记录都无效;
清空revoke块列表,同步存储设备,至此ext3日志完成恢复。
--EOF--
JBD日志的定位、分析和恢复的更多相关文章
- SpringCloud微服务实战——搭建企业级开发框架(三十八):搭建ELK日志采集与分析系统
一套好的日志分析系统可以详细记录系统的运行情况,方便我们定位分析系统性能瓶颈.查找定位系统问题.上一篇说明了日志的多种业务场景以及日志记录的实现方式,那么日志记录下来,相关人员就需要对日志数据进行 ...
- hadoop实战 -- 网站日志KPI指标分析
本项目分析apache服务器产生的日志,分析pv.独立ip数和跳出率等指标.其实这些指标在第三方系统中都可以检测到,在生产环境中通常用来分析用户交易等核心数据,此处只是用于演示说明日志数据的分析流程. ...
- MYSQL启用日志,查看日志,利用mysqlbinlog工具恢复MySQL数据库【转载】
转自 MYSQL启用日志,查看日志,利用mysqlbinlog工具恢复MySQL数据库 - _安静 - 博客园http://www.cnblogs.com/xionghui/archive/2012/ ...
- LCD 显示异常定位分析方法
第一种情况: 进入kernel或android 后,如果LCM图像示异常,可以通过如下步骤来判断问题出现在哪个层面. step1:通过DMMS截图,来判断上面刷到LCM的数据是否有问题. 若DMMS获 ...
- 【原创】大叔案例分享(4)定位分析--见证scala的强大
一 场景分析 定位分析广泛应用,比如室外基站定位,室内蓝牙beacon定位,室内wifi探针定位等,实现方式是三点定位 Trilateration 理想情况 这种理想情况要求3个基站‘同时’采集‘准确 ...
- vsftp日志xferlog格式分析
vsftp日志xferlog格式分析 [日期:2014-06-25] 来源:Linux社区 作者:Linux [字体:大 中 小] 1.开始vsftp记录日志.修改/etc/vsftpd/vsf ...
- odoo开发笔记 -- 后台日志输出及分析
odoo开发笔记 -- 后台日志输出及分析 附:日志分析软件
- 说说openjdk及G1回收器日志内容详细分析
谈谈openjdk: 在正式往下学习JVM之前,这里谈谈openjdk这个网站,这个在学习java并发时也用过它来分析过锁的底层实现,如:https://www.cnblogs.com/webor20 ...
- Linux下单机部署ELK日志收集、分析环境
一.ELK简介 ELK是elastic 公司旗下三款产品ElasticSearch .Logstash .Kibana的首字母组合,主要用于日志收集.分析与报表展示. ELK Stack包含:Elas ...
随机推荐
- VirtualBox Guest Additions 在CentOS中无法安装的解决方法
安装时出现一步错误查看log为(log文件是 /var/log/vboxadd-install.log): /tmp/vbox.0/Makefile.include.header:94: *** Er ...
- 关于Javascript的使用参考
DOM编程>1.js重要的作用就是可以让用户可以与网页元素进行交互操作-->JS精华之所在 >2.DOM编程也是ajax的基础 >3.DOM(文档对象模型),是HTML与XML ...
- PHP流式上传和表单上传(美图秀秀)
最近需要开发一个头像上传的功能,找了很多都需要授权的,后来找到了美图秀秀,功能非常好用. <?php /** * Note:for octet-stream upload * 这个是流式上传PH ...
- composer的安装以及laravel框架的安装(一)
laravel号称世界上最好的php框架,没有之一,下面介绍它的安装 laravel学习交流qq群:293798134 composer的安装 : php开发者很多,并且在web开发领域占据绝对统治地 ...
- MyEclispe发布web项目-遁地龙卷风
(-1)写在前面 我用的是MyEclipse8.5. 还记得以前帮助一个女同学解决问题的时候,特意情调了要先启动服务在发布项目,其实单独的时候都是知道的,总和起来后就容易片面的给出结论.因为不会发生问 ...
- 在 IIS 6 和 IIS 7中配置Https,设置WCF同时支持HTTP和HTPPS,以及使用HttpWebRequest和HttpClient调用HttpS
IIS 7 ,给IIS添加CA证书以支持https IIS 6 架设证书服务器 及 让IIS启用HTTPS服务 WCF IIS 7中配置HTTPS C#利用HttpWebRequest进行post请求 ...
- JS date常用代码积累
Date.prototype.Format = function(fmt) { var o = { "M+" : this.getMonth()+1, //月份 "d+& ...
- firefox
ctrl l 定位到地址栏 f6 同ctrl l ctrl k 定位到搜索框 shift delete 删除地址 ...
- SQL查询第m条到第n条的方法
SQL查询第m条到第n条的方法 如表名为GOOD Sselect top (n-m) * from GOODS where (某一列名) not in (select top m (某一列名) fro ...
- Nodejs学习笔记(十二)--- 定时任务(node-schedule)
目录 写在之前 Cron风格定时器 通配符解释 范围触发 递归规则定时器 对象文本语法定时器 取消定时器 写在之后 写在之前 在实际开发项目中,会遇到很多定时任务的工作.比如:定时导出某些数据.定时发 ...