如何制作Ext4文件系统镜像
Android中system.img的两种格式及其相互转换方法
搞Android的同学经常会接触到system.img。但是该文件经常以两种格式出现:raw和sparse。
一种是raw ext4 image,即经常说的raw image,使用file观察它: 其特点是完整的ext4分区镜像(包含很多全零的无效填充区),可以直接使用mount进行挂载,因此比较大(一般1G左右)。
$ file system.img
system.img: Linux rev 1.0 ext4 filesystem data, UUID=57f8f4bc-abf4-655f-bf67-946fc0f9f25b (extents) (large files)
信息非常明确了。
另一种是sparse ext4 image,即经常说的simg,使用file观察它:
$ file system.img
system.img: data
就是说是一个非常普通的dat文件。由于它将raw ext4进行稀疏描述,因此尺寸比较小(没有全零的无效填充区,一般在300到500M之间)。
android本身提供了源代码工具在两者之间转换,源代码位于:
system/core/libsparse/simg2img.c // 将sparse image转换为raw image;
system/core/libsparse/img2simg.c // 将raw image转换为sparse image;
如果完整的进行过一次Android的编译,默认会将simg2img当作主机工具编译出来,放在out/host/linux-x86/bin/simg2img处。 但默认是不会编译img2simg的,我们可以手工进行编译:
$ . build/envsetup.sh
$ lunch aosp_hammerhead-userdebug
$ make img2simg_host
这样就会编译出out/host/linux-x86/bin/img2simg。 如果要将system.raw.img转换为system.simg: $ img2simg system.raw.img system.simg
编译Android源码最后是使用make_ext4生成 Ext4镜像+签名(更新:合理使用make_ext4参数是不会导致添加签名的),如果需要纯Ext4镜像,那么需要再去掉签名,使用simg2img。这个两个工具都是在编译Android源码时编译出来的,如果没有Android源码编译环境,当然也可以使用Ubuntu社区已经整理好的工具集android-tools-fsutils,该工具集文件列表如下:(来自android-tools-fsutils/filelist)
/usr/bin/ext2simg
/usr/bin/ext4fixup
/usr/bin/img2simg
/usr/bin/make_ext4fs
/usr/bin/mkuserimg
/usr/bin/simg2img
/usr/bin/simg2simg
/usr/bin/simg_dump
/usr/bin/test_ext4fixup
工具集安装方法:
sudo apt-get install android-tools-fsutils
1
1
制作Ext4文件系统镜像:
# 创建测试目录
mkdir /tmp/ext4 && cd /tmp/ext4 &&
mkdir test test_mount &&
echo "Hello Ext4" > test/HelloExt4.txt &&
# 生成名为test.ext4的Ext4镜像
make_ext4fs -l 512M test.ext4 test &&
# 挂载test.ext4到test_mount测试
mount -t ext4 -o loop test.ext4 test_mount &&
ls test_mount || echo "Error!"
一、在(yourAndroid code path)/out/ 目录里查找这个工具
unyaffs: 用于解压system.img
mkyaffs2image: 用于打包system.img
呵呵, find 命令肯定会吧? find out/ -name mkyaffs2image
找到了后,记住这个目录. 我的是(codePATH)/out/host/linux-x86/bin/
1.在任意文件夹下面建立一个system文件夹,我的是在home/changjiang/下面建的,这个是用来存放你解压system.img出来的文件,然后将找到的两个工具 unyaffs、mkyaffs2image拷贝到/changjiang/home/system
2.将编译出来的system.img或者下载的system.img复制到system文件夹中。
3.在终端中输入如下命令操作(/home/changjiang/替换成自己电脑中的路径,你的不是我的)
cd /home/changjiang/system
./unyaffs /home/changjiang/system/system.img(用unyaffs命令解压system.img),解压出来的有各种应用,库等等,你可以修改或者替换
二、重新打包system.img,虽然我不知道为什么要这样做,但是我知道这样做是可以的.
呵呵,linux没有不可能的事情.朝鲜的 红星操作系统就是例子.
具体办法是
system/ 这个目录,这个目录就是用来生成system.img 的.
mkyaffs2image system/ system.img
在Ubuntu中第一次使用mkyaffs2image命令时,会提示
mkyaffs2image:找不到命令
还需要安装mkyaffs2image
http://code.google.com/p/fatplus/downloads/detail?name=yaffs2-source.tar&can=2&q=
下载yaffs2-source.tar
解压后,进入utils文件夹,然后make
将make之后生成的mkyaffs2image文件拷贝到/usr/bin目录下
$ su
# cp mkyaffs2image /usr/bin/
# chmod 777 /usr/bin/mkyaffs2image
之后
$ mkyaffs2image
mkyaffs2image: image building tool for YAFFS2 built Jan 6 2012
usage: mkyaffs2image dir image_file [convert]
dir the directory tree to be converted
image_file the output file to hold the image
'convert' produce a big-endian image from a little-endian machine
表明安装成功,同时也可以看到该命令的用法
mkyaffs2image dir image_file
例如:
mkyaffs2image am1808-fs am1808-fs.yaffs//创建jffs2文件
搞Android的同学经常会接触到system.img。但是该文件经常以两种格式出现:raw和sparse。
一种是raw ext4 image,即经常说的raw image,使用file观察它: 其特点是完整的ext4分区镜像(包含很多全零的无效填充区),可以直接使用mount进行挂载,因此比较大(一般1G左右)。
$ file system.img
system.img: Linux rev 1.0 ext4 filesystem data, UUID=57f8f4bc-abf4-655f-bf67-946fc0f9f25b (extents) (large files)
信息非常明确了。
另一种是sparse ext4 image,即经常说的simg,使用file观察它:
$ file system.img
system.img: data
就是说是一个非常普通的dat文件。由于它将raw ext4进行稀疏描述,因此尺寸比较小(没有全零的无效填充区,一般在300到500M之间)。
android本身提供了源代码工具在两者之间转换,源代码位于:
system/core/libsparse/simg2img.c // 将sparse image转换为raw image;
system/core/libsparse/img2simg.c // 将raw image转换为sparse image;
如果完整的进行过一次Android的编译,默认会将simg2img当作主机工具编译出来,放在out/host/linux-x86/bin/simg2img处。 但默认是不会编译img2simg的,我们可以手工进行编译:
$ . build/envsetup.sh
$ lunch aosp_hammerhead-userdebug
$ make img2simg_host
这样就会编译出out/host/linux-x86/bin/img2simg。 如果要将system.raw.img转换为system.simg: $ img2simg system.raw.img system.simg
为了方便没有源代码的同学,这里提供img2simg的
下载路径: http://download.csdn.net/detail/howellzhu/8399215
对于simg2img,到处都可以找到了,还有
windows版本的,这里就不提供了。 使用方法:
$ simg2img
$ img2simg []
1、解压system.img
先用file命令查看system.img的文件类型
file system.img
system.img: Linux rev 1.0 ext4 filesystem data, UUID=57f8f4bc-abf4-655f-bf67-946fc0f9f25b (needs journal recovery) (extents) (large files)
看到没 ext4 filesystem data
那好办,采用挂载分区的方式来打开system.img文件
mount -t ext4 -o loop system.img systemimg //此命令的意思将system.img镜像文件挂载到systemimg目录
cd systemimg
Android标准的文件结构就出来了,里面可以“随意”定制
2、重新打包生成system.img
打包命令:
./make_ext4fs -l 1024M -s -a system system_new.img systemimg
参数解析:1024M代表你要打包的system.img大小,这个值可以参考挂载前system.img的大小
"-a system",是指这个img用于android系统,挂载点是/system,使用这个参数,make_ext4fs会根据private/android_filesystem_config.h里定义好的权限来给文件夹里的所有文件重新设置权限,如果你刷机以后发现有文件权限不对,可以手工修改android_filesystem_config.h来添加权限,重新编译make_ext4fs,也可以不使用 “-a system”参数,这样就会使用文件的默认权限。
system_new.img代表新生成的img
systemimg就是上面挂载后的目录
一般很多人认为这样生成的system_new.img就可以用了,其实不然
再次用file命令查看文件类型
file system_new.img
system_new.img: data
跟之前的输出截然不同,这显然不是ext4文件类型,啥情况???
方法就是继续使用simg2img工具将其转换成真正的ext4文件
./simg2img system_new.img system_out.img
得到system_out.img
file system_out.img
system_out.img: Linux rev 1.0 ext4 filesystem data, UUID=57f8f4bc-abf4-655f-bf67-946fc0f9f25b (extents) (large files)
怎么样?这次得到的img就是ext4了吧?ok,那么这个system_out.img就是我们重新打包生成的img了,这就可以烧到手机里面看看效果了。。。
PS:上面提到的make_ext4fs simg2img等工具在android环境的out/host/linux-x86/bin目录下。
上文说到 通过remount的方式来修改/system/bin 下的权限;
还有一种办法就是在打包system.img时就设置好权限,而这种办法就是通过修改:make.ext4fs
system/core/include/private/android_filesystem_config.h
+ { 00777, AID_ROOT, AID_SHELL, 0, "system/bin/*" },
编译make.ext4fs:
# mmm system/extras/ext4_utils/
#mmm system/core/
再打包 ------- ,然后全部重新烧录:可以看到权限被修改过来。
参考转载:http://blog.csdn.net/myxmu/article/details/8583470
make_ext4fs用于Android平台上制作ext4文件系统的镜像。用法举例:
make_ext4fs -l 512M -s -a system system.ext4img system
之后再使用simg2img制作镜像。
有一个问题是,使用这样制作的镜像,system分区文件的权限都是预定的,即使先修改system目录文件权限后在制作镜像,烧入设备后,其权限仍未改变。关键问题在make_ext4fs工具,在制作ext4fs时更改了权限,其依据为system/core/private/android_ilesystem_config.h所定义的权限。
查询make_ext4fs的参数含义,可以了解到这一点。
-l 512M"是分区大小,i9100的system分区是512M;
-s就是生成ext4的S模式制作;
"-a system",是指这个img用于android系统,挂载点是/system,使用这个参数,make_ext4fs会根据private/android_filesystem_config.h里定义好的权限来给文件夹里的所有文件重新设置权限,如果你刷机以后发现有文件权限不对,可以手工修改android_filesystem_config.h来添加权限,重新编译make_ext4fs,也可以不使用 “-a system”参数,这样就会使用文件的默认权限。
如果不使用-a参数,则可。
今天偶尔看到有人问起,如何解包打包安卓的各类 img 文件。下面介绍下
首先可以去 https://android.googlesource.com/platform/system/extras下载工具源代码
$ git clone https://android.googlesource.com/platform/system/extras /your/path
checkout 你需要的版本,譬如 android-5.1.1_r13
$ cd /your/path/extras
$ git checkout android-5.1.1_r13
编译 simg2img,这里你需要 gcc 工具,linux 用户基本都由现成的,mac 用户通过 homebrew 安装一个去
$ cd /your/path/extras/ext4_utils
$ gcc -o simg2img -lz sparse_crc32.c simg2img.c
$ gcc -o make_ext4fs -lz make_ext4fs_main.c make_ext4fs.c ext4fixup.c ext4_utils.c allocate.c backed_block.c output_file.c contents.c extent.c indirect.c uuid.c sha1.c sparse_crc32.c wipe.c
当前目录会生成 simg2img 与 make_ext4fs 两进制文件
下面解包开始,以 system.img 为例
$ ./simg2img system.img system.ext4
随后,可以直接挂载,需要 root 权限
打包的话,则是如此操作
$ ./make_ext4fs -s -l 512M -a system system-data.img /your/path/system-data
或者使用源代码中的脚本
$ cd /your/path/extras
$ PATH="$PATH:$(pwd)/ext4_utils/make_ext4fs" ./ext4_utils/mkuserimg.sh -s /your/path/system-data system-data.img ext4 /tmp 512M
最后转换下
$ ./simg2img system-data.img system.img
OK,你可以刷入你的手机了,Good luck!
Super block
只是在ext3的基础上扩展了一些字段,可以支持更大的硬盘和更大的文件。如:s_blocks_count 修改为s_blocks_count_lo和s_blocks_count_hi
结构体如下:
- /*
- *Structure of the super block
- */
- struct ext4_super_block {
- /*00*/ __le32 s_inodes_count; /* Inodes count文件系统中inode的总数*/
- __le32 s_blocks_count_lo; /* Blocks count文件系统中块的总数*/
- __le32 s_r_blocks_count_lo; /* Reserved blocks count保留块的总数*/
- __le32 s_free_blocks_count_lo; /*Free blocks count未使用的块的总数(包括保留块)*/
- /*10*/ __le32 s_free_inodes_count; /* Free inodes count未使用的inode的总数*/
- __le32 s_first_data_block; /* First Data Block第一块块ID,在小于1KB的文件系统中为0,大于1KB的文件系统中为1*/
- __le32 s_log_block_size; /* Block size用以计算块的大小(1024算术左移该值即为块大小)(0=1K, 1=2K, 2=4K) */
- __le32 s_obso_log_frag_size; /* Obsoleted fragment size用以计算段大小(为正则1024算术左移该值,否则右移)*/
- /*20*/ __le32 s_blocks_per_group; /* # Blocks per group每个块组中块的总数*/
- __le32 s_obso_frags_per_group; /*Obsoleted fragments per group每个块组中段的总数*/
- __le32 s_inodes_per_group; /* # Inodes per group每个块组中inode的总数*/
- __le32 s_mtime; /* Mount time POSIX中定义的文件系统装载时间*/
- /*30*/ __le32 s_wtime; /* Write time POSIX中定义的文件系统最近被写入的时间*/
- __le16 s_mnt_count; /* Mount count最近一次完整校验后被装载的次数*/
- __le16 s_max_mnt_count; /* Maximal mount count在进行完整校验前还能被装载的次数*/
- __le16 s_magic; /* Magic signature文件系统标志*/
- __le16 s_state; /* File system state文件系统的状态*/
- __le16 s_errors; /* Behaviour when detectingerrors文件系统发生错误时驱动程序应该执行的操作*/
- __le16 s_minor_rev_level; /* minor revision level局部修订级别*/
- /*40*/ __le32 s_lastcheck; /* time of last check POSIX中定义的文件系统最近一次检查的时间*/
- __le32 s_checkinterval; /* max. time between checks POSIX中定义的文件系统最近检查的最大时间间隔*/
- __le32 s_creator_os; /* OS生成该文件系统的操作系统*/
- __le32 s_rev_level; /* Revision level修订级别*/
- /*50*/ __le16 s_def_resuid; /* Default uid for reserved blocks报留块的默认用户ID */
- __le16 s_def_resgid; /* Default gid for reserved blocks保留块的默认组ID */
- /*
- * These fields are for EXT4_DYNAMIC_REV superblocks only.
- *
- * Note: the difference between the compatible feature set and
- * the incompatible feature set is that if there is a bit set
- * in the incompatible feature set that the kernel doesn't
- * know about, it should refuse to mount the filesystem.
- *
- * e2fsck's requirements are more strict; if it doesn't know
- * about a feature in either the compatible or incompatible
- * feature set, it must abort and not try to meddle with
- * things it doesn't understand...
- */
- __le32 s_first_ino; /* First non-reserved inode标准文件的第一个可用inode的索引(非动态为11)*/
- __le16 s_inode_size; /* size of inode structure inode结构的大小(非动态为128)*/
- __le16 s_block_group_nr; /* block group # of this superblock保存此超级块的块组号*/
- __le32 s_feature_compat; /* compatible feature set兼容特性掩码*/
- /*60*/ __le32 s_feature_incompat; /* incompatible feature set不兼容特性掩码*/
- __le32 s_feature_ro_compat; /* readonly-compatible feature set只读特性掩码*/
- /*68*/ __u8 s_uuid[16]; /* 128-bit uuid for volume卷ID,应尽可能使每个文件系统的格式唯一*/
- /*78*/ char s_volume_name[16]; /* volume name卷名(只能为ISO-Latin-1字符集,以'\0'结束)*/
- /*88*/ char s_last_mounted[64]; /* directory where last mounted最近被安装的目录*/
- /*C8*/ __le32 s_algorithm_usage_bitmap;/* For compression文件系统采用的压缩算法*/
- /*
- * Performance hints. Directorypreallocation should only
- * happen if the EXT4_FEATURE_COMPAT_DIR_PREALLOC flag is on.
- */
- __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate预分配的块数*/
- __u8 s_prealloc_dir_blocks; /* Nr topreallocate for dirs给目录预分配的块数*/
- __le16 s_reserved_gdt_blocks; /* Pergroup desc for online growth */
- /*
- * Journaling support valid if EXT4_FEATURE_COMPAT_HAS_JOURNAL set.
- */
- /*D0*/ __u8 s_journal_uuid[16]; /* uuid of journal superblock日志超级块的卷ID */
- /*E0*/ __le32 s_journal_inum; /* inode number of journal file日志文件的inode数目*/
- __le32 s_journal_dev; /* device number of journal file日志文件的设备数*/
- __le32 s_last_orphan; /* start of list of inodes to delete要删除的inode列表的起始位置*/
- __le32 s_hash_seed[4]; /* HTREE hash seed HTREE散列种子*/
- __u8 s_def_hash_version; /* Default hash version to use默认使用的散列函数*/
- __u8 s_jnl_backup_type;
- __le16 s_desc_size; /* size of group descriptor */
- /*100*/ __le32 s_default_mount_opts;
- __le32 s_first_meta_bg; /* First metablock block group块组的第一个元块*/
- __le32 s_mkfs_time; /* Whenthe filesystem was created */
- __le32 s_jnl_blocks[17]; /* Backup of the journal inode */
- /* 64bit support valid if EXT4_FEATURE_COMPAT_64BIT */
- /*150*/ __le32 s_blocks_count_hi; /* Blocks count */
- __le32 s_r_blocks_count_hi; /* Reserved blocks count */
- __le32 s_free_blocks_count_hi; /*Free blocks count */
- __le16 s_min_extra_isize; /* All inodes have at least # bytes */
- __le16 s_want_extra_isize; /* New inodes should reserve # bytes */
- __le32 s_flags; /* Miscellaneous flags */
- __le16 s_raid_stride; /* RAID stride */
- __le16 s_mmp_update_interval; /* #seconds to wait in MMP checking */
- __le64 s_mmp_block; /* Blockfor multi-mount protection */
- __le32 s_raid_stripe_width; /* blocks on all data disks (N*stride)*/
- __u8 s_log_groups_per_flex; /* FLEX_BGgroup size */
- __u8 s_reserved_char_pad;
- __le16 s_reserved_pad;
- __le64 s_kbytes_written; /* nr of lifetime kilobytes written */
- __le32 s_snapshot_inum; /* Inode number of active snapshot */
- __le32 s_snapshot_id; /* sequential ID of active snapshot*/
- __le64 s_snapshot_r_blocks_count;/* reserved blocks for active
- snapshot's future use */
- __le32 s_snapshot_list; /* inode number of the head of the
- on-disk snapshot list */
- #define EXT4_S_ERR_START offsetof(structext4_super_block, s_error_count)
- __le32 s_error_count; /* number of fs errors */
- __le32 s_first_error_time; /* first time an error happened */
- __le32 s_first_error_ino; /* inode involved in first error */
- __le64 s_first_error_block; /* block involved of first error */
- __u8 s_first_error_func[32]; /*function where the error happened */
- __le32 s_first_error_line; /* line number where error happened */
- __le32 s_last_error_time; /* most recent time of an error */
- __le32 s_last_error_ino; /* inode involved in last error */
- __le32 s_last_error_line; /* line number where error happened */
- __le64 s_last_error_block; /* block involved of last error */
- __u8 s_last_error_func[32]; /*function where the error happened */
- #define EXT4_S_ERR_END offsetof(structext4_super_block, s_mount_opts)
- __u8 s_mount_opts[64];
- __le32 s_reserved[112]; /* Padding to the end of the block */
- };
group descriptor
块组描述信息。
包含了blockbitmap、inode bitmap、inode table等存放的块地址。
结构体如下:
- /*
- *Structure of a blocks group descriptor
- */
- struct ext4_group_desc
- {
- __le32 bg_block_bitmap_lo; /* Blocks bitmap block块位图所在的第一个块的块ID */
- __le32 bg_inode_bitmap_lo; /* Inodes bitmap block inode位图所在的第一个块的块ID */
- __le32 bg_inode_table_lo; /* Inodes table block inode表所在的第一个块的块ID */
- __le16 bg_free_blocks_count_lo;/*Free blocks count块组中未使用的块数*/
- __le16 bg_free_inodes_count_lo;/*Free inodes count块组中未使用的inode数*/
- __le16 bg_used_dirs_count_lo; /*Directories count块组分配的目录的inode数*/
- __le16 bg_flags; /* EXT4_BG_flags (INODE_UNINIT,etc) */
- __u32 bg_reserved[2]; /* Likely block/inode bitmap checksum*/
- __le16 bg_itable_unused_lo; /* Unused inodes count */
- __le16 bg_checksum; /* crc16(sb_uuid+group+desc) */
- __le32 bg_block_bitmap_hi; /* Blocks bitmap block MSB */
- __le32 bg_inode_bitmap_hi; /* Inodes bitmap block MSB */
- __le32 bg_inode_table_hi; /* Inodes table block MSB */
- __le16 bg_free_blocks_count_hi;/*Free blocks count MSB */
- __le16 bg_free_inodes_count_hi;/*Free inodes count MSB */
- __le16 bg_used_dirs_count_hi; /*Directories count MSB */
- __le16 bg_itable_unused_hi; /* Unused inodes count MSB */
- __u32 bg_reserved2[3];
- };
inode
保存了每个节点的信息。
该节点的属性、所占的块地址。
结构体如下:
- /*
- *Structure of an inode on the disk
- */
- struct ext4_inode {
- __le16 i_mode; /* File mode文件格式和访问权限*/
- __le16 i_uid; /* Low 16 bits of Owner Uid文件所有者ID的低16位*/
- __le32 i_size_lo; /* Size in bytes文件字节数*/
- __le32 i_atime; /* Access time文件上次被访问的时间*/
- __le32 i_ctime; /* Inode Change time文件创建时间*/
- __le32 i_mtime; /* Modification time文件被修改的时间*/
- __le32 i_dtime; /* Deletion Time文件被删除的时间(如果存在则为0)*/
- __le16 i_gid; /* Low 16 bits of Group Id文件所有组ID的低16位*/
- __le16 i_links_count; /* Links count此inode被连接的次数*/
- __le32 i_blocks_lo; /* Blocks count文件已使用和保留的总块数(以512B为单位)*/
- __le32 i_flags; /* File flags */
- union {
- struct {
- __le32 l_i_version;
- } linux1;
- struct {
- __u32 h_i_translator;
- } hurd1;
- struct {
- __u32 m_i_reserved1;
- } masix1;
- } osd1; /*OS dependent 1 */
- __le32 i_block[EXT4_N_BLOCKS];/*Pointers to blocks定位存储文件的块的数组*/
- __le32 i_generation; /* File version (for NFS) 用于NFS的文件版本*/
- __le32 i_file_acl_lo; /* File ACL包含扩展属性的块号,老版本中为0*/
- __le32 i_size_high;
- __le32 i_obso_faddr; /* Obsoleted fragment address */
- union {
- struct {
- __le16 l_i_blocks_high; /* were l_i_reserved1 */
- __le16 l_i_file_acl_high;
- __le16 l_i_uid_high; /* these 2 fields */
- __le16 l_i_gid_high; /* were reserved2[0] */
- __u32 l_i_reserved2;
- } linux2;
- struct {
- __le16 h_i_reserved1; /* Obsoleted fragment number/size which areremoved in ext4 */
- __u16 h_i_mode_high;
- __u16 h_i_uid_high;
- __u16 h_i_gid_high;
- __u32 h_i_author;
- } hurd2;
- struct {
- __le16 h_i_reserved1; /* Obsoleted fragment number/size which areremoved in ext4 */
- __le16 m_i_file_acl_high;
- __u32 m_i_reserved2[2];
- } masix2;
- } osd2; /*OS dependent 2 */
- __le16 i_extra_isize;
- __le16 i_pad1;
- __le32 i_ctime_extra; /* extra Change time (nsec << 2 | epoch) */
- __le32 i_mtime_extra; /* extra Modification time(nsec << 2 |epoch) */
- __le32 i_atime_extra; /* extra Access time (nsec << 2 | epoch) */
- __le32 i_crtime; /* File Creation time */
- __le32 i_crtime_extra; /* extraFileCreationtime (nsec << 2 | epoch) */
- __le32 i_version_hi; /* high 32 bits for 64-bit version */
- };
Dir
用于保存所有目录的信息。
根目录总是在inode表的第二项,而其子目录则在根目录文件的内容中定义。
结构体如下:
- struct ext4_dir_entry {
- __le32 inode; /* Inode number文件入口的inode号,0表示该项未使用*/
- __le16 rec_len; /* Directory entry length目录项长度*/
- __le16 name_len; /* Name length文件名包含的字符数*/
- char name[EXT4_NAME_LEN]; /* File name文件名*/
- };
关键词汇
先回顾几个基本的概念,如果不是特别清楚下面几个概念的话,可以去google一下。
inode:索引节点
superblock:超级块
block:文件系统块
block group:文件系统块组
disk block:磁盘块(512字节)
block device:块设备
VFS:虚拟文件系统
EXT4存储结构
假如把整个超级块比如一本书,那么文件系统的工作就是把要记录的内容,按页码,行段记录在这本书里。这当然也包括书的目录了。我们使用dumpe2fs工具输出:
root@ubuntu:/home/user1# dumpe2fs -h s.img.ext4
dumpe2fs 1.42.9 (4-Feb-2014)
Filesystem volume name: <none>
Last mounted on: <not available>
Filesystem UUID: 57f8f4bc-abf4-655f-bf67-946fc0f9f25b
Filesystem magic number: 0xEF53
Filesystem revision #: 1 (dynamic)
Filesystem features: has_journal resize_inode filetype needs_recovery extent sparse_super large_file uninit_bg
Default mount options: (none)
Filesystem state: clean
Errors behavior: Remount read-only
Filesystem OS type: Linux
Inode count: 46080
Block count: 184320
Reserved block count: 0
Free blocks: 3382
Free inodes: 44404
First block: 0
Block size: 4096
Fragment size: 4096
Reserved GDT blocks: 47
Blocks per group: 32768
Fragments per group: 32768
Inodes per group: 7680
Inode blocks per group: 480
Last mount time: Tue Jul 19 07:59:31 2016
Last write time: Tue Jul 19 07:59:31 2016
Mount count: 2
Maximum mount count: -1
Last checked: Wed Dec 31 16:00:00 1969
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: tea
Journal backup: inode blocks
Journal features: (none)
Journal size: 11M
Journal length: 2880
Journal sequence: 0x00000004
Journal start: 0
Reserved GDT blocks: 609 Blocks per group: 32768 Fragments per group: 32768 Inodes per group: 8128 Inode blocks per group: 508 Flex block group size: 16 Filesystem created: Mon May 14 13:30:51 2012 Last mount time: Sun Jan 6 18:51:16 2013 Last write time: Sun Jan 6 18:51:16 2013 Mount count: 282 Maximum mount count: -1 Last checked: Mon May 14 13:30:51 2012 Check interval: 0 (<none>) Lifetime writes: 20 GB 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 |
首先,映入眼帘的是该超级块的相关重要参数,比如inode大小,块组含块数,块组inode数目等等。这些数据是存在ext4_super_block,ext4_sb_info这两个结构体中,定义在ext4.h头文件里,它们不是本篇重要讨论的内容。我们只需要知道它们是存放一些超级块信息的结构体即可。
接下来,可以看到ext4硬盘上的存储结构:
Group 0: (Blocks 0-32767) [ITABLE_ZEROED] Checksum 0x7cf3, unused inodes 0 Primary superblock at 0, Group descriptors at 1-2 Reserved GDT blocks at 3-611 Block bitmap at 612 (+612), Inode bitmap at 628 (+628) Inode table at 644-1151 (+644) 2720 free blocks, 0 free inodes, 1383 directories Free blocks: 8888-8959, 9068, 9071-9135, 9144-9175, 9200-9207, 9213-9214, 9279, 9700-10120, 11823-11964, 12213-12870, 12879-13043, 13139-13254, 18432-19021, 22748-22975, 32549-32767 Free inodes: |
这是块组0的情况,它表明块组0由块号为0-32767这32768个块组成,超级块基本信息存在块0,块组描述符在块1-2,预留的块组描述符表在块3-611,块位图在块612中,inode位图在块628中,Inode表在块644-1151中,空闲的块有很多,空闲的inode没了。
接下来,我们将重点分析这句废话中每个词的含义
超级块基本信息:
我们在前面已经讲过了。顾名思义,不多解释。
块组描述符
在内核中就是结构体ext4_group_desc,它包括的内容为:块位图块号,inode位图块号,inode表块号,空闲块计数,自由块计数等等。
预留的块组描述符表
为以后要使用所留下来的空间。
块位图
这个就是一个块使用情况记录表。记录哪些块使用,哪些块未使用。它的原理就是对整个块组中0-32767这总共32768个块中作一个映射。根据一个bytes有8个位00000000,一个块有4096bytes也就是有4096*8=32768个位,这32768个位刚好对应了块组中32768的块。如果第N个块被使用了则标记第N位为1,否则为0。
inode位图
和上面的块位图一样,这个是inode的使用情况记录表。由超级块基本信息可以看到每个块组有8128个inode,这里对inode的映射原理和块位图也是一样,只不过 没有用满一个块。
inode表
inode表就是具体存放inode信息的地方。在ext4中,inode的大小为256字节(ext2/3中仅有它的一半,128字节),一个块可以存放16个inode,由于一个块组有8128 个inode,总共需要8128/16=508个块存放inode表。这个值可以在超级块基本信息中的Inode blocks per group中看到。
讲完了这些词的含义,我们对group 0有了初步的了解。那么group 1呢?
Group 1: (Blocks 32768-65535) [ITABLE_ZEROED] Checksum 0xbb99, unused inodes 0 Backup superblock at 32768, Group descriptors at 32769-32770 Reserved GDT blocks at 32771-33379 Block bitmap at 613 (+4294935141), Inode bitmap at 629 (+4294935157) Inode table at 1152-1659 (+4294935680) 598 free blocks, 0 free inodes, 648 directories Free blocks: 33424-33439, 33442-33443, 33564-33627, 33644-33647, 33652-33663, 33725-33871, 33878-33931, 33934-33973, 33976-33983, 34046-34047, 34176-34303, 36008, 36015, 36019, 36412, 40299-40415 Free inodes: |
我们看到group 1 中,primary superblock 变为了backup superblock,由于超级块基本信息对于文件系统至关重要,为了系统的健壮性,ext文件系统在每个块组中都进行了备份。ext4考虑到在每个块组中都备份有点多余,尤其是组描述符表所以就仅在块号以3,5,7为幂的块组上进行备份。
用个表格表示超级块中块组的结构:
ext4 超级块 | 块组描述符 | Reserved GDT Blocks | 数据块位图 | Inode位图 | inode 表 | 数据块 |
1 block | 若干blocks | 若干 blocks | 1 block | 1 block | 若干 | 好多好多块 |
inode | Purpose |
0 | Doesn't exist; there is no inode 0. |
1 | List of defective blocks. |
2 | Root directory. |
3 | ACL index. |
4 | ACL data. |
5 | Boot loader. |
6 | Undelete directory. |
7 | Reserved group descriptors inode. |
8 | Journal inode. |
11 | First non-reserved inode. Usually this is the lost+found directory. |
块寻址
ext4的块寻址已经改为48位。这种设计改动是为了支持更大的文件系统大小。EXT4使用了区段(extents)这个概念,取代了过去早期UNIX文件系统中(ext2/3)中低效的非直接块映射机制。区段和NTFS上的cluster有点类似,它们都是选定了一个特定的块地址并把数个块组合一个区间。一个文件如果是碎片化的,那么就意味它着拥有多个区段,ext4会尽它自己的努力保持文件连续。
这种新的块寻址策略导致了先前工具的大部分问题。举个列子:
[root@localhost Desktop]# stat math.c File: `math.c' Size: 1477 Blocks: 8 IO Block: 4096 regular file Device: fd00h/64768d Inode:420402 Links: 1 Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2013-01-05 15:07:11.815541582 +0800 Modify: 2012-08-20 13:40:02.496797954 +0800 Change: 2012-12-30 11:28:54.751357610 +0800 |
由上面得到文件math.c的Inode号为420402
[root@localhost Desktop]# istat /dev/mapper/VolGroup-lv_root 420402 inode: 420402 Allocated Group: 51 Generation Id: 1062005310 uid / gid: 0 / 0 mode: rrw-r--r-- Flags: size: 1477 num of links: 1 Inode Times: Accessed: 2013-01-05 15:07:11 (CST) File Modified: 2012-08-20 13:40:02 (CST) Inode Modified: 2012-12-30 11:28:54 (CST) Direct Blocks: 127754 |
由上面的命令结果可以看到,Inode位于节点上块组51上,留意上面命令最下面有Direct Blocks这一行,这一行写着127754。在ext4的文件系统中,由于direct block映射的块寻址机制被取代,而采取的是extent区段树的块寻址。这个地方的值基本上是无效的。127754这个值十六进制表示为0x1f30a,我们在稍后的讨论这个值的来源。
我们知道了math.c这个文件的inode号码为420402,那么怎样知道它数据块是拿一个呢?
由前面的内容我们知道,每个块可以存16个inode,那么420402则在第420402/16=26275.125个块中,也就是位于第26275个块的第二个位置。每个块组有508个inode块,那么26725/508=51.72可以得知,位于块组51号之中,这个值可以在我们之前istat中可以验证。
那么具体是51块组中的哪个块呢?我们先确定这个inode块是在块组中的第几个块。因为每个块组有508个inode块,51块组前面共有51*508=25908个块。第26275个inode块在51块组中排在26275-25908=367的位置。查看51块组的描述:
Group 51: (Blocks 1671168-1703935) [ITABLE_ZEROED] Checksum 0x5ffd, unused inodes 0 Block bitmap at 1572867 (+4294868995), Inode bitmap at 1572883 (+4294869011) Inode table at 1574420-1574927 (+4294870548) 34 free blocks, 1 free inodes, 541 directories Free blocks: 1672899, 1673339, 1673344, 1674035, 1674054, 1674062, 1674077, 16 74334, 1674353-1674354, 1674423, 1675259, 1675754-1675755, 1675763, 1675860-1675 861, 1675867, 1675979, 1676183, 1676287, 1676367, 1676507, 1676526-1676527, 1676 567, 1676711, 1676743, 1676924-1676925, 1676934, 1691649, 1691658, 1691707 Free inodes: 422608 |
看到inode的起点位于1574420,由此,我们想要找的inode信息的块就存在于1574787inode块中的第二个。
可以使用blkcat查看1574787的内容,我们用vi切换到16进制模式打开如下:
0000070: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0000080: 1c00 0000 4430 c1a7 c8f1 733d fc8a 6cb1 ....D0....s=..l. 0000090: 58a8 b04f 04a0 6538 0000 0000 0000 02ea X..O..e8........ 00000a0: 0706 3c00 0000 0000 2200 0000 0000 0000 ..<....."....... 00000b0: 7365 6c69 6e75 7800 0000 0000 0000 0000 selinux......... 00000c0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 00000d0: 0000 0000 0000 0000 0000 0000 7379 7374 ............syst 00000e0: 656d 5f75 3a6f 626a 6563 745f 723a 6164 em_u:object_r:ad 00000f0: 6d69 6e5f 686f 6d65 5f74 3a73 3000 0000 min_home_t:s0... 0000100: a481 0000 c505 0000 1fd1 e750 f6b4 df50 ...........P...P 0000110: b2cd 3150 0000 0000 0000 0100 0800 0000 ..1P............ 0000120: 0000 0800 0100 0000 0af3 0100 0400 0000 ................ 0000130: 0000 0000 0000 0000 0100 0000 d64a 1c00 .............J.. 0000140: 0000 0000 0000 0000 0000 0000 0000 0000 ................ |
我们知道inode的size大小为256字节,那么第二个inode的起始位置也就是256=0x100处。
这个时候,我们看下inode的数据结构:
位置 | 值 | 名称 | 描述 |
0x0 | __le16 | i_mode | 文件模式 |
0x2 | __le16 | i_uid | 所有者UID. |
0x4 | __le32 | i_size_lo | 文件大小. |
0x8 | __le32 | i_atime | 读取时间. |
0xC | __le32 | i_ctime | Inode修改时间 |
0x10 | __le32 | i_mtime | 文件修改时间. |
0x14 | __le32 | i_dtime | 删除时间 |
0x18 | __le16 | i_gid | GID. |
0x1A | __le16 | i_links_count | 硬链接计数. |
0x1C | __le32 | i_blocks_lo | 块计数(512字节) |
0x20 | __le32 | i_flags | 文件标识(ext4使用extent需要标记0x80000) |
... |
0x28 | __le32 | i_block[EXT4_N_BLOCKS=15] | 块映射(ext2/3)或区段树(ext4) |
... |
我们按照表中的结构,对照上面的块码:
偏移 | 大小 | 名称 | 描述 |
0x0 | 0x81a4 | i_mode | 文件模式 |
0x2 | 0x0000 | i_uid | 所有者UID. |
0x4 | 0x0000 05c5 | i_size_lo | 文件大小. |
0x8 | 0x50e7 d11f | i_atime | 读取时间. |
0xC | 0x50df b4f6 | i_ctime | Inode修改时间 |
0x10 | 0x5031 b2cd | i_mtime | 文件修改时间. |
0x14 | 0x0000 0000 | i_dtime | 删除时间 |
0x18 | 0x0000 | i_gid | GID. |
0x1A | 0x0001 | i_links_count | 硬链接计数. |
0x1C | 0x0000 0008 | i_blocks_lo | 块计数(512字节) |
0x20 | 0x0080 0000 | i_flags | 文件标识(ext4使用extent需要标记0x80000) |
... |
0x28 | ... | i_block[EXT4_N_BLOCKS=15] | 块映射(ext2/3)或区段树(ext4) |
... |
细心的同学会发现大小的顺序是倒过来的,这是因为__lexx类型,le是little endian小端开始的缩写,意思就是从小到大的顺序。我们看到文件的大小为0x5c5=1477,这说明我们找的正是math.c的inode。
因为ext4 使用区段去代替了块映射去查找文件的内容。从40-99这60个字节过去是用作块映射的,如今用作存储extent信息。extent结构体有12字节的大小,反应快的同学马上会说,那么一个inode可以存放最多5个extent。然而这是不对的,因为前12个字节(40-51)被段头(extent header)所占据,所以,一个inode中的区段数最多只能是4。
现在,我们重点开始讲区段树(extent tree)
在ext4中,区段树取代了旧式的逻辑块映射。这是因为在老的模式中,分配连续的1000个块需要映射这1000个块的地址。但使用了区段,只需要映射一个区段并把区段的长度标记为1000(ee_len=1000)。如果起用了flex_bg的功能,一个区段可以分配一个很大的文件,这降低了元数据的大小,也提高了硬盘的效率。inode必须使用区段标记0x80000开启区段的功能。
区段的结构是树形的。每个树节点的起始为:struct ext4_extent_header
(这是一个结构体,我们接下来会给大家描述它的内容)。如果一个节点是树的内部节点(即eh.eh_depth>0),那么eh.eh_entries的指针将指向struct ext4_extent_idx;每个这些索引都指向一个块,块中包含更多的区段树中的节点。如果节点是树的叶子节点(eh.eh_depth=0),那么eh.eh_entries的指针将指向struct ext4_extent;这些结构体中指向文件的数据块。区段树的根节点存在inode.i_block,也就是我们在前面讨论的从40-99的那60个字节里。
说了这么多,我们还是赶紧看看extent的结构吧;
首先出场的是段头(extent header)
偏移 | 大小 | 名称 | 描述 |
0x0 | __le16 | eh_magic | 幻数magic number, 0xF30A. |
0x2 | __le16 | eh_entries | 区段数. |
0x4 | __le16 | eh_max | 最大的区段数. |
0x6 | __le16 | eh_depth | 段节点在段树中的深度。0则表示为叶子节点,指向数据块;否则指向其它段节点。 |
0x8 | __le32 | eh_generation | 暂不讨论 |
同样的,对照我们的实际数据看看
偏移 | 大小 | 名称 | 描述 |
0x0 | 0xf30a | eh_magic | 幻数magic number, 0xF30A. |
0x2 | 0x0001 | eh_entries | 区段数. |
0x4 | 0x0004 | eh_max | 最大的区段数. |
0x6 | 0x0000 | eh_depth | 段节点在段树中的深度。0则表示为叶子节点,指向数据块;否则指向其它段节点。 |
0x8 | 0x0000 0000 | eh_generation | 暂不讨论 |
接下来我们先看struct ext4_extent_idx
,这个结构在前面我们有提到过,用于extent树的内部节点。
偏移 | 大小 | 名称 | 描述 |
0x0 | __le32 | ei_block | 逻辑块号. |
0x4 | __le32 | ei_leaf_lo | 区段树中下一层的区段节点块地址(低32位),可以指向叶子节点或者内部节点。 |
0x8 | __le16 | ei_leaf_hi | 上一栏的高16位地址 |
0xA | __u16 | ei_unused | 未使用 |
我们接着看struct ext4_extent,叶子节点的结构体
偏移 | 大小 | 名称 | 描述 |
0x0 | __le32 | ee_block | 此区段的第一个块号,起始块号 |
0x4 | __le16 | ee_len | 区段内包含的块数. |
0x6 | __le16 | ee_start_hi | 此区段所指向的块号(高16位) |
0x8 | __le32 | ee_start_lo | 此区段所指向的块号(低32位) |
对照我们的实际数据看看
偏移 | 大小 | 名称 | 描述 |
0x0 | 0x0000 0000 | ee_block | 此区段的第一个块号,起始块号 |
0x4 | 0x0001 | ee_len | 区段内包含的块数. |
0x6 | 0x0000 | ee_start_hi | 此区段所指向的块号(高16位) |
0x8 | 0x001c 4ad6 | ee_start_lo | 此区段所指向的块号(低32位) |
由上表可以看到,因为我们的文件较小,这里作为叶子节点直接指向了文件数据块。数据块号为0x001c4ad6=1854166。我们使用命令查看块中的内容:
[root@localhost Desktop]# blkcat /dev/mapper/VolGroup-lv_root 1854166 #include <stdlib.h> #include <math.h> ... |
呵呵,可以看到,这就是我们的math.c文件。
思考:
请读者找一个大于4k的文件,看看能不能找到它的数据块。
删除文件
执行rm后删除文件,数据块并没有被清除,inode被释放,有下面3项会改变: 1. 文件大小被置为0
2. 段头中的区段值被设为0
3. 区段被清空
清空了区段意味着我们会失去文件起始物理块的地址和区段的长度。也就是说,在inode中已经不存在元数据可以帮我们恢复文件。这种行为和ext3回收inode时清除inode中的块指针很相似。这样就意味着我们只能靠传统的file-carving去恢复文件。
还记得在上一章中,我们提到过的结构体struct ext4_extent_idx
。这个结构体表示在extent tree中的节点。 我们在前面的章节已经阐述过,ext4使用extent取代了传统的block映射方式。我们的案例中只展示了只有一个extent的情况。本篇文章将介绍多个extent情况下的具体细节。
在本文中,我们选取了文件/var/log/messages,它是系统日志的记录文件,由于它的角色特殊,时间长了会造成给很多的碎片。我们还是先看看他的inode,方法和(一)中描述的一致,在此不重复了。
0000c00: 8081 0000 d036 0000 814b ee50 7f4b ee50 .....6...K.P.K.P 0000c10: 7f4b ee50 0000 0000 0000 0100 2000 0000 .K.P........ ... 0000c20: 0000 0800 0100 00000af30400 0400 0000 ................ 0000c30: 0000 0000 0000 0000 0100 0000 58c2 0b00 ............X... 0000c40: 0100 0000 0100 0000 e599 1b00 0200 0000 ................ 0000c50: 0100 0000 41e6 2f00 0300 0000 0100 0000 ....A./......... 0000c60: b878 1c00 275f ea72 0000 0000 0000 0000 .x..'_.r........ 0000c70: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0000c80: 1c00 0000 c018 94d1 c018 94d1 c0e5 ea86 ................ 0000c90: 9bea e850 d0a4 fcb4 0000 0000 0000 02ea ...P............ 0000ca0: 0706 4000 0000 0000 1f00 0000 0000 0000 ..@............. 0000cb0: 7365 6c69 6e75 7800 0000 0000 0000 0000 selinux......... 0000cc0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0000cd0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0000ce0: 7379 7374 656d 5f75 3a6f 626a 6563 745f system_u:object_ 0000cf0: 723a 7661 725f 6c6f 675f 743a 7330 0000 r:var_log_t:s0.. |
从0x0af3开始,这是extent header
起始的标记,我们还是像(一)那样,对照着表看
偏移 | 大小 | 名称 | 描述 |
0x0 | 0xf30a | eh_magic | 幻数magic number, 0xF30A. |
0x2 | 0x0004 | eh_entries | 区段数. |
0x4 | 0x0004 | eh_max | 最大的区段数. |
0x6 | 0x0000 | eh_depth | 段节点在段数中的深度。0则表示为叶子节点,指向数据块;否则指向其它段节点。 |
0x8 | 0x0000 | eh_generation | 暂不讨论 |
因为这里看到depth为0,说明extent中指向的是数据块。在extent header中接下来的将extent是我们把接下来的数据对应到它的表中
偏移 | 大小 | 名称 | 描述 |
0x0 | 0x0000 0000 | ee_block | 此区段的第一个块号,起始块号 |
0x4 | 0x0001 | ee_len | 区段内包含的块数. |
0x6 | 0x0000 | ee_start_hi | 此区段所指向的块号(高16位) |
0x8 | 0x000b c258 | ee_start_lo | 此区段所指向的块号(低32位) |
接下来的12个字节
偏移 | 大小 | 名称 | 描述 |
0x0 | 0x0000 0001 | ee_block | 此区段的第一个块号,起始块号 |
0x4 | 0x0001 | ee_len | 区段内包含的块数. |
0x6 | 0x0000 | ee_start_hi | 此区段所指向的块号(高16位) |
0x8 | 0x001b 99e5 | ee_start_lo | 此区段所指向的块号(低32位) |
再接下来的12个字节
偏移 | 大小 | 名称 | 描述 |
0x0 | 0x0000 0002 | ee_block | 此区段的第一个块号,起始块号 |
0x4 | 0x0001 | ee_len | 区段内包含的块数. |
0x6 | 0x0000 | ee_start_hi | 此区段所指向的块号(高16位) |
0x8 | 0x00f2 e641 | ee_start_lo | 此区段所指向的块号(低32位) |
最后的12个字节
偏移 | 大小 | 名称 | 描述 |
0x0 | 0x0000 0003 | ee_block | 此区段的第一个块号,起始块号 |
0x4 | 0x0001 | ee_len | 区段内包含的块数. |
0x6 | 0x0000 | ee_start_hi | 此区段所指向的块号(高16位) |
0x8 | 0x001c 78b8 | ee_start_lo | 此区段所指向的块号(低32位) |
可以通过使用blkcat看到4个块刚好凑成了messages文件,疑心重的同学可以把4个块拼成一个文件,用md5sum比较一下。这里我们并没有想看到extent中的内部节点情况。没关系,我们系统日志文件时会随着时间增长。正在笔者撰写此文时,messages已经变大了,并且超过了16k的大小,也就是超出了4个块。这时候我们看看messages的inode信息,方法不重复了。
0000c00: 8081 0000 4f51 0000 eb84 ef50 ea84 ef50 ....OQ.....P...P 0000c10: ea84 ef50 0000 0000 0000 0100 3800 0000 ...P........8... 0000c20: 0000 0800 0100 00000af3 0100 0400 0100 ................ 0000c30: 0000 0000 0000 0000 7c03 1c00 0000 0b00 ........|....... 0000c40: 0100 0000 0100 0000 e599 1b00 0200 0000 ................ 0000c50: 0100 0000 41e6 2f00 0300 0000 0100 0000 ....A./......... 0000c60: b878 1c00 275f ea72 0000 0000 0000 0000 .x..'_.r........ 0000c70: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0000c80: 1c00 0000 c80e c097 c80e c097 accd 3948 ..............9H 0000c90: 9bea e850 d0a4 fcb4 0000 0000 0000 02ea ...P............ 0000ca0: 0706 4000 0000 0000 1f00 0000 0000 0000 ..@............. 0000cb0: 7365 6c69 6e75 7800 0000 0000 0000 0000 selinux......... 0000cc0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0000cd0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ 0000ce0: 7379 7374 656d 5f75 3a6f 626a 6563 745f system_u:object_ 0000cf0: 723a 7661 725f 6c6f 675f 743a 7330 0000 r:var_log_t:s0.. |
经过一段时间的练习,同学们应该不需要对照表来识辨这些16进制码的含义了,如果不熟练的话,可以回过头多看几遍。我们看到这个inode较之前有了变化。extent的区段数变成了1,区段树的深度变成了1。区段树深度为1,这说明非叶子节点。
偏移 | 大小 | 名称 | 描述 |
0x0 | 0x0000 0000 | ei_block | 逻辑块号. |
0x4 | 0x001c 037c | ei_leaf_lo | 区段树中下一层的区段节点块地址(低32位),可以指向叶子节点或者内部节点。 |
0x8 | 0x0000 | ei_leaf_hi | 上一栏的高16位地址 |
0xA | 0x000b | ei_unused | 未使用 |
使用blkcat查看文件系统块1835900的内容,
0000000: 0af3 0e00 5401 0000 0000 0000 0000 0000 ....T........... 0000010: 0100 0000 58c2 0b00 0100 0000 0100 0000 ....X........... 0000020: e599 1b00 0200 0000 0100 0000 41e6 2f00 ............A./. 0000030: 0300 0000 0100 0000 b878 1c00 0400 0000 .........x...... 0000040: 0100 0000 2d8b 1b00 0500 0000 0100 0000 ....-........... 0000050: 9a79 1c00 0600 0000 0100 0000 0d82 1b00 .y.............. 0000060: 0700 0000 0100 0000 1182 1b00 0800 0000 ................ 0000070: 0100 0000 1382 1b00 0900 0000 0100 0000 ................ 0000080: 1682 1b00 0a00 0000 0100 0000 1882 1b00 ................ 0000090: 0b00 0000 0300 0000 034e 1c00 0e00 0000 .........N...... 00000a0: 0200 0000 074e 1c00 1000 0000 0400 0000 .....N.......... 00000b0: e082 0c00 0000 0000 0000 0000 0000 0000 ................ |
从上面的数据,我们可以看到有0x000e个区段,也就是在extent header后有14个extent或者extent ixd的结构体。接着看到extent最大数为0x0154=340,这个数字是怎么来的呢?我们知道在一个inode里面,这个值是4,那是因为('extent space' - 'extent header size') / 'extent size' 即 (60-12)/12=4,那么在这里也一样,只不过60这里要变为4096,因为我们不在inode中,而是在一个块中,即4096-12=4084,4084/12即340.333,最后还剩4个字节浪费了。再接着,是树的深度,也就是0x0000表明是叶子节点。那么我们知道,这个文件由着14个block的块组成。有兴趣的同学可以自己把文件dump出来拼一下。呵呵。
simg2img system.img s.new
mount s.new /mnt/data
修改launcher2.apk
make_ext4fs -l 891M -s s.s /mnt/data
void CSortlogcatDlg::OnButton2()
{
// TODO: Add your control notification handler code here
CString strText;
CString szLine; //存储行字符串
CStdioFile file;
CString filepath;
GetDlgItemText(IDC_EDIT1,filepath);
file.Open(filepath,CFile::modeRead);//打开文件
//逐行读取字符串
int linescount=0;
int linestot=0;
typedef CArray <int,int&> array; // 定义二维动态数组
array num;
num.SetSize(1000);
int tot=0;
while( file.ReadString( szLine ) )
{
char* p = strstr(szLine,".xml:");
if(p==0)continue;
p += strlen(".xml:");
char* pafter = strchr(p,':');
if(pafter){
pafter[0]=0;
//m_list.AddString(p);
int d = atoi(p);
num[tot]=d;
tot++;
}
}
//关闭文件
file.Close();
int i;
int j;
int temp;
for(i=0;i<tot-1;i++){
for(j=i+1;j<tot;j++){
if(num[j]<num[i]){
temp=num[j];
num[j]=num[i];
num[i]=temp;
}
}
}
CString cs;
for(i=0;i<tot;i++){
cs.Format("%d",num[i]);
m_list.AddString(cs);
if(num[i+1]!=num[i]+1){
m_list.AddString("==========================");
}
}
}