grub-install源码分析---1

grub-install源码分析—1

本章开始分析grub-install的源码,该命令最终会将grub的映像存入引导扇区中,最简单的命令如下,

grub2-install /dev/sda

下面从main函数开始看,有一部分代码已经在《grub-mkimage源码分析》中分析了,本章包括后面的章节对这些代码就直接略过了。

grub2-install第一部分
util/grub-install.c

int main (int argc, char *argv[]){
    int is_efi = 0;
    const char *efi_distributor = NULL;
    const char *efi_file = NULL;
    char **grub_devices;
    grub_fs_t grub_fs;
    grub_device_t grub_dev = NULL;
    enum grub_install_plat platform;
    char *grubdir, *device_map;
    char **curdev, **curdrive;
    char **grub_drives;
    char *relative_grubdir;
    char **efidir_device_names = NULL;
    grub_device_t efidir_grub_dev = NULL;
    char *efidir_grub_devname;
    int efidir_is_mac = 0;
    int is_prep = 0;
    const char *pkgdatadir;

    grub_util_host_init (&argc, &argv);
    product_version = xstrdup (PACKAGE_VERSION);
    pkgdatadir = grub_util_get_pkgdatadir ();
    label_font = grub_util_path_concat (2, pkgdatadir, "unicode.pf2");

    argp_parse (&argp, argc, argv, 0, 0, 0);
    grub_util_load_config (&config);

    ...

grub_util_host_init设置全局的应用程序名,product_version表示grub的版本,本文使用的为2.02版本,grub_util_get_pkgdatadir函数默认返回的路径”/usr/local/share/grub”,该目录下存放了grub的数据文件,例如编码文件unicode.pf2。

argp_parse函数用于解析命令行的参数,和《grub-mkimage源码分析》中的分析的不同之处在于,其解析结果主要保存在一些全局变量中。

最后的grub_util_load_config函数用于解析配置文件,将结果保存在参数config中。

grub2-install->grub_util_load_config
grub-core/osdep/unix/config.c

void grub_util_load_config (struct grub_util_config *cfg) {
    pid_t pid;
    const char *argv[4];
    char *script, *ptr;
    const char *cfgfile, *iptr;
    FILE *f = NULL;
    int fd;
    const char *v;

    memset (cfg, 0, sizeof (*cfg));

    v = getenv ("GRUB_ENABLE_CRYPTODISK");
    if (v && v[0] == 'y' && v[1] == '\0')
        cfg->is_cryptodisk_enabled = 1;

    v = getenv ("GRUB_DISTRIBUTOR");
    if (v)
        cfg->grub_distributor = xstrdup (v);

    cfgfile = grub_util_get_config_filename ();

    argv[0] = "sh";
    argv[1] = "-c";

    script = xmalloc (4 * strlen (cfgfile) + 300);

    ptr = script;
    memcpy (ptr, ". '", 3);
    ptr += 3;
    for (iptr = cfgfile; *iptr; iptr++) {
        if (*iptr == '\\') {
            memcpy (ptr, "'\\''", 4);
            ptr += 4;
            continue;
        }
        *ptr++ = *iptr;
    }

    strcpy (ptr, "'; printf \"GRUB_ENABLE_CRYPTODISK=%s\\nGRUB_DISTRIBUTOR=%s\\n\" "
            "\"$GRUB_ENABLE_CRYPTODISK\" \"$GRUB_DISTRIBUTOR\"");

    argv[2] = script;
    argv[3] = '\0';

    pid = grub_util_exec_pipe (argv, &fd);
    if (pid)
        f = fdopen (fd, "r");
    if (f) {
        grub_util_parse_config (f, cfg, 1);
        fclose (f);
    }
    if (pid) {
        close (fd);
        waitpid (pid, NULL, 0);
    }
    if (f)
        return;

    f = grub_util_fopen (cfgfile, "r");
    if (f) {
        grub_util_parse_config (f, cfg, 0);
        fclose (f);
    }
}

首先读取环境变量GRUB_ENABLE_CRYPTODISK和GRUB_DISTRIBUTOR设置返回参数cfg相应的值。grub_util_get_config_filename获得默认的配置文件路径”/usr/local/etc/default/grub”。

接下来分配内存空间script,用于存放脚本命令。接下来首先存入的”.”表示会逐行执行文件中的每行脚本命令。然后通过for循环拷贝配置文件路径cfgfile到script中,再将GRUB_ENABLE_CRYPTODISK和GRUB_DISTRIBUTOR的信息也添加到脚本命令中。完成后,就通过grub_util_exec_pipe函数执行刚刚编写的脚本script,参数fd指向的文件用于保存返回结果。

执行完成后以只读方式打开该文件,调用grub_util_parse_config函数对执行结果进行解析并将结果写入cfg中。
grub_util_parse_config是主要的解析函数。其中waitpid用于等待grub_util_exec_pipe函数中子进程的返回。

如果前面的方法失败了,则可能配置文件中内容并不是脚本,而是可读的配置条目,此时打开该文件,并直接通过grub_util_parse_config函数解析其中的配置条目。

grub2-install->grub_util_load_config->grub_util_exec_pipe
grub-core/osdep/unix/exec.c

pid_t grub_util_exec_pipe (const char *const *argv, int *fd) {
    int pipe_fd[2];
    pid_t pid;

    *fd = 0;

    pipe (pipe_fd);
    pid = fork ();

    if (pid == 0) {
        setenv ("LC_ALL", "C", 1);

        close (pipe_fd[0]);
        dup2 (pipe_fd[1], STDOUT_FILENO);
        close (pipe_fd[1]);

        execvp ((char *) argv[0], (char **) argv);
        exit (127);
    } else {
        close (pipe_fd[1]);
        *fd = pipe_fd[0];
        return pid;
    }
}

传入的参数argv[0]为sh,argv[1]为-c,表示后续的字符串为sh可以执行的脚本指令。首先创建管道pipe_fd,调用fork函数创建子进程,在子进程中首先关闭管道的读端pipe_fd[0],复制写端到标准输出描述符STDOUT_FILENO上,再关闭写端pipe_fd[1](这里未真正关闭),然后通过execvp执行sh脚本,也即argv[2]中字符串表示的脚本命令,其结果会写入标准输出描述符上,也即pipe_fd[1],执行成功后,退出子进程。父进程首先关闭管道的写端pipe_fd[1],将参数fd指向管道的读端,最后返回父进程ID。

grub2-install->grub_util_load_config->grub_util_parse_config
util/config.c

void grub_util_parse_config (FILE *f, struct grub_util_config *cfg, int simple) {
    char *buffer = NULL;
    size_t sz = 0;
    while (getline (&buffer, &sz, f) >= 0) {
        const char *ptr;
        for (ptr = buffer; *ptr && grub_isspace (*ptr); ptr++);
        if (grub_strncmp (ptr, "GRUB_ENABLE_CRYPTODISK=",
            sizeof ("GRUB_ENABLE_CRYPTODISK=") - 1) == 0) {
            ptr += sizeof ("GRUB_ENABLE_CRYPTODISK=") - 1;
            if (*ptr == '"' || *ptr == '\'')
                ptr++;
            if (*ptr == 'y')
                cfg->is_cryptodisk_enabled = 1;
            continue;
        }

        if (grub_strncmp (ptr, "GRUB_DISTRIBUTOR=", sizeof ("GRUB_DISTRIBUTOR=") - 1) == 0) {
            char *optr;
            enum { NONE, SNGLQUOT, DBLQUOT } state;

            ptr += sizeof ("GRUB_DISTRIBUTOR=") - 1;

            if (simple) {
                char *ptr2;
                free (cfg->grub_distributor);
                cfg->grub_distributor = xstrdup (ptr);
                for (ptr2 = cfg->grub_distributor + grub_strlen (cfg->grub_distributor) - 1;
                    ptr2 >= cfg->grub_distributor && (*ptr2 == '\r' || *ptr2 == '\n'); ptr2--);
                ptr2[1] = '\0';
                continue;
            }
            ...
        }
    }
}

首先通过while循环逐行读取文件中的内容至buffer中,第一个for循环利用grub_isspace函数删除该行中的无效内容,例如空格,换行符等等。再往下的第一个if语句判断如果该行是例如GRUB_ENABLE_CRYPTODISK=”y”,则覆盖之前环境变量的配置,将is_cryptodisk_enabled设为1。第二个if语句类似,将”GRUB_DISTRIBUTOR=”后面跟着的字符串拷贝到grub_distributor中。假设传入的simple为1,内部的for循环用于将结尾的换行符替换成结束符’\0’,如果传入的simple为0,表示解析过程较为复杂,需要通过脚本命令格式解析最终的内容,这里省略了这部分代码。

grub2-install第二部分
util/grub-install.c

    ...

    if (!bootloader_id && config.grub_distributor) {
        char *ptr;
        bootloader_id = xstrdup (config.grub_distributor);
        for (ptr = bootloader_id; *ptr && *ptr != ' '; ptr++)
            if (*ptr >= 'A' && *ptr <= 'Z')
                *ptr = *ptr - 'A' + 'a';

        *ptr = '\0';
    }

    if (!bootloader_id || bootloader_id[0] == '\0') {
        free (bootloader_id);
        bootloader_id = xstrdup ("grub");
    }

    if (!grub_install_source_directory) {
        if (!target) {
            const char * t;
            t = get_default_platform ();
            target = xstrdup (t); 
        }
        grub_install_source_directory = grub_util_path_concat (2, 
                                            grub_util_get_pkglibdir (), target);
    }

    platform = grub_install_get_target (grub_install_source_directory);

    switch (platform) {
    case GRUB_INSTALL_PLATFORM_I386_PC:
        if (!disk_module)
            disk_module = xstrdup ("biosdisk");
        break;
    ...

    }

    switch (platform) {
        case GRUB_INSTALL_PLATFORM_I386_PC:
        case GRUB_INSTALL_PLATFORM_SPARC64_IEEE1275:
            if (!install_device)
                grub_util_error ("%s", _("install device isn't specified"));
            break;
    ...

    }

    ...

如果config中的grub_distributor存在,就将其设置为bootloader_id,并将大写转换为小写,否则设置默认值为grub。bootloader_id和efi接口相关,这里不过多关心。

接下里假设grub_install_source_directory和target输入参数都未提供,此时通过get_default_platform返回平台信息,对于i386体系,返回”i386-pc”字符串。grub_util_get_pkglibdir函数最终返回”/usr/local/lib/grub”,因此最后的grub_install_source_directory被设置为”/usr/local/lib/grub/i386-pc”。

grub_install_get_target获得cpu和platform对应的索引,对于i386,返回GRUB_INSTALL_PLATFORM_I386_PC。再往下,如果参数未提供,就设置disk_module为”biosdisk”,install_device默认由参数提供,例如”/dev/sda”。

grub2-install->grub_install_get_target
util/grub-install-common.c

enum grub_install_plat grub_install_get_target (const char *src) {
    char *fn;
    grub_util_fd_t f;
    char buf[8192];
    ssize_t r;
    char *c, *pl, *p;
    size_t i;

    fn = grub_util_path_concat (2, src, "modinfo.sh");
    f = grub_util_fd_open (fn, GRUB_UTIL_FD_O_RDONLY);

    r = grub_util_fd_read (f, buf, sizeof (buf) - 1);
    grub_util_fd_close (f);
    buf[r] = '\0';

    c = strstr (buf, "grub_modinfo_target_cpu=");
    pl = strstr (buf, "grub_modinfo_platform=");

    c += sizeof ("grub_modinfo_target_cpu=") - 1;
    pl += sizeof ("grub_modinfo_platform=") - 1;
    for (p = c; *p && !grub_isspace (*p); p++);
        *p = '\0';
    for (p = pl; *p && !grub_isspace (*p); p++);
        *p = '\0';

    for (i = 0; i < ARRAY_SIZE (platforms); i++)
        if (strcmp (platforms[i].cpu, c) == 0 && strcmp (platforms[i].platform, pl) == 0) {
            free (fn);
            return i;
        }
}

对于i386而言,这里传入的参数src为”/usr/local/lib/grub/i386-pc”,接下来通过grub_util_fd_open函数打开文件”/usr/local/lib/grub/i386-pc/modinfo.sh”,并通过grub_util_fd_read函数将其读入内存buf中。然后通过strstr函数查找”grub_modinfo_target_cpu=”和”grub_modinfo_platform=”所在位置,进而获得等号后面跟着的值,分别赋值给变量c和pl,最后通过for循环在platforms数组中比较两者的值,如果相等,就返回数组的索引。platforms的定义如下,

static struct
{
  const char *cpu;
  const char *platform;
} platforms[GRUB_INSTALL_PLATFORM_MAX] =
  {
    [GRUB_INSTALL_PLATFORM_I386_PC] =          { "i386",    "pc"        },
    [GRUB_INSTALL_PLATFORM_I386_EFI] =         { "i386",    "efi"       },
    [GRUB_INSTALL_PLATFORM_I386_QEMU] =        { "i386",    "qemu"      },
    [GRUB_INSTALL_PLATFORM_I386_COREBOOT] =    { "i386",    "coreboot"  },
    [GRUB_INSTALL_PLATFORM_I386_MULTIBOOT] =   { "i386",    "multiboot" },
    [GRUB_INSTALL_PLATFORM_I386_IEEE1275] =    { "i386",    "ieee1275"  },
    [GRUB_INSTALL_PLATFORM_X86_64_EFI] =       { "x86_64",  "efi"       },
    [GRUB_INSTALL_PLATFORM_I386_XEN] =         { "i386",    "xen"       },
    [GRUB_INSTALL_PLATFORM_X86_64_XEN] =       { "x86_64",  "xen"       },
    [GRUB_INSTALL_PLATFORM_MIPSEL_LOONGSON] =  { "mipsel",  "loongson"  },
    [GRUB_INSTALL_PLATFORM_MIPSEL_QEMU_MIPS] = { "mipsel",  "qemu_mips" },
    [GRUB_INSTALL_PLATFORM_MIPS_QEMU_MIPS] =   { "mips",    "qemu_mips" },
    [GRUB_INSTALL_PLATFORM_MIPSEL_ARC] =       { "mipsel",  "arc"       },
    [GRUB_INSTALL_PLATFORM_MIPS_ARC] =         { "mips",    "arc"       },
    [GRUB_INSTALL_PLATFORM_SPARC64_IEEE1275] = { "sparc64", "ieee1275"  },
    [GRUB_INSTALL_PLATFORM_POWERPC_IEEE1275] = { "powerpc", "ieee1275"  },
    [GRUB_INSTALL_PLATFORM_IA64_EFI] =         { "ia64",    "efi"       },
    [GRUB_INSTALL_PLATFORM_ARM_EFI] =          { "arm",     "efi"       },
    [GRUB_INSTALL_PLATFORM_ARM64_EFI] =        { "arm64",   "efi"       },
    [GRUB_INSTALL_PLATFORM_ARM_UBOOT] =        { "arm",     "uboot"     },
  }; 

因此对于i386-pc而言,最后返回的数组索引为GRUB_INSTALL_PLATFORM_I386_PC。

grub2-install第三部分
util/grub-install.c

    ...

    if (!bootdir)
        bootdir = grub_util_path_concat (3, "/", rootdir, GRUB_BOOT_DIR_NAME);


    char * t = grub_util_path_concat (2, bootdir, GRUB_DIR_NAME);
    grub_install_mkdir_p (t);
    grubdir = grub_canonicalize_file_name (t);
    free (t);

    device_map = grub_util_path_concat (2, grubdir, "device.map");
    device_map_check_duplicates (device_map);
    grub_util_biosdisk_init (device_map);

    ...

    grub_install_copy_files (grub_install_source_directory,
                        grubdir, platform);

    char *envfile = grub_util_path_concat (2, grubdir, "grubenv");
    if (!grub_util_is_regular (envfile))
        grub_util_create_envblk_file (envfile);  

    ...

首先,如果rootdir未提供,则默认的grub根路径bootdir为”/boot”。根路径下grub文件所在的目录t默认为”/boot/grub”,grub_install_mkdir_p检查”/boot/grub”文件夹是否存在,如果不存在则创建。

接下来获取grub目录”/boot/grub”下的device_map文件,该文件存储了设备信息,然后通过device_map_check_duplicates函数检查device.map文件中是否有重复定义,该函数读取device.map文件,并按字母排序,比较前后两个信息是否相同,删除多余的信息。检查完毕后,通过grub_util_biosdisk_init函数再次读取device_map文件,将信息保存到全局变量中。

往下省略了部分初始化函数。

根据前面的分析,对于i386体系而言,接下来的grub_install_source_directory被赋值为”/usr/local/lib/grub/i386-pc”,grub目录grubdir为”/boot/grub”,platform是在grub_install_get_target函数中获得的数组索引GRUB_INSTALL_PLATFORM_I386_PC,grub_install_copy_files函数传入这些参数,将”/usr/local”目录下的相关文件拷贝到”/boot/grub”目录中。

最后通过grub_util_create_envblk_file函数创建”/boot/grub/grubenv.new”文件,该文件包含1024个字节,对其进行简单数据的初始化,其实就是加了头部的注释在该文件中。

grub2-install->grub_util_biosdisk_init
grub-core/kern/emu/hostdisk.c

void grub_util_biosdisk_init (const char *dev_map) {
    read_device_map (dev_map);
    grub_disk_dev_register (&grub_util_biosdisk_dev);
}

首先通过read_device_map函数读取device.map文件dev_map,将驱动号和设备号存入全局的map数组中。然后通过grub_disk_dev_register函数将grub_util_biosdisk_dev插入到全局的grub_disk_dev_list链表中。

grub2-install->grub_util_biosdisk_init->read_device_map
grub-core/kern/emu/hostdisk.c

static void read_device_map (const char *dev_map) {
    FILE *fp;
    char buf[1024];
    int lineno = 0;

    fp = grub_util_fopen (dev_map, "r");

    while (fgets (buf, sizeof (buf), fp)) {
        char *p = buf;
        char *e;
        char *drive_e, *drive_p;
        int drive;

        lineno++;

        while (*p && grub_isspace (*p))
            p++;

        if (*p == '\0' || *p == '#')
            continue;

        p++;
        drive = find_free_slot ();

        e = p;
        p = strchr (p, ')');

        map[drive].drive = 0;

        if ((e[0] == 'f' || e[0] == 'h' || e[0] == 'c') && e[1] == 'd') {
            char *ptr;
            for (ptr = e + 2; ptr < p; ptr++)
                if (!grub_isdigit (*ptr))
                    break;

            if (ptr == p) {
                map[drive].drive = xmalloc (p - e + sizeof ('\0'));
                strncpy (map[drive].drive, e, p - e + sizeof ('\0'));
                map[drive].drive[p - e] = '\0';
            }
        }

        drive_e = e;
        drive_p = p;
        map[drive].device_map = 1;

        p++;
        while (*p && grub_isspace (*p))
            p++;

        e = p;
        while (*e && ! grub_isspace (*e))
            e++;
        *e = '\0';

        map[drive].device = grub_canonicalize_file_name (p);
        if (! map[drive].device)
            map[drive].device = xstrdup (p);

        ...
    }

    fclose (fp);
}

为了方便后面的分析,这里举个device.map文件的例子,如下所示,

(fd0) /dev/fd0
(hd0) /dev/hda
(hd1) /dev/hdb
(hd2) /dev/sda
(hd3) /dev/sdb

打开该文件后,通过while循环逐行读取文件中的内容,然后通过grub_isspace函数找到第一个有效字符,第一个有效字符为左括号,因此需要将p指针递增一个位置。再通过find_free_slot函数在全局的map数组中找到空闲位置并返回对应的索引drive,find_free_slot函数从左到右遍历map数组,返回数组中第一个drive字段不为0的元素下标。

如果字符串e以”fd”、”hd”、”cd”开头,并紧跟着以数字结尾,则将该硬盘号存入全局数组map中的drive字段中,例如第一块硬盘hd0。其中grub_isdigit函数用于判断字符是否为数字。

设置完设备号后,继续读取文件,找到右扩号后的第一个有效字符,拷贝后续的文件名到map数组的device字段中,该文件名就是设备文件名,例如”/dev/hda”。最后省略的代码和hostdisk有关,这里不关心。

grub2-install->grub_install_copy_files
util/grub-install-common.c

void grub_install_copy_files (const char *src, const char *dst, enum grub_install_plat platid) {

    char *dst_platform, *dst_locale, *dst_fonts;
    const char *pkgdatadir = grub_util_get_pkgdatadir ();
    char *themes_dir;

    char *platform;
    platform = xasprintf ("%s-%s", platforms[platid].cpu,
              platforms[platid].platform);
    dst_platform = grub_util_path_concat (2, dst, platform);
    free (platform);

    dst_locale = grub_util_path_concat (2, dst, "locale");
    dst_fonts = grub_util_path_concat (2, dst, "fonts");
    grub_install_mkdir_p (dst_platform);
    grub_install_mkdir_p (dst_locale);
    clean_grub_dir (dst);
    clean_grub_dir (dst_platform);
    clean_grub_dir (dst_locale);

    if (install_modules.is_default)
        copy_by_ext (src, dst_platform, ".mod", 1);
    else{
        ...
    }

    const char *pkglib_DATA[] = {"efiemu32.o", "efiemu64.o", "moddep.lst", "command.lst",
                                "fs.lst", "partmap.lst", "parttool.lst", "video.lst", 
                                "crypto.lst", "terminal.lst", "modinfo.sh" };
    size_t i;

    for (i = 0; i < ARRAY_SIZE (pkglib_DATA); i++) {
        char *srcf = grub_util_path_concat (2, src, pkglib_DATA[i]);
        char *dstf = grub_util_path_concat (2, dst_platform, pkglib_DATA[i]);
        if (i == 0 || i == 1)
            grub_install_compress_file (srcf, dstf, 0);
        else
            grub_install_compress_file (srcf, dstf, 1);
        free (srcf);
        free (dstf);
    }

    if (install_locales.is_default) {
        char *srcd = grub_util_path_concat (2, src, "po");
        copy_by_ext (srcd, dst_locale, ".mo", 0);
        copy_locales (dst_locale);
        free (srcd);
    } else {
        ...
    }

    if (install_themes.is_default){
        install_themes.is_default = 0;
        install_themes.n_entries = 1;
        install_themes.entries = xmalloc (2 * sizeof (install_themes.entries[0]));
        install_themes.entries[0] = xstrdup ("starfield");
        install_themes.entries[1] = NULL;
    }

    themes_dir = grub_util_path_concat (2, grub_util_get_pkgdatadir (),
                    "themes");

    for (i = 0; i < install_themes.n_entries; i++) {
        char *srcf = grub_util_path_concat (3, themes_dir, install_themes.entries[i], "theme.txt");
        if (grub_util_is_regular (srcf)) {
            char *srcd = grub_util_path_concat (2, themes_dir, install_themes.entries[i]);
            char *dstd = grub_util_path_concat (3, dst, "themes", install_themes.entries[i]);
            grub_install_mkdir_p (dstd);
            copy_all (srcd, dstd);
            free (srcd);
            free (dstd);
        }
        free (srcf);
    }

    free (themes_dir);

    if (install_fonts.is_default) {
        install_fonts.is_default = 0;
        install_fonts.n_entries = 1;
        install_fonts.entries = xmalloc (2 * sizeof (install_fonts.entries[0]));
        install_fonts.entries[0] = xstrdup ("unicode");
        install_fonts.entries[1] = NULL;
    }

    grub_install_mkdir_p (dst_fonts);

    for (i = 0; i < install_fonts.n_entries; i++) {
        char *srcf = grub_util_path_concat_ext (2, pkgdatadir, install_fonts.entries[i], ".pf2");
        char *dstf = grub_util_path_concat_ext (2, dst_fonts, install_fonts.entries[i], ".pf2");

        grub_install_compress_file (srcf, dstf, 0);
        free (srcf);
        free (dstf);
    }

    free (dst_platform);
    free (dst_locale);
    free (dst_fonts);
}

grub_util_get_pkgdatadir函数默认返回的路径”/usr/local/share/grub”,传入的参数platid为数组platforms的索引值,由前面的grub_install_get_target函数计算为GRUB_INSTALL_PLATFORM_I386_PC,因此这里最终计算的platform就为i386-pc。获得platform路径后,再拼接成目标地址dst_platform为”/boot/grub/i386-pc”。依次类推,dst_locale为”/boot/grub/locale”,dst_fonts为”/boot/grub/fonts”。然后通过grub_install_mkdir_p函数检查并创建这些文件目录,通过clean_grub_dir清空文件夹下带特定拓展名的文件,例如.img、.mod文件等等。

接下来的install_modules可以在命令行中改变,默认定义如下,

struct install_list install_modules = { 1, 0, 0, 0 };
struct install_list
{
  int is_default;
  char **entries;
  size_t n_entries;
  size_t n_alloc;
};

其is_default字段默认值为1,因此通过copy_by_ext函数将src也即”/usr/local/lib/grub/i386-pc”目录下的以”.mod”为拓展名的文件拷贝到dst_platform也即”/boot/grub/i386-pc”目录中。然后遍历pkglib_DATA数组中的文件名,依次通过grub_install_compress_file函数将文件从src目录拷贝到dst_platform目录中,其内部通过grub_install_copy_file函数进行单个文件的拷贝操作,传入的最后一个参数为1表示如果存在压缩函数,需要对数据进行压缩,并且必须拷贝。

再往下的install_locales定义和install_modules一样,is_default字段默认值为1,因此接下来将”usr/local/lib/grub/i386-pc/po”目录下的所有以”.mo”为拓展名的文件拷贝到”/boot/grub/locale”目录中,最后调用的copy_locales函数会将”/boot/grub/locale”中的文件更名再拷贝到”/usr/local/share/locale”目录中。

install_themes和install_modules的定义也类似,字段is_default默认值为1,因此接下来初始化该结构,目录themes_dir最终拼接为”/usr/local/share/grub/themes”,然后将该目录下的starfield目录中的所有内容拷贝到”/boot/grub/themes/starfield”中。

install_fonts和install_themes也类似,最终将”/usr/local/share/grub/unicode/.pf2”文件拷贝到”/boot/grub/fonts/.ps2”文件中。

你可能感兴趣的:(glibc+linux源码分析,linux逆向编程)