ch11-EXT2文件系统
作者:互联网
知识点归纳
EXT2即第二代扩展文件系统(英语:second extended filesystem,缩写为 EXT2)
Linux一直使用EXT2作为默认文件系统,EXT3是EXT2的扩展,增加的主要内容是日志文件
Block#0 是引导块,文件系统不会使用它,它用于容纳从磁盘引导操作系统的引导程序
Block#1 是超级块,用于容纳关于整个文件系统的信息
Block#2 是块组描述符,EXT2将磁盘块分成几个组,每组有8192个块,每组用一个块组描述符结构体描述
Block#8 是块位图,用于表示某种项的位序列,例如磁盘块或索引节点,位图用于分配和回收项。
Block#9 是索引节点位图,索引节点用于代表一个文件的数据结构
Block#10 是索引(开始)节点块,索引节点大小用于平均分割块大小,所以每个索引节点块都包含整数个索引节点。
数据块 是紧跟在索引节点块后面的文件存储块。
inode结构体
struct ext2_inode{
u16 i_mode; //16 bits = |tttt | ugs |rwx | rwx | rwx |
u16 i_uid; //owner uid
u32 i_size; //file size in bytes
u32 i_atime; // time fields in seconds
u32 i_ctime ; // since 00:00: 00,1-1-1970
u32 i_mtime;
u32 i_dtime;
u16 i_gid; //group ID
u16 i_links_count; //hard-1ink count
u32 i_b1ocks; //number of 512-byte sectors
u32 i_f1ags; //IGNORE
u32 i_reserved1; //IGNORE
u32 i_block [15]; //See details below
u32 i_pad[7]; //for inode size = 128 bytes
}
i_mode
i_block[15]数组存了15个指针
i_block[0-11]指向12个直接磁盘块
i_block[12]是间接块,指向一个磁盘块,该磁盘块包含256个块编号,即能映射256个磁盘块
i_block[13]是双重间接块,即双重映射,1 --> 256 * 256
i_block[14]是三重间接块,即三重映射,1 --> 256 * 256 * 256
dentry结构体
struct ext2_dir_entry_2{
u32 inode; //inode number; count from 1,NOT o
u16 rec_len; //this entry's length in bytes
u8 name_len; //name length in bytes
u8 fi1e_type; //not used
char name[EXT2_NAME_LEN] ; // name:1-255 chars,no ending NULL
};
邮差算法:类似二维数组的存储和访问
遍历算法:
(1)读取超级块。检查幻数s_magic ( OxEF53),验证它确实是EXT2FS。
(2)读取块组描述符块(1+s_first_data_block),以访问组0描述符。从块组描述符的bg_inode_table条目中找到索引节点的起始块编号,并将其称为InodesBeginBlock 。
(3)读取 InodeBeginBlock,获取/的索引节点,即INODE #2。
(4)将路径名标记为组件字符串,假设组件数量为n。
例如,如果路径名=/a/b/c,则组件字符串是“a”"b”“c”,其中n =3。用name[0],name[1],…,name[n-1]来表示组件。
(5)
从(3)中的根索引节点开始,在其数据块中搜索name[0]。
为简单起见,我们可以假设某个目录中的条目数量很少,因此一个目录索引节点只有12个直接数据块。
有了这个假设,就可以在12个(非零)直接块中搜索name[0]。目录索引节点的每个数据块都包含以下形式的dir_entry结构体:
[ino rec_len name_len NANE] [ino rec_len name_len NAME]
其中NAME是一系列nlen字符,不含终止NULL。
对于每个数据块,将该块读入内存并使用dir_entry *dp指向加载的数据块。
然后使用name_len将NAME提取为字符串,并与name[0]进行比较。如果它们不匹配,则通过以下代码转到下一个dir_entry:
dp = (dir_entry *) ((char *)dp + dp->rec_len);
继续搜索。如果存在name[0],则可以找到它的dir_entry,从而找到它的索引节点号。
(6)
使用索引节点号ino来定位相应的索引节点。回想前面的内容,ino从1开始计数。使用邮差算法计算包含索引节点的磁盘块及其在该块中的偏移量。
blk=(ino - 1) / INODES_PER_BLOCK+ InodesBeginBlock;
offset = (ino - 1)% INODES_PER_BLOCK;
然后在索引节点中读取/a,从中确定它是否是一个目录(DIR)。
如果/a不是目录,则不能有/a/b,因此搜索失败。
如果它是目录,并且有更多需要搜索的组件,那么继续搜索下一个组件name[1]。
现在的问题是:在索引节点中搜索/a的name[1],与第(5)步完全相同。
(7)
由于(5)~(6)步将会重复n次,所以最好编写一个搜索函数:
u32 search ( INODE*inodePtr, char *name)
然后只需调用n次search函数
如果搜索循环成功结束,ip必须指向路径名的索引节点。
文件系统的结构
文件系统的级别
文件系统的实现分为三个级别。每个级别处理文件系统的不同部分。这使得实现过程模块化,更容易理解。
第1级别实现了基本文件系统树。用户命令程序有:mkdir,creat,mknod,rmdir,link,unlink,symlink,rm,ls,cd和pwd
第2级别实现了文件内容读写函数
第3级别实现了文件系统的挂载、卸载和文件保护
基本文件系统
- type.h文件
这类文件包含EXT2文件系统的数据结构类型,比如超块、组描述符、索引节点和目录条目结构。
此外,它还包含打开文件表、挂载表、PROC结构体和文件系统常数。 - global.c文件
这类文件包含文件系统的全局变量。全局变量的例子有:
MINODE minode [NMINODE]; //in memory INODEs
MTABLE mtable[NMTABLE]; //mount tables
OFT oft[NOFT]; //Opened file instance
PROC proc [NPROC] //PRoC structures
PROC *running; //current executing PROC - util.c文件
该文件包含文件系统常用的实用程序函数。最重要的实用程序函数是读/写磁盘块函数iget()、iput()和 getino()。 - mount-root.c文件
该文件包含mount_root()函数,在系统初始化期间调用该函数来挂载根文件系统。它读取根设备的超级块,以验证该设备是否为有效的EXT2文件系统。
然后,它将根设备的根 INODE ( ino = 2)加载到minode中,并将根指针设置为根minode。它还将所有进程的当前工作目录设置为根minode。
分配一个挂载表条目来记录挂载的根文件系统。根设备的一些关键信息,如 inode和块的数量、位图的起始块和inode,表,也记录在挂载表中,以便快速访问。
问题与解决思路
时间戳转换为任意可读的日期格式
unix时间戳的数据类型,是32位(4字节)的无符号数,可以表示的大致时间范围是1970-2038
为了避免"时间回归"bug,有的地方已经开始用64位(8字节)的无符号数,来表示系统时间
<time.h>
time_t
通过sizeof,在本人电脑上是8字节,long是4字节,long long 是8字节。
获取unix时间戳
time_t time(time_t *seconds)time_t curtime; curtime = time(NULL); or time(curtime) printf("%ld\n",curtime);
char *ctime(const time_t *timer)
该函数返回一个字符串,包含了可读格式的日期和时间信息。
注意传入参数是指针。
格式固定,英文输出,星期,月份,日期,时:分:秒,年份
字符串结尾自带 '\n' ,不够灵活
struct tm *localtime(const time_t *timer)
传入指针,返回结构体指针struct tm { int tm_sec; /* 秒,范围从 0 到 59 */ int tm_min; /* 分,范围从 0 到 59 */ int tm_hour; /* 小时,范围从 0 到 23 */ int tm_mday; /* 一月中的第几天,范围从 1 到 31 */ int tm_mon; /* 月份,范围从 0 到 11 */ int tm_year; /* 自 1900 起的年数 */ int tm_wday; /* 一周中的第几天,范围从 0 到 6 */ int tm_yday; /* 一年中的第几天,范围从 0 到 365 */ int tm_isdst; /* 夏令时 */ };
虽然可以自行输出结构体里的成员,达到灵活输出日期的效果,但是time.h还有个格式化输出神器。
size_t strftime(char *str, size_t maxsize, const char *format, const struct tm *timeptr)
format如下:它们是区分大小写的
%a 星期几的简写
%A 星期几的全称
%b 月分的简写
%B 月份的全称
%c 标准的日期的时间串
%C 年份的后两位数字
%d 十进制表示的每月的第几天
%D 月/天/年
%e 在两字符域中,十进制表示的每月的第几天
%F 年-月-日
%g 年份的后两位数字,使用基于周的年
%G 年分,使用基于周的年
%h 简写的月份名
%H 24小时制的小时
%I 12小时制的小时
%j 十进制表示的每年的第几天
%m 十进制表示的月份
%M 十时制表示的分钟数
%n 新行符
%p 本地的AM或PM的等价显示
%r 12小时的时间
%R 显示小时和分钟:hh:mm
%S 十进制的秒数
%t 水平制表符
%T 显示时分秒:hh:mm:ss
%u 每周的第几天,星期一为第一天 (值从0到6,星期一为0)
%U 第年的第几周,把星期日做为第一天(值从0到53)
%V 每年的第几周,使用基于周的年
%w 十进制表示的星期几(值从0到6,星期天为0)
%W 每年的第几周,把星期一做为第一天(值从0到53)
%x 标准的日期串
%X 标准的时间串
%y 不带世纪的十进制年份(值从0到99)
%Y 带世纪部分的十进制年份
%z,%Z 时区名称,如果不能得到时区名称则返回空字符。
%% 百分号void format_time(time_t arg){ char buf[64]; struct tm *ptm = localtime(&arg); strftime(buf,64,"%F %T",ptm); printf("%s\n",buf); } 想要灵活输出,修改format即可 "%F %R" 2021-10-17 17:15 "%F %T" 2021-10-17 17:15:10 "%Y年%m月%e日 %H:%M:%S" 2021年10月17日 17:15:10
实践内容
显示超级块
首先创建一个包含简单EXT2文件系统的虚拟磁盘
dd if=/dev/zero of=mydisk bs=1024 count=1440
mke2fs -b 1024 mydisk 1440
superblock.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <fcntl.h>
4 #include <sys/types.h>
5 #include <unistd.h>
6 #include <ext2fs/ext2_fs.h>
7 #include <time.h>
8 //typedef u8,u16,u32 SUPER for convenience
9 typedef unsigned char u8;
10 typedef unsigned short u16;
11 typedef unsigned int u32 ;
12 typedef struct ext2_super_block SUPER;
13
14 SUPER *sp;
15 char buf[1024];
16 int fd,blksize,inodesize;
17 int print (char *s,u32 x){
18 printf ( "%-30s = %8d\n" ,s,x);
19 }
20
21 int super (char *device)
22 {
23 fd = open(device, O_RDONLY);
24 if(fd < 0 ){
25 printf ( " open %sfailed\n" , device);
26 exit(1);
27 }
28 lseek(fd,(long)1024*1,0); //block 1 on PD,offset 1024 on HD
29 read (fd,buf,1024);
30 sp = (SUPER*)buf; //as a super b1ock structure
31
32 //check EXT2 Fs magic number :
33 printf ("%-30s = %8x ","s_magic",sp->s_magic);
34 if(sp->s_magic != 0xEF53){
35 printf ( "NOT an EXT2 FS\n" );
36 exit(2);
37 }
38 printf( "EXT2 FS OK\n" ) ;
39 print( "s_inodes__count" ,sp->s_inodes_count);
40 print( "s_blocks_count" ,sp->s_blocks_count) ;
41 print ("s_r_blocks_count" ,sp->s_r_blocks_count);
42 print("s_free_inodes_count" , sp->s_free_inodes_count);
43 print ("s_free_blocks_count" , sp->s_free_blocks_count);
44 print ( "s_first_data_block" ,sp->s_first_data_block);
45 print ( "s_log_block_size" ,sp->s_log_block_size);
46 print ( "s_blocks_per_group", sp->s_blocks_per_group);
47 print ( "s_inodes _per_group",sp->s_inodes_per_group);
48 print ( "s__mnt_count" ,sp->s_mnt_count) ;
49 print ( "s_max_mnt_count" ,sp->s_max_mnt_count);
50 printf ( "%-30s = %8x\n" , "s_magic" , sp->s_magic);
51 printf ( "s__mtime = %s" , ctime ( &sp->s_mtime) ) ;
52 printf( "s_wtime =%s" , ctime ( &sp->s_wtime));
53 blksize = 1024 * ( 1 <<sp->s_log_block_size);
54 printf( "block size = %d\n", blksize) ;
55 printf ( "inode size = %d\n" ,sp->s_inode_size);
56 }
57
58 char *device = "mydisk" ; //default device name
59 int main(int argc, char *argv[])
60 {
61 if(argc>1){
62 device = argv[1];
63 }
64 super(device);
65 }
66
显示索引节点位图(imap)
imap.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <fcntl.h>
4 #include <ext2fs/ext2_fs.h>
5 #include <unistd.h>
6 #include <sys/types.h>
7 typedef unsigned char u8;
8 typedef unsigned short u16;
9 typedef unsigned int u32 ;
10 typedef struct ext2_super_block SUPER;
11 typedef struct ext2_group_desc GD;
12 #define BLKSIZE 1024
13
14 SUPER *sp;
15 GD *gp;
16 char buf[BLKSIZE];
17 int fd;
18
19 //get_block ( ) reads a disk block into a buf[ ]
20
21 int get_block(int fd, int blk,char *buf){
22 lseek (fd,(long)blk*BLKSIZE,SEEK_SET);
23 return read(fd,buf,BLKSIZE);
24 }
25
26 int imap(char *device){
27 int i, ninodes, blksize,imapblk;
28 fd = open (device,O_RDONLY) ;
29 if (fd < 0 ){
30 printf ( " open %s failed\n" , device);
31 exit (1);
32 }
33 get_block (fd,1,buf) ; //get superblock
34 sp = (SUPER*) buf;
35 //check magic number to ensure it's an EXT2 FS
36 ninodes = sp->s_inodes_count; //get inodes_count
37 printf ( "number of inodes = %d\n" , ninodes) ;
38 get_block(fd,2,buf); //get group descriptor
39 gp = (GD*)buf;
40 imapblk = gp->bg_inode_bitmap; //get imap block number
41 printf ( "imap blk number = %d\n" , imapblk) ;
42 get_block(fd,imapblk,buf); //get imap block into buf[ ]
43 for (i=0; i<=ninodes/8; i++){ //print each byte in HEX
44 printf ("%02x ", (u8)buf[i]);
45 }
46 printf ("\n");
47 }
48
49 int main(int argc,char *argv[]){
50 char *dev= "mydisk" ; //default device
51 if(argc>1){
52 dev = argv[1];
53 }
54 imap(dev);
55 }
标签:count,ch11,EXT2,int,sp,文件系统,char,block,name 来源: https://www.cnblogs.com/cfqlovem-521/p/15414979.html