/* * GRUB -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010,2011,2012,2013 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GRUB is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GRUB. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include char * grub_install_help_filter (int key, const char *text, void *input __attribute__ ((unused))) { switch (key) { case GRUB_INSTALL_OPTIONS_INSTALL_THEMES: return xasprintf(text, "starfield"); case GRUB_INSTALL_OPTIONS_INSTALL_FONTS: return xasprintf(text, "unicode"); case GRUB_INSTALL_OPTIONS_DIRECTORY: case GRUB_INSTALL_OPTIONS_DIRECTORY2: return xasprintf(text, grub_util_get_pkglibdir ()); case GRUB_INSTALL_OPTIONS_LOCALE_DIRECTORY: return xasprintf(text, grub_util_get_localedir ()); default: return (char *) text; } } static int (*compress_func) (const char *src, const char *dest) = NULL; char *grub_install_copy_buffer; int grub_install_copy_file (const char *src, const char *dst, int is_needed) { grub_util_fd_t in, out; ssize_t r; grub_util_info ("copying `%s' -> `%s'", src, dst); in = grub_util_fd_open (src, GRUB_UTIL_FD_O_RDONLY); if (!GRUB_UTIL_FD_IS_VALID (in)) { if (is_needed) grub_util_error (_("cannot open `%s': %s"), src, grub_util_fd_strerror ()); else grub_util_info (_("cannot open `%s': %s"), src, grub_util_fd_strerror ()); return 0; } out = grub_util_fd_open (dst, GRUB_UTIL_FD_O_WRONLY | GRUB_UTIL_FD_O_CREATTRUNC); if (!GRUB_UTIL_FD_IS_VALID (out)) { grub_util_error (_("cannot open `%s': %s"), dst, grub_util_fd_strerror ()); grub_util_fd_close (in); return 0; } if (!grub_install_copy_buffer) grub_install_copy_buffer = xmalloc (GRUB_INSTALL_COPY_BUFFER_SIZE); while (1) { r = grub_util_fd_read (in, grub_install_copy_buffer, GRUB_INSTALL_COPY_BUFFER_SIZE); if (r <= 0) break; grub_util_fd_write (out, grub_install_copy_buffer, r); } grub_util_fd_sync (out); grub_util_fd_close (in); grub_util_fd_close (out); if (r < 0) grub_util_error ("cannot copy `%s' to `%s': %s", src, dst, grub_util_fd_strerror ()); return 1; } int grub_install_compress_file (const char *in_name, const char *out_name, int is_needed) { int ret; if (!compress_func) ret = grub_install_copy_file (in_name, out_name, is_needed); else { grub_util_info ("compressing `%s' -> `%s'", in_name, out_name); ret = !compress_func (in_name, out_name); if (!ret && is_needed) grub_util_warn ("can't compress `%s' to `%s'", in_name, out_name); } if (!ret && is_needed) grub_util_error ("cannot copy `%s' to `%s': %s", in_name, out_name, grub_util_fd_strerror ()); return ret; } static int is_path_separator (char c) { #if defined (__MINGW32__) || defined (__CYGWIN__) if (c == '\\') return 1; #endif if (c == '/') return 1; return 0; } void grub_install_mkdir_p (const char *dst) { char *t = xstrdup (dst); char *p; for (p = t; *p; p++) { if (is_path_separator (*p)) { char s = *p; *p = '\0'; grub_util_mkdir (t); *p = s; } } grub_util_mkdir (t); free (t); } static void clean_grub_dir (const char *di) { grub_util_fd_dir_t d; grub_util_fd_dirent_t de; d = grub_util_fd_opendir (di); if (!d) grub_util_error (_("cannot open directory `%s': %s"), di, grub_util_fd_strerror ()); while ((de = grub_util_fd_readdir (d))) { const char *ext = strrchr (de->d_name, '.'); if ((ext && (strcmp (ext, ".mod") == 0 || strcmp (ext, ".lst") == 0 || strcmp (ext, ".img") == 0 || strcmp (ext, ".mo") == 0) && strcmp (de->d_name, "menu.lst") != 0) || strcmp (de->d_name, "efiemu32.o") == 0 || strcmp (de->d_name, "efiemu64.o") == 0) { char *x = grub_util_path_concat (2, di, de->d_name); if (grub_util_unlink (x) < 0) grub_util_error ("cannont delete `%s': %s", x, grub_util_fd_strerror ()); free (x); } } grub_util_fd_closedir (d); } struct install_list { int is_default; char **entries; size_t n_entries; size_t n_alloc; }; struct install_list install_modules = { 1, 0, 0, 0 }; struct install_list modules = { 1, 0, 0, 0 }; struct install_list install_locales = { 1, 0, 0, 0 }; struct install_list install_fonts = { 1, 0, 0, 0 }; struct install_list install_themes = { 1, 0, 0, 0 }; char *grub_install_source_directory = NULL; char *grub_install_locale_directory = NULL; 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; } void grub_install_pop_module (void) { modules.n_entries--; free (modules.entries[modules.n_entries]); modules.entries[modules.n_entries] = NULL; } static void handle_install_list (struct install_list *il, const char *val, int default_all) { const char *ptr; char **ce; il->is_default = 0; free (il->entries); il->entries = NULL; il->n_entries = 0; if (strcmp (val, "all") == 0 && default_all) { il->is_default = 1; return; } ptr = val; while (1) { while (*ptr && grub_isspace (*ptr)) ptr++; if (!*ptr) break; while (*ptr && !grub_isspace (*ptr)) ptr++; il->n_entries++; } il->n_alloc = il->n_entries + 1; il->entries = xmalloc (il->n_alloc * sizeof (il->entries[0])); ptr = val; for (ce = il->entries; ; ce++) { const char *bptr; while (*ptr && grub_isspace (*ptr)) ptr++; if (!*ptr) break; bptr = ptr; while (*ptr && !grub_isspace (*ptr)) ptr++; *ce = xmalloc (ptr - bptr + 1); memcpy (*ce, bptr, ptr - bptr); (*ce)[ptr - bptr] = '\0'; } *ce = NULL; } static char **pubkeys; static size_t npubkeys; int grub_install_parse (int key, char *arg) { switch (key) { case 'k': pubkeys = xrealloc (pubkeys, sizeof (pubkeys[0]) * (npubkeys + 1)); pubkeys[npubkeys++] = xstrdup (arg); return 1; case GRUB_INSTALL_OPTIONS_VERBOSITY: verbosity++; return 1; case GRUB_INSTALL_OPTIONS_DIRECTORY: case GRUB_INSTALL_OPTIONS_DIRECTORY2: free (grub_install_source_directory); grub_install_source_directory = xstrdup (arg); return 1; case GRUB_INSTALL_OPTIONS_LOCALE_DIRECTORY: free (grub_install_locale_directory); grub_install_locale_directory = xstrdup (arg); return 1; case GRUB_INSTALL_OPTIONS_INSTALL_MODULES: handle_install_list (&install_modules, arg, 0); return 1; case GRUB_INSTALL_OPTIONS_MODULES: handle_install_list (&modules, arg, 0); return 1; case GRUB_INSTALL_OPTIONS_INSTALL_LOCALES: handle_install_list (&install_locales, arg, 0); return 1; case GRUB_INSTALL_OPTIONS_INSTALL_THEMES: handle_install_list (&install_themes, arg, 0); return 1; case GRUB_INSTALL_OPTIONS_INSTALL_FONTS: handle_install_list (&install_fonts, arg, 0); return 1; case GRUB_INSTALL_OPTIONS_INSTALL_COMPRESS: if (strcmp (arg, "no") == 0 || strcmp (arg, "none") == 0) { compress_func = NULL; return 1; } if (strcmp (arg, "gz") == 0) { compress_func = grub_install_compress_gzip; return 1; } if (strcmp (arg, "xz") == 0) { compress_func = grub_install_compress_xz; return 1; } if (strcmp (arg, "lzo") == 0) { compress_func = grub_install_compress_lzop; return 1; } grub_util_error (_("Unrecognized compression `%s'"), arg); case GRUB_INSTALL_OPTIONS_GRUB_MKIMAGE: return 1; default: return 0; } } static int decompressors (void) { if (compress_func == grub_install_compress_gzip) { grub_install_push_module ("gzio"); return 1; } if (compress_func == grub_install_compress_xz) { grub_install_push_module ("xzio"); grub_install_push_module ("gcry_crc"); return 2; } if (compress_func == grub_install_compress_lzop) { grub_install_push_module ("lzopio"); grub_install_push_module ("adler32"); grub_install_push_module ("gcry_crc"); return 3; } return 0; } void grub_install_make_image_wrap_file (const char *dir, const char *prefix, FILE *fp, const char *outname, char *memdisk_path, char *config_path, const char *mkimage_target, int note, grub_compression_t comp) { const struct grub_install_image_target_desc *tgt; const char *const compnames[] = { [GRUB_COMPRESSION_AUTO] = "auto", [GRUB_COMPRESSION_NONE] = "none", [GRUB_COMPRESSION_XZ] = "xz", [GRUB_COMPRESSION_LZMA] = "lzma", }; grub_size_t slen = 1; char *s, *p; char **pk, **md; int dc = decompressors (); if (memdisk_path) slen += 20 + grub_strlen (memdisk_path); if (config_path) slen += 20 + grub_strlen (config_path); for (pk = pubkeys; pk < pubkeys + npubkeys; pk++) slen += 20 + grub_strlen (*pk); for (md = modules.entries; *md; md++) { slen += 10 + grub_strlen (*md); } p = s = xmalloc (slen); if (memdisk_path) { p = grub_stpcpy (p, "--memdisk '"); p = grub_stpcpy (p, memdisk_path); *p++ = '\''; *p++ = ' '; } if (config_path) { p = grub_stpcpy (p, "--config '"); p = grub_stpcpy (p, config_path); *p++ = '\''; *p++ = ' '; } for (pk = pubkeys; pk < pubkeys + npubkeys; pk++) { p = grub_stpcpy (p, "--pubkey '"); p = grub_stpcpy (p, *pk); *p++ = '\''; *p++ = ' '; } for (md = modules.entries; *md; md++) { *p++ = '\''; p = grub_stpcpy (p, *md); *p++ = '\''; *p++ = ' '; } *p = '\0'; grub_util_info ("grub-mkimage --directory '%s' --prefix '%s'" " --output '%s' " "--format '%s' --compression '%s' %s %s\n", dir, prefix, outname, mkimage_target, compnames[comp], note ? "--note" : "", s); tgt = grub_install_get_image_target (mkimage_target); if (!tgt) grub_util_error (_("unknown target format %s\n"), mkimage_target); grub_install_generate_image (dir, prefix, fp, outname, modules.entries, memdisk_path, pubkeys, npubkeys, config_path, tgt, note, comp); while (dc--) grub_install_pop_module (); } void grub_install_make_image_wrap (const char *dir, const char *prefix, const char *outname, char *memdisk_path, char *config_path, const char *mkimage_target, int note, grub_compression_t comp) { FILE *fp; fp = grub_util_fopen (outname, "wb"); if (! fp) grub_util_error (_("cannot open `%s': %s"), outname, strerror (errno)); grub_install_make_image_wrap_file (dir, prefix, fp, outname, memdisk_path, config_path, mkimage_target, note, comp); grub_util_file_sync (fp); fclose (fp); } static void copy_by_ext (const char *srcd, const char *dstd, const char *extf, int req) { grub_util_fd_dir_t d; grub_util_fd_dirent_t de; d = grub_util_fd_opendir (srcd); if (!d && !req) return; if (!d) grub_util_error (_("cannot open directory `%s': %s"), srcd, grub_util_fd_strerror ()); while ((de = grub_util_fd_readdir (d))) { const char *ext = strrchr (de->d_name, '.'); if (ext && strcmp (ext, extf) == 0) { char *srcf = grub_util_path_concat (2, srcd, de->d_name); char *dstf = grub_util_path_concat (2, dstd, de->d_name); grub_install_compress_file (srcf, dstf, 1); free (srcf); free (dstf); } } grub_util_fd_closedir (d); } static void copy_all (const char *srcd, const char *dstd) { grub_util_fd_dir_t d; grub_util_fd_dirent_t de; d = grub_util_fd_opendir (srcd); if (!d) grub_util_error (_("cannot open directory `%s': %s"), srcd, grub_util_fd_strerror ()); while ((de = grub_util_fd_readdir (d))) { char *srcf; char *dstf; if (strcmp (de->d_name, ".") == 0 || strcmp (de->d_name, "..") == 0) continue; srcf = grub_util_path_concat (2, srcd, de->d_name); if (grub_util_is_special_file (srcf) || grub_util_is_directory (srcf)) continue; dstf = grub_util_path_concat (2, dstd, de->d_name); grub_install_compress_file (srcf, dstf, 1); free (srcf); free (dstf); } grub_util_fd_closedir (d); } static const char * get_localedir (void) { if (grub_install_locale_directory) return grub_install_locale_directory; else return grub_util_get_localedir (); } static void copy_locales (const char *dstd) { grub_util_fd_dir_t d; grub_util_fd_dirent_t de; const char *locale_dir = get_localedir (); d = grub_util_fd_opendir (locale_dir); if (!d) { grub_util_warn (_("cannot open directory `%s': %s"), locale_dir, grub_util_fd_strerror ()); return; } while ((de = grub_util_fd_readdir (d))) { char *srcf; char *dstf; char *ext; if (strcmp (de->d_name, ".") == 0) continue; if (strcmp (de->d_name, "..") == 0) continue; ext = grub_strrchr (de->d_name, '.'); if (ext && (grub_strcmp (ext, ".mo") == 0 || grub_strcmp (ext, ".gmo") == 0)) { srcf = grub_util_path_concat (2, locale_dir, de->d_name); dstf = grub_util_path_concat (2, dstd, de->d_name); ext = grub_strrchr (dstf, '.'); grub_strcpy (ext, ".mo"); } else { srcf = grub_util_path_concat_ext (4, locale_dir, de->d_name, "LC_MESSAGES", PACKAGE, ".mo"); dstf = grub_util_path_concat_ext (2, dstd, de->d_name, ".mo"); } grub_install_compress_file (srcf, dstf, 0); free (srcf); free (dstf); } grub_util_fd_closedir (d); } 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" }, }; char * grub_install_get_platform_name (enum grub_install_plat platid) { return xasprintf ("%s-%s", platforms[platid].cpu, platforms[platid].platform); } const char * grub_install_get_platform_cpu (enum grub_install_plat platid) { return platforms[platid].cpu; } const char * grub_install_get_platform_platform (enum grub_install_plat platid) { return platforms[platid].platform; } 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 *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 { struct grub_util_path_list *path_list, *p; path_list = grub_util_resolve_dependencies (src, "moddep.lst", install_modules.entries); for (p = path_list; p; p = p->next) { const char *srcf = p->name; const char *dir; char *dstf; dir = grub_strrchr (srcf, '/'); if (dir) dir++; else dir = srcf; dstf = grub_util_path_concat (2, dst_platform, dir); grub_install_compress_file (srcf, dstf, 1); free (dstf); } } const char *pkglib_DATA[] = {"efiemu32.o", "efiemu64.o", "moddep.lst", "command.lst", "fs.lst", "partmap.lst", "parttool.lst", "video.lst", "crypto.lst", "terminal.lst" }; 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 { const char *locale_dir = get_localedir (); for (i = 0; i < install_locales.n_entries; i++) { char *srcf = grub_util_path_concat_ext (3, src, "po", install_locales.entries[i], ".mo"); char *dstf = grub_util_path_concat_ext (2, dst_locale, install_locales.entries[i], ".mo"); if (grub_install_compress_file (srcf, dstf, 0)) { free (srcf); free (dstf); continue; } free (srcf); srcf = grub_util_path_concat_ext (4, locale_dir, install_locales.entries[i], "LC_MESSAGES", PACKAGE, ".mo"); if (grub_install_compress_file (srcf, dstf, 0)) { free (srcf); free (dstf); continue; } grub_util_error (_("cannot find locale `%s'"), install_locales.entries[i]); } } 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; } for (i = 0; i < install_themes.n_entries; i++) { char *srcf = grub_util_path_concat (4, pkgdatadir, "themes", install_themes.entries[i], "theme.txt"); if (grub_util_is_regular (srcf)) { char *srcd = grub_util_path_concat (3, pkgdatadir, "themes", 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); } 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); } enum grub_install_plat grub_install_get_target (const char *src) { char *fn; grub_util_fd_t f; char buf[2048]; size_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); if (!GRUB_UTIL_FD_IS_VALID (f)) grub_util_error (_("%s doesn't exist. Please specify --target or --directory"), fn); 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="); if (!c || (c != buf && !grub_isspace (*(c-1)))) grub_util_error (_("invalid modinfo file `%s'"), fn); pl = strstr (buf, "grub_modinfo_platform="); if (!pl || (pl != buf && !grub_isspace (*(pl-1)))) grub_util_error (_("invalid modinfo file `%s'"), fn); 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; } grub_util_error (_("Unknown platform `%s-%s'"), c, pl); } void grub_util_unlink_recursive (const char *name) { grub_util_fd_dir_t d; grub_util_fd_dirent_t de; d = grub_util_fd_opendir (name); while ((de = grub_util_fd_readdir (d))) { char *fp; if (strcmp (de->d_name, ".") == 0) continue; if (strcmp (de->d_name, "..") == 0) continue; fp = grub_util_path_concat (2, name, de->d_name); if (grub_util_is_special_file (fp)) { free (fp); continue; } if (grub_util_is_regular (fp)) grub_util_unlink (fp); else if (grub_util_is_directory (fp)) grub_util_unlink_recursive (fp); free (fp); } grub_util_rmdir (name); grub_util_fd_closedir (d); }