本章之前的分析介绍了grub-install如何解析输入参数,并打开相应的存储设备,本章主要看grub如何识别根设备上的文件系统。
grub2-install第五部分
util/grub-install.c
...
grub_fs = grub_fs_probe (grub_dev);
grub_install_push_module (grub_fs->name);
grub_install_push_module (disk_module);
relative_grubdir = grub_make_system_path_relative_to_its_root (grubdir);
char *platname = grub_install_get_platform_name (platform);
char *platdir;
char *t = grub_util_path_concat (2, grubdir, platname);
platdir = grub_canonicalize_file_name (t);
free (t);
load_cfg = grub_util_path_concat (2, platdir, "load.cfg");
grub_util_unlink (load_cfg);
grub_util_pull_device (install_device);
install_drive = grub_util_get_grub_dev (install_device);
...
首先通过grub_fs_probe函数查找根设备grub_dev对应的文件系统,假设该文件系统为ext2,则返回值grub_fs为grub_ext2_fs。根据上一章的分析,传入的参数grub_dev为设备打开函数grub_device_open的返回值,内部存储了设备的信息。
往下的grub_install_push_module函数将模块名添加到全局数组中,后续要加载对应的模块,传入的参数grub_fs的name字段保存了文件系统名称,例如ext2、ext3,disk_module默认值为biosdisk。
如果grubdir属于某个挂载设备,grub_make_system_path_relative_to_its_root函数返回以挂点为根目录的路径,传入的参数grubdir为grub文件所在目录,默认为”/boot/grub”。
grub_install_get_platform_name函数根据平台索引platform获得平台名称platname,假设platform为GRUB_INSTALL_PLATFORM_I386_PC,则该函数返回i386-pc。在获得grub目录下对应cpu体系的目录名称platdir,假设为”/boot/grub/i386-pc”。然后检查platdir目录下是否存在load.cfg文件,如果存在,则通过grub_util_unlink函数删除该文件。
最后,grub_util_pull_device函数打开设备install_device,该参数由命令行中的参数指定,表示grub文件被安装的设备文件,例如”/dev/sda1”,再通过grub_util_get_grub_dev函数将该设备文件转化为驱动号,例如hd0。
下面重点分析grub_fs_probe函数。
grub2-install->grub_fs_probe
grub-core/kern/fs.c
grub_fs_t grub_fs_probe (grub_device_t device){
grub_fs_t p;
if (device->disk){
static int count = 0;
for (p = grub_fs_list; p; p = p->next){
(p->dir) (device, "/", probe_dummy_iter, NULL);
if (grub_errno == GRUB_ERR_NONE)
return p;
...
}
...
}
从这段简单的代码就能看出grub如何探测硬盘的文件系统,传入的参数device->disk中存储了硬盘的基本信息,例如磁道数,扇区数等等。如果这些信息存在,就遍历grub_fs_list链表,该链表存储了各个文件系统对应的结构,然后调用结构中的dir函数查找根路径,如果找到,就返回该结构对应的文件系统。例如查找一个以ext2为文件系统的硬盘的根路径时,最终就返回grub_fs_list列表中包含的grub_ext2_fs结构。
grub_ext2_fs结构对应的dir函数为grub_ext2_dir,下面来看。
grub2-install->grub_fs_probe->grub_ext2_dir
grub-core/fs/ext2.c
static grub_err_t grub_ext2_dir (grub_device_t device, const char *path,
grub_fs_dir_hook_t hook, void *hook_data){
struct grub_ext2_dir_ctx ctx = {
.hook = hook,
.hook_data = hook_data
};
struct grub_fshelp_node *fdiro = 0;
ctx.data = grub_ext2_mount (device->disk);
grub_fshelp_find_file (path, &ctx.data->diropen, &fdiro,
grub_ext2_iterate_dir, grub_ext2_read_symlink,
GRUB_FSHELP_DIR);
grub_ext2_iterate_dir (fdiro, grub_ext2_dir_iter, &ctx);
grub_free (ctx.data);
grub_dl_unref (my_mod);
return grub_errno;
}
传入的参数device为对应的设备,内部的disk存储了设备的基本信息,path为需要查找的路径,即根目录”/”,hook为probe_dummy_iter函数,hook_data为null。
首先通过grub_ext2_mount函数读取硬盘上的superblock,然后再获得根目录对应的inode信息。接着通过grub_fshelp_find_file函数查找路径path,返回的文件结点保存在参数fdiro中,内部存储了文件的具体信息,例如inode号等等。根据该inode信息,最后调用grub_ext2_iterate_dir函数修改文件的信息。
因此,如果某个文件系统是ext2,则调用grub_ext2_dir函数遍历根目录时就不会出错,最终返回的grub_errno为GRUB_ERR_NONE。
grub2-install->grub_fs_probe->grub_ext2_dir->grub_ext2_mount
grub-core/fs/ext2.c
static struct grub_ext2_data * grub_ext2_mount (grub_disk_t disk){
struct grub_ext2_data *data;
data = grub_malloc (sizeof (struct grub_ext2_data));
grub_disk_read (disk, 1 * 2, 0, sizeof (struct grub_ext2_sblock),
&data->sblock);
if (data->sblock.magic != grub_cpu_to_le16_compile_time (EXT2_MAGIC)
|| grub_le_to_cpu32 (data->sblock.log2_block_size) >= 16
|| data->sblock.inodes_per_group == 0
|| grub_le_to_cpu32 (data->sblock.log2_block_size) > 20
|| EXT2_INODE_SIZE (data) == 0
|| EXT2_BLOCK_SIZE (data) / EXT2_INODE_SIZE (data) == 0){
goto fail;
}
...
data->disk = disk;
data->diropen.data = data;
data->diropen.ino = 2;
data->diropen.inode_read = 1;
data->inode = &data->diropen.inode;
grub_ext2_read_inode (data, 2, data->inode);
return data;
}
首先通过grub_disk_read函数读取superblock,superblock位于每个分区的第3个扇区,因此传入的偏移参数为2,返回的结果保存在data->sblock中。接下来的if语句检查魔数magic,log2_block_size表示记录硬盘每个block的大小需要的比特数,block的大小不能大于65536,即2的16次方,inodes_per_group表示每个group中的索引节点数,该值不能等于0,最后检查log2_block_size是否大于20,由于比较的出发点不同,暂时保留,在本次比较中没用。其中EXT2_BLOCK_SIZE用于获取每个block的大小,EXT2_INODE_SIZE宏用于获取每个inode的大小。
#define EXT2_INODE_SIZE(data) \
(data->sblock.revision_level \
== grub_cpu_to_le32_compile_time (EXT2_GOOD_OLD_REVISION) \
? EXT2_GOOD_OLD_INODE_SIZE \
: grub_le_to_cpu16 (data->sblock.inode_size))
revision_level标识了superblock的版本,除了EXT2_GOOD_OLD_REVISION外,还有EXT2_DYNAMIC_REV。EXT2_GOOD_OLD_REVISION为较老的版本,只支持固定的inode大小EXT2_GOOD_OLD_INODE_SIZE,即128字节。新版本的EXT2_DYNAMIC_REV使用superblock中的inode_size字段记录每个inode的大小。
再往下省略了一些版本的检查。最后设置相关参数,调用grub_ext2_read_inode读取第二个inode,也即根目录对应的inode。
grub2-install->grub_fs_probe->grub_ext2_dir->grub_ext2_mount->grub_ext2_read_inode
grub-core/fs/ext2.c
static grub_err_t grub_ext2_read_inode (struct grub_ext2_data *data,
int ino, struct grub_ext2_inode *inode){
struct grub_ext2_block_group blkgrp;
struct grub_ext2_sblock *sblock = &data->sblock;
int inodes_per_block;
unsigned int blkno;
unsigned int blkoff;
grub_disk_addr_t base;
ino--;
grub_ext2_blockgroup (data, ino / grub_le_to_cpu32
(sblock->inodes_per_group), &blkgrp);
inodes_per_block = EXT2_BLOCK_SIZE (data) / EXT2_INODE_SIZE (data);
blkno = (ino % grub_le_to_cpu32 (sblock->inodes_per_group)) / inodes_per_block;
blkoff = (ino % grub_le_to_cpu32 (sblock->inodes_per_group)) % inodes_per_block;
base = grub_le_to_cpu32 (blkgrp.inode_table_id);
if (data->log_group_desc_size >= 6)
base |= (((grub_disk_addr_t) grub_le_to_cpu32 (blkgrp.inode_table_id_hi)) << 32);
if (grub_disk_read (data->disk, ((base + blkno) << LOG2_EXT2_BLOCK_SIZE (data)),
EXT2_INODE_SIZE (data) * blkoff, sizeof (struct grub_ext2_inode), inode))
return grub_errno;
return 0;
}
由于inode号ino计数时是从1开始的, 因此该函数首先将ino减1。然后通过grub_ext2_blockgroup函数读取对应group的group descriptor结构,存放在返回参数blkgrp中。其中ino除inodes_per_group表示要读取的inode在哪个group。
接下来计算的inodes_per_block表示每个block里的inode数,ino%grub_le_to_cpu32 (sblock->inodes_per_group)表示inode在group中是第几个inode。因此blkno表示对应的inode在某个group中是第几个block,blkoff为block内的偏移。前面返回的group descriptor结构中的inode_table_id字段指向对应group的inode table的绝对位置,单位为block。
最后通过grub_disk_read函数在该inode table内读出该inode并返回。
grub2-install->grub_fs_probe->grub_ext2_dir->grub_ext2_mount->grub_ext2_read_inode->
grub-core/fs/ext2.c
inline static grub_err_t grub_ext2_blockgroup (struct grub_ext2_data *data,
grub_uint64_t group, struct grub_ext2_block_group *blkgrp){
grub_uint64_t full_offset = (group << data->log_group_desc_size);
grub_uint64_t block, offset;
block = (full_offset >> LOG2_BLOCK_SIZE (data));
offset = (full_offset & ((1 << LOG2_BLOCK_SIZE (data)) - 1));
if ((data->sblock.feature_incompat
& grub_cpu_to_le32_compile_time (EXT2_FEATURE_INCOMPAT_META_BG))
&& block >= grub_le_to_cpu32(data->sblock.first_meta_bg)){
grub_uint64_t first_block_group;
first_block_group = (block << (LOG2_BLOCK_SIZE (data)
- data->log_group_desc_size));
block = (first_block_group * grub_le_to_cpu32(data->sblock.blocks_per_group));
if (group_has_super_block (data, first_block_group))
block++;
}else
block++;
return grub_disk_read (data->disk, ((grub_le_to_cpu32 (data->sblock.first_data_block)
+ block) << LOG2_EXT2_BLOCK_SIZE (data)), offset,
sizeof (struct grub_ext2_block_group), blkgrp);
}
group_desc_size表示group descriptor的大小,log_group_desc_size是其对应占用的比特数,full_offset是该group desciptor的起始地址,将该地址转化为block数block,对应的block内偏移为offset,full_offset按照block大小对齐。
接下来无论if还是else最后都递增block,略过superblock。对于block为1kB大小的文件系统,first_data_block为1,first_data_block对应superblock之前1024字节的引导扇区,即一个block。
grub_disk_read在上一张分析过了,最后读取的数据保存在地址blkgrp处,其实最终读取的就是某个group的group descriptor,这些descriptor被聚集起来,放在硬盘分区的开头部分,跟在superblock的后面。
grub2-install->grub_fs_probe->grub_ext2_dir->grub_fshelp_find_file
grub-core/fs/fshelp.c
grub_err_t grub_fshelp_find_file (const char *path, grub_fshelp_node_t rootnode,
grub_fshelp_node_t *foundnode, iterate_dir_func iterate_dir,
read_symlink_func read_symlink, enum grub_fshelp_filetype expecttype){
return grub_fshelp_find_file_real (path, rootnode, foundnode,
iterate_dir, NULL, read_symlink, expecttype);
}
static grub_err_t grub_fshelp_find_file_real (const char *path, grub_fshelp_node_t rootnode,
grub_fshelp_node_t *foundnode, iterate_dir_func iterate_dir,
lookup_file_func lookup_file, read_symlink_func read_symlink,
enum grub_fshelp_filetype expecttype){
struct grub_fshelp_find_file_ctx ctx = {
.path = path,
.rootnode = rootnode,
.symlinknest = 0,
.currnode = 0
};
grub_err_t err;
enum grub_fshelp_filetype foundtype;
char *duppath;
go_to_root (&ctx);
duppath = grub_strdup (path);
find_file (duppath, iterate_dir, lookup_file, read_symlink, &ctx);
grub_free (duppath);
*foundnode = ctx.currnode->node;
foundtype = ctx.currnode->type;
ctx.currnode->node = 0;
free_stack (&ctx);
if (expecttype == GRUB_FSHELP_REG && foundtype != expecttype)
return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a regular file"));
else if (expecttype == GRUB_FSHELP_DIR && foundtype != expecttype)
return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
return 0;
}
ctx为整个函数的上下文,其内部存放了一个节点链表,根节点为rootnode,首先通过go_to_root函数清空以根节点起始的所有节点,然后设置查找的根节点,并将当前节点指向根节点。
接着通过find_file函数查找文件,返回的foundnode表示文件结点,内部存储了inode号等信息,foundtype为文件类型,最后比较文件类型,如果不等于要查找的文件类型则报错,成功返回0。
grub2-install->grub_fs_probe->grub_ext2_dir->grub_fshelp_find_file->go_to_root
grub-core/fs/fshelp.c
static grub_err_t go_to_root (struct grub_fshelp_find_file_ctx *ctx){
free_stack (ctx);
return push_node (ctx, ctx->rootnode, GRUB_FSHELP_DIR);
}
static void free_stack (struct grub_fshelp_find_file_ctx *ctx){
while (ctx->currnode)
pop_element (ctx);
}
static grub_err_t push_node (struct grub_fshelp_find_file_ctx *ctx, grub_fshelp_node_t node,
enum grub_fshelp_filetype filetype){
struct stack_element *nst;
nst = grub_malloc (sizeof (*nst));
nst->node = node;
nst->type = filetype & ~GRUB_FSHELP_CASE_INSENSITIVE;
nst->parent = ctx->currnode;
ctx->currnode = nst;
return GRUB_ERR_NONE;
}
首先通过free_stack释放ctx上下文中的节点列表,然后通过push_node函数创建一个node节点,其父节点parent指向当前节点currnode,再更新当前节点currnode指向新创建的节点nst。
grub2-install->grub_fs_probe->grub_ext2_dir->grub_fshelp_find_file->grub_fshelp_find_file_real
grub-core/fs/fshelp.c
static grub_err_t find_file (char *currpath, iterate_dir_func iterate_dir,
lookup_file_func lookup_file, read_symlink_func read_symlink,
struct grub_fshelp_find_file_ctx *ctx){
char *name, *next;
grub_err_t err;
for (name = currpath; ; name = next){
char c;
grub_fshelp_node_t foundnode = NULL;
enum grub_fshelp_filetype foundtype = 0;
while (*name == '/')
name++;
if (! *name)
return 0;
for (next = name; *next && *next != '/'; next++);
if (ctx->currnode->type != GRUB_FSHELP_DIR)
return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
if (next - name == 1 && name[0] == '.')
continue;
if (next - name == 2 && name[0] == '.' && name[1] == '.'){
go_up_a_level (ctx);
continue;
}
c = *next;
*next = '\0';
directory_find_file (ctx->currnode->node, name, &foundnode, &foundtype, iterate_dir);
*next = c;
if (!foundnode)
break;
push_node (ctx, foundnode, foundtype);
if (ctx->currnode->type == GRUB_FSHELP_SYMLINK){
...
}
}
}
第一个for循环遍历路径currpath中的每个目录。首先略过路径中的文件分隔符,第二个for循环计算的next用于获取前后两个路径分隔符之间的文件名。如果当前的节点currnode不是目录,则报错,因为首先会从根目录开始查找,如果在某个目录中找到了最终文件或目录,则next为null,函数返回0,不会执行到这里。
如果两个路径分隔符之间为当前目录表示”.”,则遍历下一个目录,如果为”..”,则表示上一个目录,此时通过go_up_a_level函数返回上一级目录。接下来通过directory_find_file函数遍历当前目录,匹配name,查号文件,返回的foundnode中包含了查找到的文件对应的结点,结点内保存了诸如inode等信息。
如果没有找到foundnode,则表示路径中的某个目录或文件不存在,直接退出循环。如果找到了,则通过push_node函数将新结点foundnode加入到上下文ctx中。最后if语句中省略的部分用于处理文件链接,本章不关心。
grub2-install->grub_fs_probe->grub_ext2_dir->grub_fshelp_find_file->grub_fshelp_find_file_real->directory_find_file
grub-core/fs/fshelp.c
static grub_err_t directory_find_file (grub_fshelp_node_t node, const char *name,
grub_fshelp_node_t *foundnode, enum grub_fshelp_filetype *foundtype,
iterate_dir_func iterate_dir){
int found;
struct grub_fshelp_find_file_iter_ctx ctx = {
.foundnode = foundnode,
.foundtype = foundtype,
.name = name
};
iterate_dir (node, find_file_iter, &ctx);
return GRUB_ERR_NONE;
}
传入的函数指针iterate_dir为grub_ext2_iterate_dir,该函数遍历目录节点node下的目录结构dirent,找到name对应的文件,将结果保存在上下文ctx中并返回,传入的函数指针find_file_iter用于作匹配操作。
grub2-install->grub_fs_probe->grub_ext2_dir->grub_fshelp_find_file->grub_fshelp_find_file_real->directory_find_file->grub_ext2_iterate_dir
grub-core/fs/fshelp.c
static int grub_ext2_iterate_dir (grub_fshelp_node_t dir,
grub_fshelp_iterate_dir_hook_t hook, void *hook_data){
unsigned int fpos = 0;
struct grub_fshelp_node *diro = (struct grub_fshelp_node *) dir;
if (! diro->inode_read){
grub_ext2_read_inode (diro->data, diro->ino, &diro->inode);
}
while (fpos < grub_le_to_cpu32 (diro->inode.size)){
struct ext2_dirent dirent;
grub_ext2_read_file (diro, 0, 0, fpos, sizeof (struct ext2_dirent), (char *) &dirent);
if (dirent.direntlen == 0)
return 0;
if (dirent.inode != 0 && dirent.namelen != 0){
char filename[MAX_NAMELEN + 1];
struct grub_fshelp_node *fdiro;
enum grub_fshelp_filetype type = GRUB_FSHELP_UNKNOWN;
grub_ext2_read_file (diro, 0, 0, fpos + sizeof (struct ext2_dirent), dirent.namelen, filename);
fdiro = grub_malloc (sizeof (struct grub_fshelp_node));
fdiro->data = diro->data;
fdiro->ino = grub_le_to_cpu32 (dirent.inode);
filename[dirent.namelen] = '\0';
if (dirent.filetype != FILETYPE_UNKNOWN){
fdiro->inode_read = 0;
if (dirent.filetype == FILETYPE_DIRECTORY)
type = GRUB_FSHELP_DIR;
else if (dirent.filetype == FILETYPE_SYMLINK)
type = GRUB_FSHELP_SYMLINK;
else if (dirent.filetype == FILETYPE_REG)
type = GRUB_FSHELP_REG;
} else {
...
}
if (hook (filename, type, fdiro, hook_data))
return 1;
}
fpos += grub_le_to_cpu16 (dirent.direntlen);
}
return 0;
}
dir为当前目录对应的节点node,如果该节点对应的inode结构未读取,则通过grub_ext2_read_inode函数读取inode,该函数前面分析过了。接着通过fpos指针遍历目录,inode的size标识了该目录的大小,然后通过grub_ext2_read_file读取目录下每个文件的dirent结构,获取文件名长度信息namelen,然后根据namelen,再一次调用grub_ext2_read_file函数读取文件名,保存在filename中。
接着分配一个node结点,存储读取到的文件信息,然后设置读取到的文件类型。函数指针hook为find_file_iter函数,该函数匹配文件夹中的当前文件名filename和待查找的当前文件名name,如果找到了,则返回1 ,返回的hook_data中保存了新文件的信息,例如inode号等,如果没找到,则递增fpos,循环遍历下一个目录。
grub2-install->grub_fs_probe->grub_ext2_dir->grub_fshelp_find_file->grub_fshelp_find_file_real->directory_find_file->grub_ext2_iterate_dir->grub_ext2_read_file
grub-core/fs/ext2.c
static grub_ssize_t grub_ext2_read_file (grub_fshelp_node_t node,
grub_disk_read_hook_t read_hook, void *read_hook_data,
grub_off_t pos, grub_size_t len, char *buf){
return grub_fshelp_read_file (node->data->disk, node,
read_hook, read_hook_data,
pos, len, buf, grub_ext2_read_block,
grub_cpu_to_le32 (node->inode.size)
| (((grub_off_t) grub_cpu_to_le32 (node->inode.size_high)) << 32),
LOG2_EXT2_BLOCK_SIZE (node->data), 0);
}
grub_ssize_t grub_fshelp_read_file (grub_disk_t disk, grub_fshelp_node_t node,
grub_disk_read_hook_t read_hook, void *read_hook_data,
grub_off_t pos, grub_size_t len, char *buf,
grub_disk_addr_t (*get_block) (grub_fshelp_node_t node, grub_disk_addr_t block),
grub_off_t filesize, int log2blocksize,
grub_disk_addr_t blocks_start){
grub_disk_addr_t i, blockcnt;
int blocksize = 1 << (log2blocksize + GRUB_DISK_SECTOR_BITS);
if (pos > filesize){
return -1;
}
if (pos + len > filesize)
len = filesize - pos;
blockcnt = ((len + pos) + blocksize - 1) >> (log2blocksize + GRUB_DISK_SECTOR_BITS);
for (i = pos >> (log2blocksize + GRUB_DISK_SECTOR_BITS); i < blockcnt; i++){
grub_disk_addr_t blknr;
int blockoff = pos & (blocksize - 1);
int blockend = blocksize;
int skipfirst = 0;
blknr = get_block (node, i);
blknr = blknr << log2blocksize;
if (i == blockcnt - 1){
blockend = (len + pos) & (blocksize - 1);
if (! blockend)
blockend = blocksize;
}
if (i == (pos >> (log2blocksize + GRUB_DISK_SECTOR_BITS))){
skipfirst = blockoff;
blockend -= skipfirst;
}
if (blknr){
disk->read_hook = read_hook;
disk->read_hook_data = read_hook_data;
grub_disk_read (disk, blknr + blocks_start, skipfirst, blockend, buf);
disk->read_hook = 0;
}else
grub_memset (buf, 0, blockend);
buf += blocksize - skipfirst;
}
return len;
}
传入的参数pos表示当前inode读取时的相对位置,从0开始。传入的参数log2blocksize标识一个block占用的扇区数对应的位数,加上GRUB_DISK_SECTOR_BITS标识一个block对应的字节数占用的bit位数。参数filesize标识文件大小,len是本次读取的长度,即一个ext2_dirent结构的大小。
首先计算的blockcnt表示读取的结束位置位于哪个block,并对齐。接下来的for循环逐个读取block,blockoff和blockend表示读取的起始和结束的相对位置。函数指针get_block为grub_ext2_read_block,用于获取当前将要读取的是第几个block,注意这里是绝对的block数,左移log2blocksize获取对应的扇区数。
接下来的第一个if语句调整最后一次读取的block,使得读取长度取一个block的大小。第二个if语句判断如果是第一次读取block,则要调整读取的起始偏移skipfirst和第一个读取的长度blockend。再往下调用grub_disk_read执行具体的读取数据,注意如果是第0个block,则直接填充0即可。传入的read_hook和read_hook_data参数为空,这里不管它。
grub2-install->grub_fs_probe->grub_ext2_dir->grub_fshelp_find_file->grub_fshelp_find_file_real->directory_find_file->grub_ext2_iterate_dir->grub_ext2_read_file->grub_ext2_read_block
grub-core/fs/ext2.c
static grub_disk_addr_t grub_ext2_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock){
struct grub_ext2_data *data = node->data;
struct grub_ext2_inode *inode = &node->inode;
unsigned int blksz = EXT2_BLOCK_SIZE (data);
grub_disk_addr_t blksz_quarter = blksz / 4;
int log2_blksz = LOG2_EXT2_BLOCK_SIZE (data);
int log_perblock = log2_blksz + 9 - 2;
grub_uint32_t indir;
int shift;
if (inode->flags & grub_cpu_to_le32_compile_time (EXT4_EXTENTS_FLAG)){
...
}
if (fileblock < INDIRECT_BLOCKS)
return grub_le_to_cpu32 (inode->blocks.dir_blocks[fileblock]);
fileblock -= INDIRECT_BLOCKS;
if (fileblock < blksz_quarter){
indir = inode->blocks.indir_block;
shift = 0;
goto indirect;
}
fileblock -= blksz_quarter;
if (fileblock < blksz_quarter * blksz_quarter){
indir = inode->blocks.double_indir_block;
shift = 1;
goto indirect;
}
fileblock -= blksz_quarter * blksz_quarter;
if (fileblock < blksz_quarter * blksz_quarter * (blksz_quarter + 1)){
indir = inode->blocks.triple_indir_block;
shift = 2;
goto indirect;
}
indirect:
do {
if (indir == 0)
return 0;
if (grub_disk_read (data->disk,
((grub_disk_addr_t) grub_le_to_cpu32 (indir))
<< log2_blksz,
((fileblock >> (log_perblock * shift))
& ((1 << log_perblock) - 1))
* sizeof (indir),
sizeof (indir), &indir))
return -1;
} while (shift--);
return grub_le_to_cpu32 (indir);
}
传入的参数fileblock表示要读取的是第几个block。log2_blksz是每个block占用的扇区数,log_perblock表示当某个block是用于存放其他block的地址时,每个block可以存储多少个block的地址,加上9将扇区数转化为字节数,减去2是因为每4个字节用于存放一个地址。inode的block数组指向文件数据所在的block。其中前INDIRECT_BLOCKS(默认为12)个block是可以直接访问的,后面的(默认情况下为3)block是要简介访问的,unix这么划分是出于性能的考虑。
如果要读取的数据在前12个block里,则直接获取,并返回。前12个block叫做data block。第13个block为indirect block,存储了data block的地址,其中blksz_quarter为一个block的大小除以4,因为每4字节用来保存地址,因此对于1KB大小的block,可以存储256个block地址。第14个block为doubly-indirect block,存储了indirect block的地址,类似的第15个block叫做triply indirect block,存储了doubly-indirect block的地址。
根据不同的block类型,计算的shift变量标识要读几次才能获得想要的数据,例如对于indirect block,需要读一次,对于doubly-indirect block,就需要读两次。接下来就根据该原理循环调用grub_disk_read读取数据,indir存储了标识当前读取的是第几个block,左移log2_blksz表示起始的扇区数绝对偏移。
grub2-install->grub_fs_probe->grub_ext2_dir->grub_fshelp_find_file->grub_fshelp_find_file_real->directory_find_file->grub_ext2_iterate_dir->find_file_iter
grub-core/fs/fshelp.c
static int find_file_iter (const char *filename, enum grub_fshelp_filetype filetype, grub_fshelp_node_t node, void *data){
struct grub_fshelp_find_file_iter_ctx *ctx = data;
if (filetype == GRUB_FSHELP_UNKNOWN || ((filetype & GRUB_FSHELP_CASE_INSENSITIVE) ? grub_strcasecmp (ctx->name, filename) : grub_strcmp (ctx->name, filename))){
grub_free (node);
return 0;
}
*ctx->foundnode = node;
*ctx->foundtype = filetype;
return 1;
}
该函数首先比较文件名,检查遍历的文件filename是否就是要查找的文件name,如果不是则释放结点,返回0,如果找到了,则将结果存入上下文ctx中,其中包括文件节点foundnode和文件类型foundtype,并返回1。
grub2-install->grub_fs_probe->grub_ext2_dir->…->grub_ext2_dir_iter
grub-core/fs/ext2.c
static int grub_ext2_dir_iter (const char *filename, enum grub_fshelp_filetype filetype, grub_fshelp_node_t node, void *data){
struct grub_ext2_dir_ctx *ctx = data;
struct grub_dirhook_info info;
grub_memset (&info, 0, sizeof (info));
if (! node->inode_read){
grub_ext2_read_inode (ctx->data, node->ino, &node->inode);
if (!grub_errno)
node->inode_read = 1;
grub_errno = GRUB_ERR_NONE;
}
if (node->inode_read){
info.mtimeset = 1;
info.mtime = grub_le_to_cpu32 (node->inode.mtime);
}
info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
grub_free (node);
return ctx->hook (filename, &info, ctx->hook_data);
}
如果还未读取inode,就通过grub_ext2_read_inode函数读取inode,并将inode_read置1。接下来修改inode的mtime字段,表示该文件的修改时间。上下文中的hook函数为probe_dummy_iter函数指针,该函数直接返回1。
grub2-install->grub_install_push_module
util/grub-install-common.c
void grub_install_push_module (const char *val){
modules.is_default = 0;
if (modules.n_entries + 1 >= modules.n_alloc){
modules.n_alloc <<= 1;
if (modules.n_alloc < 16)
modules.n_alloc = 16;
modules.entries = xrealloc (modules.entries, modules.n_alloc * sizeof (*modules.entries));
}
modules.entries[modules.n_entries++] = xstrdup (val);
modules.entries[modules.n_entries] = NULL;
}
grub_install_push_module函数只是将模块名存入全局的modules变量的entries数组中,其中n_entries表示当前entries数组存储的长度,n_alloc表示数组的总长度,if语句判断数组是否已满,如果已满,就将数组的长度扩大一倍。