mirror of
https://git.proxmox.com/git/grub2
synced 2025-10-04 23:10:33 +00:00

Refactor clean_grub_dir() to create a backup of all the files, instead of just irrevocably removing them as the first action. If available, register atexit() handler to restore the backup if errors occur before point of no return, or remove the backup if everything was successful. If atexit() is not available, the backup remains on disk for manual recovery. Some platforms defined a point of no return, i.e. after modules & core images were updated. Failures from any commands after that stage are ignored, and backup is cleaned up. For example, on EFI platforms update is not reverted when efibootmgr fails. Extra care is taken to ensure atexit() handler is only invoked by the parent process and not any children forks. Some older GRUB codebases can invoke parent atexit() hooks from forks, which can mess up the backup. This allows safer upgrades of MBR & modules, such that modules/images/fonts/translations are consistent with MBR in case of errors. For example accidental grub-install /dev/non-existent-disk currently clobbers and upgrades modules in /boot/grub, despite not actually updating any MBR. This patch only handles backup and restore of files copied to /boot/grub. This patch does not perform backup (or restoration) of MBR itself or blocklists. Thus when installing i386-pc platform, corruption may still occur with MBR and blocklists which will not be attempted to be automatically recovered. Also add modinfo.sh and *.efi to the cleanup/backup/restore code path, to ensure it is also cleaned, backed up and restored. Signed-off-by: Dimitri John Ledkov <xnox@ubuntu.com> Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
1165 lines
30 KiB
C
1165 lines
30 KiB
C
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <grub/types.h>
|
|
#include <grub/emu/misc.h>
|
|
#include <grub/util/misc.h>
|
|
#include <grub/misc.h>
|
|
#include <grub/device.h>
|
|
#include <grub/disk.h>
|
|
#include <grub/file.h>
|
|
#include <grub/fs.h>
|
|
#include <grub/env.h>
|
|
#include <grub/term.h>
|
|
#include <grub/mm.h>
|
|
#include <grub/lib/hexdump.h>
|
|
#include <grub/crypto.h>
|
|
#include <grub/command.h>
|
|
#include <grub/i18n.h>
|
|
#include <grub/zfs/zfs.h>
|
|
#include <grub/util/install.h>
|
|
#include <grub/util/resolve.h>
|
|
#include <grub/emu/hostfile.h>
|
|
#include <grub/emu/config.h>
|
|
#include <grub/emu/hostfile.h>
|
|
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
|
|
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
|
|
|
|
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 ());
|
|
case GRUB_INSTALL_OPTIONS_THEMES_DIRECTORY:
|
|
return grub_util_path_concat (2, grub_util_get_pkgdatadir (), "themes");
|
|
default:
|
|
return (char *) text;
|
|
}
|
|
}
|
|
|
|
#pragma GCC diagnostic error "-Wformat-nonliteral"
|
|
|
|
static int (*compress_func) (const char *src, const char *dest) = NULL;
|
|
char *grub_install_copy_buffer;
|
|
static char *dtb;
|
|
|
|
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;
|
|
r = grub_util_fd_write (out, grub_install_copy_buffer, r);
|
|
if (r <= 0)
|
|
break;
|
|
}
|
|
if (grub_util_fd_sync (out) < 0)
|
|
r = -1;
|
|
if (grub_util_fd_close (in) < 0)
|
|
r = -1;
|
|
if (grub_util_fd_close (out) < 0)
|
|
r = -1;
|
|
|
|
if (r < 0)
|
|
grub_util_error (_("cannot copy `%s' to `%s': %s"),
|
|
src, dst, grub_util_fd_strerror ());
|
|
|
|
return 1;
|
|
}
|
|
|
|
static 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 int
|
|
strcmp_ext (const char *a, const char *b, const char *ext)
|
|
{
|
|
char *bsuffix = grub_util_path_concat_ext (1, b, ext);
|
|
int r = strcmp (a, bsuffix);
|
|
|
|
free (bsuffix);
|
|
return r;
|
|
}
|
|
|
|
enum clean_grub_dir_mode
|
|
{
|
|
CLEAN_NEW,
|
|
CLEAN_BACKUP,
|
|
CREATE_BACKUP,
|
|
RESTORE_BACKUP
|
|
};
|
|
|
|
#ifdef HAVE_ATEXIT
|
|
static size_t backup_dirs_size = 0;
|
|
static char **backup_dirs = NULL;
|
|
static pid_t backup_process = 0;
|
|
static int grub_install_backup_ponr = 0;
|
|
|
|
void
|
|
grub_set_install_backup_ponr (void)
|
|
{
|
|
grub_install_backup_ponr = 1;
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
clean_grub_dir_real (const char *di, enum clean_grub_dir_mode mode)
|
|
{
|
|
grub_util_fd_dir_t d;
|
|
grub_util_fd_dirent_t de;
|
|
const char *suffix = "";
|
|
|
|
if ((mode == CLEAN_BACKUP) || (mode == RESTORE_BACKUP))
|
|
suffix = "~";
|
|
|
|
d = grub_util_fd_opendir (di);
|
|
if (!d)
|
|
{
|
|
if (mode == CLEAN_BACKUP)
|
|
return;
|
|
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 (ext, ".mod", suffix) == 0
|
|
|| strcmp_ext (ext, ".lst", suffix) == 0
|
|
|| strcmp_ext (ext, ".img", suffix) == 0
|
|
|| strcmp_ext (ext, ".efi", suffix) == 0
|
|
|| strcmp_ext (ext, ".mo", suffix) == 0)
|
|
&& strcmp_ext (de->d_name, "menu.lst", suffix) != 0)
|
|
|| strcmp_ext (de->d_name, "modinfo.sh", suffix) == 0
|
|
|| strcmp_ext (de->d_name, "efiemu32.o", suffix) == 0
|
|
|| strcmp_ext (de->d_name, "efiemu64.o", suffix) == 0)
|
|
{
|
|
char *srcf = grub_util_path_concat (2, di, de->d_name);
|
|
|
|
if (mode == CREATE_BACKUP)
|
|
{
|
|
char *dstf = grub_util_path_concat_ext (2, di, de->d_name, "~");
|
|
|
|
if (grub_util_rename (srcf, dstf) < 0)
|
|
grub_util_error (_("cannot backup `%s': %s"), srcf,
|
|
grub_util_fd_strerror ());
|
|
free (dstf);
|
|
}
|
|
else if (mode == RESTORE_BACKUP)
|
|
{
|
|
char *dstf = grub_util_path_concat (2, di, de->d_name);
|
|
|
|
dstf[strlen (dstf) - 1] = '\0';
|
|
if (grub_util_rename (srcf, dstf) < 0)
|
|
grub_util_error (_("cannot restore `%s': %s"), dstf,
|
|
grub_util_fd_strerror ());
|
|
free (dstf);
|
|
}
|
|
else
|
|
{
|
|
if (grub_util_unlink (srcf) < 0)
|
|
grub_util_error (_("cannot delete `%s': %s"), srcf,
|
|
grub_util_fd_strerror ());
|
|
}
|
|
free (srcf);
|
|
}
|
|
}
|
|
grub_util_fd_closedir (d);
|
|
}
|
|
|
|
#ifdef HAVE_ATEXIT
|
|
static void
|
|
restore_backup_atexit (void)
|
|
{
|
|
size_t i;
|
|
|
|
/*
|
|
* Some child inherited atexit() handler, did not clear it, and called it.
|
|
* Thus skip clean or restore logic.
|
|
*/
|
|
if (backup_process != getpid ())
|
|
return;
|
|
|
|
for (i = 0; i < backup_dirs_size; i++)
|
|
{
|
|
/*
|
|
* If past point of no return simply clean the backups. Otherwise
|
|
* cleanup newly installed files, and restore the backups.
|
|
*/
|
|
if (grub_install_backup_ponr)
|
|
clean_grub_dir_real (backup_dirs[i], CLEAN_BACKUP);
|
|
else
|
|
{
|
|
clean_grub_dir_real (backup_dirs[i], CLEAN_NEW);
|
|
clean_grub_dir_real (backup_dirs[i], RESTORE_BACKUP);
|
|
}
|
|
free (backup_dirs[i]);
|
|
}
|
|
|
|
backup_dirs_size = 0;
|
|
|
|
free (backup_dirs);
|
|
}
|
|
|
|
static void
|
|
append_to_backup_dirs (const char *dir)
|
|
{
|
|
backup_dirs = xrealloc (backup_dirs, sizeof (char *) * (backup_dirs_size + 1));
|
|
backup_dirs[backup_dirs_size] = xstrdup (dir);
|
|
backup_dirs_size++;
|
|
if (!backup_process)
|
|
{
|
|
atexit (restore_backup_atexit);
|
|
backup_process = getpid ();
|
|
}
|
|
}
|
|
#else
|
|
static void
|
|
append_to_backup_dirs (const char *dir __attribute__ ((unused)))
|
|
{
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
clean_grub_dir (const char *di)
|
|
{
|
|
clean_grub_dir_real (di, CLEAN_BACKUP);
|
|
clean_grub_dir_real (di, CREATE_BACKUP);
|
|
append_to_backup_dirs (di);
|
|
}
|
|
|
|
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;
|
|
char *grub_install_themes_directory = NULL;
|
|
|
|
int
|
|
grub_install_is_short_mbrgap_supported (void)
|
|
{
|
|
int i, j;
|
|
static const char *whitelist[] =
|
|
{
|
|
"part_msdos", "biosdisk", "affs", "afs", "bfs", "archelp",
|
|
"cpio", "cpio_be", "newc", "odc", "ext2", "fat", "exfat",
|
|
"f2fs", "fshelp", "hfs", "hfsplus", "iso9660", "jfs", "minix",
|
|
"minix2", "minix3", "minix_be", "minix2_be", "nilfs2", "ntfs",
|
|
"ntfscomp", "reiserfs", "romfs", "sfs", "tar", "udf", "ufs1",
|
|
"ufs1_be", "ufs2", "xfs"
|
|
};
|
|
|
|
for (i = 0; i < modules.n_entries; i++) {
|
|
for (j = 0; j < ARRAY_SIZE (whitelist); j++)
|
|
if (strcmp(modules.entries[i], whitelist[j]) == 0)
|
|
break;
|
|
if (j == ARRAY_SIZE (whitelist))
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
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 = xcalloc (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;
|
|
static char *sbat;
|
|
static int disable_shim_lock;
|
|
static grub_compression_t compression;
|
|
|
|
int
|
|
grub_install_parse (int key, char *arg)
|
|
{
|
|
switch (key)
|
|
{
|
|
case 'C':
|
|
if (grub_strcmp (arg, "xz") == 0)
|
|
{
|
|
#ifdef HAVE_LIBLZMA
|
|
compression = GRUB_COMPRESSION_XZ;
|
|
#else
|
|
grub_util_error ("%s",
|
|
_("grub-mkimage is compiled without XZ support"));
|
|
#endif
|
|
}
|
|
else if (grub_strcmp (arg, "none") == 0)
|
|
compression = GRUB_COMPRESSION_NONE;
|
|
else if (grub_strcmp (arg, "auto") == 0)
|
|
compression = GRUB_COMPRESSION_AUTO;
|
|
else
|
|
grub_util_error (_("Unknown compression format %s"), arg);
|
|
return 1;
|
|
case 'k':
|
|
pubkeys = xrealloc (pubkeys,
|
|
sizeof (pubkeys[0])
|
|
* (npubkeys + 1));
|
|
pubkeys[npubkeys++] = xstrdup (arg);
|
|
return 1;
|
|
case GRUB_INSTALL_OPTIONS_SBAT:
|
|
if (sbat)
|
|
free (sbat);
|
|
|
|
sbat = xstrdup (arg);
|
|
return 1;
|
|
case GRUB_INSTALL_OPTIONS_DISABLE_SHIM_LOCK:
|
|
disable_shim_lock = 1;
|
|
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_THEMES_DIRECTORY:
|
|
free (grub_install_themes_directory);
|
|
grub_install_themes_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_DTB:
|
|
if (dtb)
|
|
free (dtb);
|
|
dtb = xstrdup (arg);
|
|
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)
|
|
{
|
|
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' "
|
|
" --dtb '%s' "
|
|
"--sbat '%s' "
|
|
"--format '%s' --compression '%s' %s %s %s\n",
|
|
dir, prefix,
|
|
outname, dtb ? : "", sbat ? : "", mkimage_target,
|
|
compnames[compression], note ? "--note" : "",
|
|
disable_shim_lock ? "--disable-shim-lock" : "", s);
|
|
free (s);
|
|
|
|
tgt = grub_install_get_image_target (mkimage_target);
|
|
if (!tgt)
|
|
grub_util_error (_("unknown target format %s"), mkimage_target);
|
|
|
|
grub_install_generate_image (dir, prefix, fp, outname,
|
|
modules.entries, memdisk_path,
|
|
pubkeys, npubkeys, config_path, tgt,
|
|
note, compression, dtb, sbat,
|
|
disable_shim_lock);
|
|
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)
|
|
{
|
|
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);
|
|
if (grub_util_file_sync (fp) < 0)
|
|
grub_util_error (_("cannot sync `%s': %s"), outname, strerror (errno));
|
|
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);
|
|
}
|
|
|
|
#if (defined (GRUB_UTIL) && defined(ENABLE_NLS) && ENABLE_NLS)
|
|
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);
|
|
}
|
|
#endif
|
|
|
|
static void
|
|
grub_install_copy_nls(const char *src __attribute__ ((unused)),
|
|
const char *dst __attribute__ ((unused)))
|
|
{
|
|
#if (defined (GRUB_UTIL) && defined(ENABLE_NLS) && ENABLE_NLS)
|
|
char *dst_locale;
|
|
|
|
dst_locale = grub_util_path_concat (2, dst, "locale");
|
|
grub_install_mkdir_p (dst_locale);
|
|
clean_grub_dir (dst_locale);
|
|
|
|
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
|
|
{
|
|
size_t i;
|
|
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) == 0)
|
|
grub_util_error (_("cannot find locale `%s'"),
|
|
install_locales.entries[i]);
|
|
free (srcf);
|
|
free (dstf);
|
|
}
|
|
}
|
|
free (dst_locale);
|
|
#endif
|
|
}
|
|
|
|
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_I386_XEN_PVH] = { "i386", "xen_pvh" },
|
|
[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" },
|
|
[GRUB_INSTALL_PLATFORM_ARM_COREBOOT] = { "arm", "coreboot" },
|
|
[GRUB_INSTALL_PLATFORM_RISCV32_EFI] = { "riscv32", "efi" },
|
|
[GRUB_INSTALL_PLATFORM_RISCV64_EFI] = { "riscv64", "efi" },
|
|
};
|
|
|
|
char *
|
|
grub_install_get_platforms_string (void)
|
|
{
|
|
char **arr = xmalloc (sizeof (char *) * ARRAY_SIZE (platforms));
|
|
int platform_strins_len = 0;
|
|
char *platforms_string;
|
|
char *ptr;
|
|
unsigned i;
|
|
for (i = 0; i < ARRAY_SIZE (platforms); i++)
|
|
{
|
|
arr[i] = xasprintf ("%s-%s", platforms[i].cpu,
|
|
platforms[i].platform);
|
|
platform_strins_len += strlen (arr[i]) + 2;
|
|
}
|
|
ptr = platforms_string = xmalloc (platform_strins_len);
|
|
qsort (arr, ARRAY_SIZE (platforms), sizeof (char *), grub_qsort_strcmp);
|
|
for (i = 0; i < ARRAY_SIZE (platforms); i++)
|
|
{
|
|
strcpy (ptr, arr[i]);
|
|
ptr += strlen (arr[i]);
|
|
*ptr++ = ',';
|
|
*ptr++ = ' ';
|
|
free (arr[i]);
|
|
}
|
|
ptr[-2] = 0;
|
|
free (arr);
|
|
|
|
return platforms_string;
|
|
}
|
|
|
|
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_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_fonts = grub_util_path_concat (2, dst, "fonts");
|
|
grub_install_mkdir_p (dst_platform);
|
|
clean_grub_dir (dst);
|
|
clean_grub_dir (dst_platform);
|
|
|
|
grub_install_copy_nls(src, dst);
|
|
|
|
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);
|
|
}
|
|
|
|
grub_util_free_path_list (path_list);
|
|
}
|
|
|
|
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_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;
|
|
}
|
|
|
|
if (grub_install_themes_directory)
|
|
themes_dir = xstrdup (grub_install_themes_directory);
|
|
else
|
|
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_fonts);
|
|
}
|
|
|
|
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);
|
|
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);
|
|
if (r < 0)
|
|
grub_util_error (_("cannot read `%s': %s"), fn, strerror (errno));
|
|
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);
|
|
}
|