目录是文件的一种形式,也是最常见的一种形式。我们知道,对于普通文件来讲,文件的内容就是我们输入的内容。但是目录文件,他的数据block里面存的是什么呢?
我新建的ext2文件系统和以前一样,分出512M的空间,挂载到/mnt/bean下面。
- [root@localhost bean]# cd code/
- [root@localhost code]# ll -i
- total 32
- 65538 drwxr-xr-x 2 root root 4096 Aug 11 14:06 C
- 65540 drwxr-xr-x 2 root root 4096 Aug 11 14:06 php
- 65541 drwxr-xr-x 3 root root 4096 Aug 11 14:17 raw
- 65539 drwxr-xr-x 2 root root 4096 Aug 11 14:06 shell
- [root@localhost code]# cd raw/
- [root@localhost raw]# ll
- total 32
- -rw-r--r-- 1 root root 12 Aug 11 14:07 hello
- drwxr-xr-x 2 root root 4096 Aug 11 14:17 sub_dir
- -rw-r--r-- 1 root root 13 Aug 11 14:07 test
- -rw-r--r-- 1 root root 21 Aug 11 14:14 test_another
- [root@localhost raw]# ll -ai
- total 48
- 65541 drwxr-xr-x 3 root root 4096 Aug 11 14:17 .
- 65537 drwxr-xr-x 6 root root 4096 Aug 11 14:06 ..
- 65542 -rw-r--r-- 1 root root 12 Aug 11 14:07 hello
- 65545 drwxr-xr-x 2 root root 4096 Aug 11 14:17 sub_dir
- 65543 -rw-r--r-- 1 root root 13 Aug 11 14:07 test
- 65544 -rw-r--r-- 1 root root 21 Aug 11 14:14 test_another
- [root@localhost raw]# cat hello
- hello world
- [root@localhost raw]# cat test
- this is test
- [root@localhost raw]# cat test_another
- this is another test
任何一个靠谱的ext2的教程都会有下面这个结构体。这个结构体讲述的目录下的一个条目在磁盘上的存储形式。
- struct ext2_dir_entry_2 {
- __le32 inode; /* Inode number */
- __le16 rec_len; /* Directory entry length */
- __u8 name_len; /* Name length */
- __u8 file_type;
- char name[]; /* File name, up to EXT2_NAME_LEN */
- };
- enum {
- EXT2_FT_UNKNOWN = 0,
- EXT2_FT_REG_FILE = 1,
- EXT2_FT_DIR = 2,
- EXT2_FT_CHRDEV = 3,
- EXT2_FT_BLKDEV = 4,
- EXT2_FT_FIFO = 5,
- EXT2_FT_SOCK = 6,
- EXT2_FT_SYMLINK = 7,
- EXT2_FT_MAX
- };
对于我们关注的code下面的raw目录而言,下面有六个条目(算上 . 和..)。我们的raw目录文件有3个目录,三个普通文件。现在我们要做的是把目录文件所在的数据块的内容拷贝到一个文件,然后分析下目录文件在数据块上都存了那些内容。
- [root@localhost raw]# ll -ai
- total 48
- 65541 drwxr-xr-x 3 root root 4096 Aug 11 14:17 .
- 65537 drwxr-xr-x 6 root root 4096 Aug 11 14:06 ..
- 65542 -rw-r--r-- 1 root root 12 Aug 11 14:07 hello
- 65545 drwxr-xr-x 2 root root 4096 Aug 11 14:17 sub_dir
- 65543 -rw-r--r-- 1 root root 13 Aug 11 14:07 test
- 65544 -rw-r--r-- 1 root root 21 Aug 11 14:14 test_another
因为我们的raw目录的inode号为65541,
- 65541 drwxr-xr-x 3 root root 4096 Aug 11 14:17 raw
- [root@localhost /]# debugfs /dev/loop0
- debugfs 1.39 (29-May-2006)
- debugfs: stat <65541>
- Inode: 65541 Type: directory Mode: 0755 Flags: 0x0 Generation: 3830748590
- User: 0 Group: 0 Size: 4096
- File ACL: 66562 Directory ACL: 0
- Links: 2 Blockcount: 16
- Fragment: Address: 0 Number: 0 Size: 0
- ctime: 0x50269f5d -- Sat Aug 11 14:07:25 2012
- atime: 0x50269f62 -- Sat Aug 11 14:07:30 2012
- mtime: 0x50269f5d -- Sat Aug 11 14:07:25 2012
- BLOCKS:
- (0):77824
- TOTAL: 1
- [root@localhost ext2_study]# dd if=/dev/loop0 bs=4k skip=77824 count=1 |od -tx1 -Ax > raw_info.log
- 1+0 records in
- 1+0 records out
- 4096 bytes (4.1 kB) copied, 6.3837e-05 seconds, 64.2 MB/s
- struct ext2_inode {
- __le16 i_mode; /* File mode */
- __le16 i_uid; /* Low 16 bits of Owner Uid */
- __le32 i_size; /* Size in bytes */
- __le32 i_atime; /* Access time */
- __le32 i_ctime; /* Creation time */
- __le32 i_mtime; /* Modification time */
- __le32 i_dtime; /* Deletion Time */
- __le16 i_gid; /* Low 16 bits of Group Id */
- __le16 i_links_count; /* Links count */
- __le32 i_blocks; /* Blocks count */
- __le32 i_flags; /* File flags */
- union {
- struct {
- __le32 l_i_reserved1;
- } linux1;
- struct {
- __le32 h_i_translator;
- } hurd1;
- struct {
- __le32 m_i_reserved1;
- } masix1;
- } osd1; /* OS dependent 1 */
- __le32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
- __le32 i_generation; /* File version (for NFS) */
- __le32 i_file_acl; /* File ACL */
- __le32 i_dir_acl; /* Directory ACL */
- __le32 i_faddr; /* Fragment address */
- union {
- struct {
- __u8 l_i_frag; /* Fragment number */
- __u8 l_i_fsize; /* Fragment size */
- __u16 i_pad1;
- __le16 l_i_uid_high; /* these 2 fields */
- __le16 l_i_gid_high; /* were reserved2[0] */
- __u32 l_i_reserved2;
- } linux2;
- struct {
- __u8 h_i_frag; /* Fragment number */
- __u8 h_i_fsize; /* Fragment size */
- __le16 h_i_mode_high;
- __le16 h_i_uid_high;
- __le16 h_i_gid_high;
- __le32 h_i_author;
- } hurd2;
- struct {
- __u8 m_i_frag; /* Fragment number */
- __u8 m_i_fsize; /* Fragment size */
- __u16 m_pad1;
- __u32 m_i_reserved2[2];
- } masix2;
- } osd2; /* OS dependent 2 */
- };
- [root@localhost code]# dumpe2fs /dev/loop0
- dumpe2fs 1.39 (29-May-2006)
- Filesystem volume name:
- .......
- Group 1: (Blocks 32768-65535)
- Backup superblock at 32768, Group descriptors at 32769-32769
- Reserved GDT blocks at 32770-32800
- Block bitmap at 32801 (+33), Inode bitmap at 32802 (+34)
- Inode table at 32803-33826 (+35)
- 31709 free blocks, 32768 free inodes, 0 directories
- Free blocks: 33827-65535
- Free inodes: 32769-65536
- Group 2: (Blocks 65536-98303)
- Block bitmap at 65536 (+0), Inode bitmap at 65537 (+1)
- Inode table at 65538-66561 (+2)
- 31732 free blocks, 32759 free inodes, 6 directories
- Free blocks: 66563-67583, 67585-69631, 69633-71679, 71681-77823, 77825-96255, 96261-98303
- Free inodes: 65546-98304
- [root@localhost ext2_study]# dd if=/dev/loop0 bs=4k skip=65536 count=3 |od -tx1 -Ax > group_info.log
- 3+0 records in
- 3+0 records out
- 12288 bytes (12 kB) copied, 9.311e-05 seconds, 132 MB/s
- [root@localhost ext2_study]# vi group_info.log
- Added cscope database /home/manu/work/TDA/src/trend/cscope.out
- Press ENTER or type command to continue
1 我们的inode表在group_info中的的偏移量为8KB
2 块组2的第一个inode号是65537,我们关心的inode是65441,也就是第5个inode,第五个inode的偏移量为
(5-1)*128 = 512
3 i_block数组对应inode数据结构偏移量为40字节。
所以我们的inode为65441对应的文件的数据块的块号对应的偏移量为
8192+512+40 = 8744 = 0x2228
OK ,我们打开group_info,log 看下0x2228出的int值是不是77824
- 002200 ed 41 00 00 00 10 00 00 3c a2 26 50 d6 a1 26 50
- 002210 d6 a1 26 50 00 00 00 00 00 00 03 00 10 00 00 00
- 002220 00 00 00 00 00 00 00 00 00 30 01 00 00 00 00 00
- 002230 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
殊途同归,用debugfs得到的目录文件所在的数据块的块号,和我们读块组2的inode table得到的,是一样的。
按照教科书上的讲解,目录文件的内容以目录条目的形式,存储,我们共有6个条目,在磁盘上应该是这种形式存储的:
需要注意的有两点
1 文件名按4个字节对齐,不足的补0.
- #define EXT2_DIR_PAD 4
- #define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1)
- #define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \
- ~EXT2_DIR_ROUND)
2 rec_len这个字段需要摘出来特别解释,字面的解释是当前这个记录的长度,比如hello 文件,文件名5字节,加上前面的ext2_dir_entry_2 8个字节,共13个字节,又因为4字节对齐,所以16个字节。表面看就是这个含义。其实不然。他的官方含义是 :从当前条目的rec_len的末尾到下一个条目rec_len末尾的偏移量。
呵呵有些人说了,这不是一回事吗。其实不然,如果我把test文件删除了,你就会看出不一样。后面会提到,并解释。
下面,写了个代码,解析dir文件数据块的内容。(注,下面的代码实在冯锐郑勇代码基础上修改而成,同时参考了UNIX Filesystems一书的第二章,光荣属于前辈)
- #include <stdio.h>
- #include <stdlib.h>
- #include <ext2fs/ext2_fs.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- #include <sys/types.h>
- #include <unistd.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include<errno.h>
- #include<string.h>
- #define BUFSZ 4096
- #define EXT2_DIR_PAD 4
- #define EXT2_DIR_ROUND (EXT2_DIR_PAD - 1)
- #define EXT2_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT2_DIR_ROUND) & \
- ~EXT2_DIR_ROUND)
- struct ext2_dir_entry_part {
- __u32 inode; /* Inode number */
- __u16 rec_len; /* Directory entry length */
- __u8 name_len; /* Name length */
- __u8 file_type;
- } ;
- void usage()
- {
- printf("read_dir_entry [dir entry filename]\n");
- }
- int main(int argc, char **argv)
- {
- struct ext2_dir_entry_2 de;
- char *filename = NULL;
- int fd ;
- int rtn = 0;
- int length = 0;
- char buf[BUFSZ] = {0};
- char name[512] = {0};
- struct ext2_dir_entry_part *dir = NULL;
- if (argc < 2)
- {
- printf("Too few parameters!\n");
- usage();
- exit(1);
- }
- filename = argv[1];
- fd = open(filename, O_RDONLY);
- if (fd < 0)
- {
- printf("cannot open file: %s\n", filename);
- exit(1);
- }
- printf(" offset | inode number | rec_len | name_len | file_type | name\n");
- printf("======================================================================\n");
-
- int pos = 0;
-
- while ( rtn = read(fd,buf,BUFSZ))
- {
- if (rtn < 0)
- {
- fprintf(stderr, "read dir file (%s) failed ,(%s)\n ",filename,strerror(errno));
- return -1;
- }
- loop:
- dir = (struct ext2_dir_entry_part*) (buf + pos);
- if(dir->rec_len == 0)
- {
- break;
- }
- strncpy(name,(char*)(dir+1),dir->name_len);
-
- printf("%6d: %12d%12d%12d%12d %s\n",
- pos, dir->inode, dir->rec_len, dir->name_len, dir->file_type,name);
- pos += EXT2_DIR_REC_LEN(dir->name_len);
- // pos +=dir->rec_len;
- memset(name,0,512);
- if (pos < rtn - sizeof(struct ext2_dir_entry_part))
- {
- goto loop;
- }
-
- }
- close(fd);
- }
- [root@localhost ext2_study]# dd if=/dev/loop0 of=raw_block bs=4K skip=77824 count=1
- 1+0 records in
- 1+0 records out
- 4096 bytes (4.1 kB) copied, 4.6787e-05 seconds, 87.5 MB/s
在对照看下前面我用visio绘制的图,是一样的。目录文件的内容就是这样的。
参考文献
1 如何恢复 Linux 上删除的文件,第 2 部分
2深入linux内核架构
3 Understand Linux Kernel
4 UNIX Filesystems Evolution Design and Implementation