JBD日誌的定位、分析和恢復

 在上一篇中,咱們介紹了Ext3文件系統的日誌能夠看作一個文件,由JBD進行管理。天然而然引出以下這些問題:html

1)如何定位ext3日誌文件和查看日誌文件的裸數據?node

2)ext3日誌文件數據在物理上是如何佈局的?python

3)JBD如何利用日誌文件進行數據恢復?bash

帶着這些問題,開始此次探索之旅。數據結構

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所在的數據塊ui

# /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爲單位)spa

>>> (504*4096+0x700)/256.
8071.0

讀出這個inode的數據debug

# 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--

相關文章
相關標籤/搜索