From b8765fa0824aab891a28b728f9b7309cbe5c6ba2 Mon Sep 17 00:00:00 2001 From: Vladimir Serbinenko Date: Tue, 17 Dec 2013 15:21:02 +0100 Subject: [PATCH] Implement better integration with Mac firmware. --- ChangeLog | 4 + Makefile.util.def | 19 ++ docs/man/grub-macbless.h2m | 4 + grub-core/Makefile.core.def | 5 + grub-core/commands/macbless.c | 234 ++++++++++++++++++++++++ grub-core/fs/hfs.c | 4 + grub-core/fs/hfsplus.c | 7 +- include/grub/fs.h | 2 + include/grub/hfs.h | 13 +- include/grub/hfsplus.h | 37 +++- util/grub-install.c | 332 +++++++++++++++++++++++++++++++--- util/grub-macbless.c | 199 ++++++++++++++++++++ util/grub-mkrescue.c | 5 + util/grub-render-label.c | 5 + util/render-label.c | 4 - 15 files changed, 835 insertions(+), 39 deletions(-) create mode 100644 docs/man/grub-macbless.h2m create mode 100644 grub-core/commands/macbless.c create mode 100644 util/grub-macbless.c diff --git a/ChangeLog b/ChangeLog index 2850c76c7..c75b86f33 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2013-12-17 Vladimir Serbinenko + + Implement better integration with Mac firmware. + 2013-12-17 Vladimir Serbinenko * grub-core/loader/multiboot_mbi2.c: Implement special value for diff --git a/Makefile.util.def b/Makefile.util.def index 2720f6bd3..4ea1c3501 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -62,6 +62,7 @@ library = { common_nodist = grub_script.tab.h; common = grub-core/commands/blocklist.c; + common = grub-core/commands/macbless.c; common = grub-core/commands/xnu_uuid.c; common = grub-core/commands/testload.c; common = grub-core/commands/ls.c; @@ -401,6 +402,21 @@ program = { ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; }; +program = { + name = grub-macbless; + installdir = sbin; + mansection = 1; + common = util/grub-macbless.c; + common = grub-core/osdep/init.c; + common = grub-core/kern/emu/argp_common.c; + + ldadd = libgrubmods.a; + ldadd = libgrubgcry.a; + ldadd = libgrubkern.a; + ldadd = grub-core/gnulib/libgnu.a; + ldadd = '$(LIBINTL) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)'; +}; + data = { common = util/grub.d/README; installdir = grubconf; @@ -585,6 +601,9 @@ program = { common = grub-core/osdep/blocklist.c; common = grub-core/osdep/config.c; common = util/config.c; + common = util/render-label.c; + common = grub-core/kern/emu/hostfs.c; + common = grub-core/disk/host.c; common = util/resolve.c; enable = noemu; diff --git a/docs/man/grub-macbless.h2m b/docs/man/grub-macbless.h2m new file mode 100644 index 000000000..0197c0087 --- /dev/null +++ b/docs/man/grub-macbless.h2m @@ -0,0 +1,4 @@ +[NAME] +grub-macbless \- bless a mac file/directory +[SEE ALSO] +.BR grub-install (1) diff --git a/grub-core/Makefile.core.def b/grub-core/Makefile.core.def index e32e2ca20..286ea98a4 100644 --- a/grub-core/Makefile.core.def +++ b/grub-core/Makefile.core.def @@ -1450,6 +1450,11 @@ module = { common = fs/zfs/zfsinfo.c; }; +module = { + name = macbless; + common = commands/macbless.c; +}; + module = { name = pxe; i386_pc = net/drivers/i386/pc/pxe.c; diff --git a/grub-core/commands/macbless.c b/grub-core/commands/macbless.c new file mode 100644 index 000000000..df53b024f --- /dev/null +++ b/grub-core/commands/macbless.c @@ -0,0 +1,234 @@ +/* hfspbless.c - set the hfs+ boot directory. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2005,2007,2008,2009,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 + +struct find_node_context +{ + grub_uint64_t inode_found; + char *dirname; + enum + { NONE, FILE, DIR } found; +}; + +static int +find_inode (const char *filename, + const struct grub_dirhook_info *info, void *data) +{ + struct find_node_context *ctx = data; + if (!info->inodeset) + return 0; + + if ((grub_strcmp (ctx->dirname, filename) == 0 + || (info->case_insensitive + && grub_strcasecmp (ctx->dirname, filename) == 0))) + { + ctx->inode_found = info->inode; + ctx->found = info->dir ? DIR : FILE; + } + return 0; +} + +grub_err_t +grub_mac_bless_inode (grub_device_t dev, grub_uint64_t inode, int is_dir, + int intel) +{ + grub_err_t err; + union + { + struct grub_hfs_sblock hfs; + struct grub_hfsplus_volheader hfsplus; + } volheader; + grub_disk_addr_t embedded_offset; + + if (intel && is_dir) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "can't bless a directory for mactel"); + if (!intel && !is_dir) + return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, + "can't bless a file for mac PPC"); + + /* Read the bootblock. */ + err = grub_disk_read (dev->disk, GRUB_HFSPLUS_SBLOCK, 0, sizeof (volheader), + (char *) &volheader); + if (err) + return err; + + embedded_offset = 0; + if (grub_be_to_cpu16 (volheader.hfs.magic) == GRUB_HFS_MAGIC) + { + int extent_start; + int ablk_size; + int ablk_start; + + /* See if there's an embedded HFS+ filesystem. */ + if (grub_be_to_cpu16 (volheader.hfs.embed_sig) != GRUB_HFSPLUS_MAGIC) + { + if (intel) + volheader.hfs.intel_bootfile = grub_be_to_cpu32 (inode); + else + volheader.hfs.ppc_bootdir = grub_be_to_cpu32 (inode); + return GRUB_ERR_NONE; + } + + /* Calculate the offset needed to translate HFS+ sector numbers. */ + extent_start = + grub_be_to_cpu16 (volheader.hfs.embed_extent.first_block); + ablk_size = grub_be_to_cpu32 (volheader.hfs.blksz); + ablk_start = grub_be_to_cpu16 (volheader.hfs.first_block); + embedded_offset = (ablk_start + + extent_start + * (ablk_size >> GRUB_DISK_SECTOR_BITS)); + + err = + grub_disk_read (dev->disk, embedded_offset + GRUB_HFSPLUS_SBLOCK, 0, + sizeof (volheader), (char *) &volheader); + if (err) + return err; + } + + if ((grub_be_to_cpu16 (volheader.hfsplus.magic) != GRUB_HFSPLUS_MAGIC) + && (grub_be_to_cpu16 (volheader.hfsplus.magic) != GRUB_HFSPLUSX_MAGIC)) + return grub_error (GRUB_ERR_BAD_FS, "not a HFS+ filesystem"); + if (intel) + volheader.hfsplus.intel_bootfile = grub_be_to_cpu32 (inode); + else + volheader.hfsplus.ppc_bootdir = grub_be_to_cpu32 (inode); + + return grub_disk_write (dev->disk, embedded_offset + GRUB_HFSPLUS_SBLOCK, 0, + sizeof (volheader), (char *) &volheader); +} + +grub_err_t +grub_mac_bless_file (grub_device_t dev, const char *path_in, int intel) +{ + grub_fs_t fs; + + char *path, *tail; + struct find_node_context ctx; + + fs = grub_fs_probe (dev); + if (!fs || (grub_strcmp (fs->name, "hfsplus") != 0 + && grub_strcmp (fs->name, "hfs") != 0)) + return grub_error (GRUB_ERR_BAD_FS, "no suitable FS found"); + + path = grub_strdup (path_in); + if (!path) + return grub_errno; + + tail = path + grub_strlen (path) - 1; + + /* Remove trailing '/'. */ + while (tail != path && *tail == '/') + *(tail--) = 0; + + tail = grub_strrchr (path, '/'); + ctx.found = 0; + + if (tail) + { + *tail = 0; + ctx.dirname = tail + 1; + + (fs->dir) (dev, *path == 0 ? "/" : path, find_inode, &ctx); + } + else + { + ctx.dirname = path + 1; + (fs->dir) (dev, "/", find_inode, &ctx); + } + if (!ctx.found) + { + grub_free (path); + return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), + path_in); + } + grub_free (path); + + return grub_mac_bless_inode (dev, ctx.inode_found, (ctx.found == DIR), + intel); +} + +static grub_err_t +grub_cmd_macbless (grub_command_t cmd, int argc, char **args) +{ + char *device_name; + char *path = 0; + grub_device_t dev; + grub_err_t err; + + if (argc != 1) + return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected")); + device_name = grub_file_get_device_name (args[0]); + dev = grub_device_open (device_name); + + path = grub_strchr (args[0], ')'); + if (!path) + path = args[0]; + else + path = path + 1; + + if (!path || *path == 0 || !device_name) + { + if (dev) + grub_device_close (dev); + + grub_free (device_name); + grub_free (path); + + return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid argument"); + } + + err = grub_mac_bless_file (dev, path, cmd->name[3] == 't'); + + grub_device_close (dev); + grub_free (device_name); + return err; +} + +static grub_command_t cmd, cmd_ppc; + +GRUB_MOD_INIT(macbless) +{ + cmd = grub_register_command ("mactelbless", grub_cmd_macbless, + N_("FILE"), + N_ + ("Bless FILE of HFS or HFS+ partition for intel macs.")); + cmd_ppc = + grub_register_command ("macppcbless", grub_cmd_macbless, N_("DIR"), + N_ + ("Bless DIR of HFS or HFS+ partition for PPC macs.")); +} + +GRUB_MOD_FINI(macbless) +{ + grub_unregister_command (cmd); + grub_unregister_command (cmd_ppc); +} diff --git a/grub-core/fs/hfs.c b/grub-core/fs/hfs.c index ee6f6f955..1e593059a 100644 --- a/grub-core/fs/hfs.c +++ b/grub-core/fs/hfs.c @@ -1229,14 +1229,18 @@ grub_hfs_dir_hook (struct grub_hfs_record *rec, void *hook_arg) { info.dir = 1; info.mtimeset = 1; + info.inodeset = 1; info.mtime = grub_be_to_cpu32 (drec->mtime) - 2082844800; + info.inode = grub_be_to_cpu32 (drec->dirid); return ctx->hook (fname, &info, ctx->hook_data); } if (frec->type == GRUB_HFS_FILETYPE_FILE) { info.dir = 0; info.mtimeset = 1; + info.inodeset = 1; info.mtime = grub_be_to_cpu32 (frec->mtime) - 2082844800; + info.inode = grub_be_to_cpu32 (frec->fileid); return ctx->hook (fname, &info, ctx->hook_data); } diff --git a/grub-core/fs/hfsplus.c b/grub-core/fs/hfsplus.c index a5d7bc8f7..950d8a1e1 100644 --- a/grub-core/fs/hfsplus.c +++ b/grub-core/fs/hfsplus.c @@ -34,11 +34,6 @@ GRUB_MOD_LICENSE ("GPLv3+"); -#define GRUB_HFSPLUS_MAGIC 0x482B -#define GRUB_HFSPLUSX_MAGIC 0x4858 -#define GRUB_HFSPLUS_SBLOCK 2 - - /* The type of node. */ enum grub_hfsplus_btnode_type { @@ -919,6 +914,8 @@ grub_hfsplus_dir_iter (const char *filename, info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR); info.mtimeset = 1; info.mtime = node->mtime; + info.inodeset = 1; + info.inode = node->fileid; info.case_insensitive = !! (filetype & GRUB_FSHELP_CASE_INSENSITIVE); grub_free (node); return ctx->hook (filename, &info, ctx->hook_data); diff --git a/include/grub/fs.h b/include/grub/fs.h index e4517972b..5678c60c2 100644 --- a/include/grub/fs.h +++ b/include/grub/fs.h @@ -38,7 +38,9 @@ struct grub_dirhook_info unsigned dir:1; unsigned mtimeset:1; unsigned case_insensitive:1; + unsigned inodeset:1; grub_int32_t mtime; + grub_uint64_t inode; }; typedef int (*grub_fs_dir_hook_t) (const char *filename, diff --git a/include/grub/hfs.h b/include/grub/hfs.h index bb8ec0517..d935f5005 100644 --- a/include/grub/hfs.h +++ b/include/grub/hfs.h @@ -50,11 +50,20 @@ struct grub_hfs_sblock /* A pascal style string that holds the volumename. */ grub_uint8_t volname[28]; - grub_uint8_t unused5[52]; + grub_uint8_t unused5[28]; + + grub_uint32_t ppc_bootdir; + grub_uint32_t intel_bootfile; + /* Folder opened when disk is mounted. Unused by GRUB. */ + grub_uint32_t showfolder; + grub_uint32_t os9folder; + grub_uint8_t unused6[4]; + grub_uint32_t osxfolder; + grub_uint64_t num_serial; grub_uint16_t embed_sig; struct grub_hfs_extent embed_extent; - grub_uint8_t unused6[4]; + grub_uint8_t unused7[4]; grub_hfs_datarecord_t extent_recs; grub_uint32_t catalog_size; grub_hfs_datarecord_t catalog_recs; diff --git a/include/grub/hfsplus.h b/include/grub/hfsplus.h index fedf37d3d..75c9d18a3 100644 --- a/include/grub/hfsplus.h +++ b/include/grub/hfsplus.h @@ -1,7 +1,28 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2005,2006,2007,2008,2009,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 +#define GRUB_HFSPLUS_MAGIC 0x482B +#define GRUB_HFSPLUSX_MAGIC 0x4858 +#define GRUB_HFSPLUS_SBLOCK 2 + /* A HFS+ extent. */ struct grub_hfsplus_extent { @@ -30,7 +51,16 @@ struct grub_hfsplus_volheader grub_uint32_t utime; grub_uint8_t unused2[16]; grub_uint32_t blksize; - grub_uint8_t unused3[60]; + grub_uint8_t unused3[36]; + + grub_uint32_t ppc_bootdir; + grub_uint32_t intel_bootfile; + /* Folder opened when disk is mounted. Unused by GRUB. */ + grub_uint32_t showfolder; + grub_uint32_t os9folder; + grub_uint8_t unused4[4]; + grub_uint32_t osxfolder; + grub_uint64_t num_serial; struct grub_hfsplus_forkdata allocations_file; struct grub_hfsplus_forkdata extents_file; @@ -216,3 +246,8 @@ grub_hfsplus_btree_search (struct grub_hfsplus_btree *btree, struct grub_hfsplus_key_internal *keyb), struct grub_hfsplus_btnode **matchnode, grub_off_t *keyoffset); +grub_err_t +grub_mac_bless_inode (grub_device_t dev, grub_uint64_t inode, int is_dir, + int intel); +grub_err_t +grub_mac_bless_file (grub_device_t dev, const char *path_in, int intel); diff --git a/util/grub-install.c b/util/grub-install.c index 0c584084a..9bc0b163e 100644 --- a/util/grub-install.c +++ b/util/grub-install.c @@ -41,6 +41,7 @@ #include #include #include +#include #include @@ -60,6 +61,7 @@ static int allow_floppy = 0; static int force_file_id = 0; static char *disk_module = NULL; static char *efidir = NULL; +static char *macppcdir = NULL; static int force = 0; static int have_abstractions = 0; static int have_cryptodisk = 0; @@ -68,6 +70,10 @@ static int have_load_cfg = 0; static FILE * load_cfg_f = NULL; static char *load_cfg; static int install_bootsector = 1; +static char *label_font; +static char *label_color; +static char *label_bgcolor; +static char *product_version; static int add_rs_codes = 1; enum @@ -96,6 +102,11 @@ enum OPTION_DISK_MODULE, OPTION_NO_BOOTSECTOR, OPTION_NO_RS_CODES, + OPTION_MACPPC_DIRECTORY, + OPTION_LABEL_FONT, + OPTION_LABEL_COLOR, + OPTION_LABEL_BGCOLOR, + OPTION_PRODUCT_VERSION }; static int fs_probe = 1; @@ -119,6 +130,25 @@ argp_parser (int key, char *arg, struct argp_state *state) install_bootsector = 0; return 0; + case OPTION_PRODUCT_VERSION: + free (product_version); + product_version = xstrdup (arg); + return 0; + case OPTION_LABEL_FONT: + free (label_font); + label_font = xstrdup (arg); + return 0; + + case OPTION_LABEL_COLOR: + free (label_color); + label_color = xstrdup (arg); + return 0; + + case OPTION_LABEL_BGCOLOR: + free (label_bgcolor); + label_bgcolor = xstrdup (arg); + return 0; + /* Accept and ignore for compatibility. */ case OPTION_FONT: case OPTION_MKRELPATH: @@ -138,6 +168,11 @@ argp_parser (int key, char *arg, struct argp_state *state) bootdir = xstrdup (arg); return 0; + case OPTION_MACPPC_DIRECTORY: + free (macppcdir); + macppcdir = xstrdup (arg); + return 0; + case OPTION_EFI_DIRECTORY: free (efidir); efidir = xstrdup (arg); @@ -255,9 +290,15 @@ static struct argp_option options[] = { N_("the installation device is removable. " "This option is only available on EFI."), 2}, {"bootloader-id", OPTION_BOOTLOADER_ID, N_("ID"), 0, - N_("the ID of bootloader. This option is only available on EFI."), 2}, + N_("the ID of bootloader. This option is only available on EFI and Macs."), 2}, {"efi-directory", OPTION_EFI_DIRECTORY, N_("DIR"), 0, N_("use DIR as the EFI System Partition root."), 2}, + {"macppc-directory", OPTION_MACPPC_DIRECTORY, N_("DIR"), 0, + N_("use DIR for PPC MAC install."), 2}, + {"label-font", OPTION_LABEL_FONT, N_("FILE"), 0, N_("use FILE as font for label"), 2}, + {"label-color", OPTION_LABEL_COLOR, N_("COLOR"), 0, N_("use COLOR for label"), 2}, + {"label-bgcolor", OPTION_LABEL_BGCOLOR, N_("COLOR"), 0, N_("use COLOR for label background"), 2}, + {"product-version", OPTION_PRODUCT_VERSION, N_("STRING"), 0, N_("use STRING as product version"), 2}, {0, 0, 0, 0, 0, 0} }; @@ -697,6 +738,63 @@ is_prep_empty (grub_device_t dev) return 1; } +static void +bless (grub_device_t dev, const char *path, int x86) +{ + struct stat st; + grub_err_t err; + + grub_util_info ("blessing %s", path); + + if (stat (path, &st) < 0) + grub_util_error (N_("cannot stat `%s': %s"), + path, strerror (errno)); + + err = grub_mac_bless_inode (dev, st.st_ino, S_ISDIR (st.st_mode), x86); + if (err) + grub_util_error ("%s", grub_errmsg); + grub_util_info ("blessed\n"); +} + +static void +fill_core_services (const char *core_services) +{ + char *label; + FILE *f; + char *label_text; + char *label_string = xasprintf ("%s %s", bootloader_id, product_version); + char *sysv_plist; + + label = grub_util_path_concat (2, core_services, ".disk_label"); + grub_util_info ("rendering label %s", label_string); + grub_util_render_label (label_font, label_bgcolor ? : "white", + label_color ? : "black", label_string, label); + grub_util_info ("label rendered"); + free (label); + label_text = grub_util_path_concat (2, core_services, ".disk_label.contentDetails"); + f = grub_util_fopen (label_text, "wb"); + fprintf (f, "%s\n", label_string); + fclose (f); + free (label_string); + free (label_text); + + sysv_plist = grub_util_path_concat (2, core_services, "SystemVersion.plist"); + f = grub_util_fopen (sysv_plist, "wb"); + fprintf (f, + "\n" + "\n" + " ProductBuildVersion\n" + " \n" + " ProductName\n" + " %s\n" + " ProductVersion\n" + " %s\n" + "\n" + "\n", bootloader_id, product_version); + fclose (f); + free (sysv_plist); +} + int main (int argc, char *argv[]) { @@ -714,8 +812,14 @@ main (int argc, char *argv[]) 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); @@ -800,9 +904,12 @@ main (int argc, char *argv[]) if (!install_device) grub_util_error ("%s", _("install device isn't specified")); break; + case GRUB_INSTALL_PLATFORM_POWERPC_IEEE1275: + if (install_device) + is_prep = 1; + break; case GRUB_INSTALL_PLATFORM_MIPS_ARC: case GRUB_INSTALL_PLATFORM_MIPSEL_ARC: - case GRUB_INSTALL_PLATFORM_POWERPC_IEEE1275: break; case GRUB_INSTALL_PLATFORM_I386_EFI: case GRUB_INSTALL_PLATFORM_X86_64_EFI: @@ -850,6 +957,9 @@ main (int argc, char *argv[]) /* Initialize all modules. */ grub_init_all (); grub_gcry_init_all (); + grub_hostfs_init (); + grub_host_init (); + switch (platform) { case GRUB_INSTALL_PLATFORM_I386_EFI: @@ -927,7 +1037,13 @@ main (int argc, char *argv[]) if (! fs) grub_util_error ("%s", grub_errmsg); - if (grub_strcmp (fs->name, "fat") != 0) + efidir_is_mac = 0; + + if (grub_strcmp (fs->name, "hfs") == 0 + || grub_strcmp (fs->name, "hfsplus") == 0) + efidir_is_mac = 1; + + if (!efidir_is_mac && grub_strcmp (fs->name, "fat") != 0) grub_util_error (_("%s doesn't look like an EFI partition.\n"), efidir); /* The EFI specification requires that an EFI System Partition must @@ -1001,6 +1117,76 @@ main (int argc, char *argv[]) grub_install_mkdir_p (efidir); } + if (platform == GRUB_INSTALL_PLATFORM_POWERPC_IEEE1275) + { + int is_guess = 0; + if (!macppcdir) + { + char *d; + + is_guess = 1; + d = grub_util_path_concat (2, bootdir, "macppc"); + if (!grub_util_is_directory (d)) + { + free (d); + d = grub_util_path_concat (2, bootdir, "efi"); + } + /* Find the Mac HFS(+) System Partition. */ + if (!grub_util_is_directory (d)) + { + free (d); + d = grub_util_path_concat (2, bootdir, "EFI"); + } + if (!grub_util_is_directory (d)) + { + free (d); + d = 0; + } + if (d) + macppcdir = d; + } + if (macppcdir) + { + char **macppcdir_device_names = NULL; + grub_device_t macppcdir_grub_dev = NULL; + char *macppcdir_grub_devname; + grub_fs_t fs; + + macppcdir_device_names = grub_guess_root_devices (macppcdir); + if (!macppcdir_device_names || !macppcdir_device_names[0]) + grub_util_error (_("cannot find a device for %s (is /dev mounted?)"), + macppcdir); + + for (curdev = macppcdir_device_names; *curdev; curdev++) + grub_util_pull_device (*curdev); + + macppcdir_grub_devname = grub_util_get_grub_dev (macppcdir_device_names[0]); + if (!macppcdir_grub_devname) + grub_util_error (_("cannot find a GRUB drive for %s. Check your device.map"), + macppcdir_device_names[0]); + + macppcdir_grub_dev = grub_device_open (macppcdir_grub_devname); + if (! macppcdir_grub_dev) + grub_util_error ("%s", grub_errmsg); + + fs = grub_fs_probe (macppcdir_grub_dev); + if (! fs) + grub_util_error ("%s", grub_errmsg); + + if (grub_strcmp (fs->name, "hfs") != 0 + && grub_strcmp (fs->name, "hfsplus") != 0 + && !is_guess) + grub_util_error (_("%s is neither hfs nor hfsplue"), + macppcdir); + if (grub_strcmp (fs->name, "hfs") == 0 + || grub_strcmp (fs->name, "hfsplus") == 0) + { + install_device = macppcdir_device_names[0]; + is_prep = 0; + } + } + } + grub_install_copy_files (grub_install_source_directory, grubdir, platform); @@ -1491,8 +1677,59 @@ main (int argc, char *argv[]) } case GRUB_INSTALL_PLATFORM_POWERPC_IEEE1275: + if (macppcdir) + { + char *core_services = grub_util_path_concat (4, macppcdir, + "System", "Library", + "CoreServices"); + char *mach_kernel = grub_util_path_concat (2, macppcdir, + "mach_kernel"); + char *grub_elf, *bootx; + FILE *f; + grub_device_t ins_dev; + char *grub_chrp = grub_util_path_concat (2, + grub_install_source_directory, + "grub.chrp"); + + grub_install_mkdir_p (core_services); + + bootx = grub_util_path_concat (2, core_services, "BootX"); + grub_install_copy_file (grub_chrp, bootx, 1); + + grub_elf = grub_util_path_concat (2, core_services, "grub.elf"); + grub_install_copy_file (imgfile, grub_elf, 1); + + f = grub_util_fopen (mach_kernel, "r+"); + if (!f) + grub_util_error ("Can't create file: %s", strerror (errno)); + fclose (f); + + fill_core_services (core_services); + + ins_dev = grub_device_open (install_drive); + + bless (ins_dev, core_services, 0); + + if (update_nvram) + { + const char *dev; + int partno; + + partno = ins_dev->disk->partition + ? ins_dev->disk->partition->number + 1 : 0; + dev = grub_util_get_os_disk (install_device); + grub_install_register_ieee1275 (0, dev, partno, + "\\\\BootX"); + } + grub_device_close (ins_dev); + free (grub_elf); + free (bootx); + free (mach_kernel); + free (grub_chrp); + break; + } /* If a install device is defined, copy the core.elf to PReP partition. */ - if (install_device && install_device[0]) + if (is_prep && install_device && install_device[0]) { grub_device_t ins_dev; ins_dev = grub_device_open (install_drive); @@ -1512,28 +1749,24 @@ main (int argc, char *argv[]) s); } grub_device_close (ins_dev); + if (update_nvram) + grub_install_register_ieee1275 (1, grub_util_get_os_disk (install_device), + 0, NULL); + break; } /* fallthrough. */ case GRUB_INSTALL_PLATFORM_I386_IEEE1275: if (update_nvram) { - if (platform != GRUB_INSTALL_PLATFORM_POWERPC_IEEE1275 - || !install_device - || install_device[0] == '\0') - { - const char *dev; - char *relpath; - int partno; - relpath = grub_make_system_path_relative_to_its_root (imgfile); - partno = grub_dev->disk->partition - ? grub_dev->disk->partition->number + 1 : 0; - dev = grub_util_get_os_disk (grub_devices[0]); - grub_install_register_ieee1275 (0, dev, - partno, relpath); - } - else - grub_install_register_ieee1275 (1, grub_util_get_os_disk (install_device), - 0, NULL); + const char *dev; + char *relpath; + int partno; + relpath = grub_make_system_path_relative_to_its_root (imgfile); + partno = grub_dev->disk->partition + ? grub_dev->disk->partition->number + 1 : 0; + dev = grub_util_get_os_disk (grub_devices[0]); + grub_install_register_ieee1275 (0, dev, + partno, relpath); } break; case GRUB_INSTALL_PLATFORM_MIPS_ARC: @@ -1541,14 +1774,59 @@ main (int argc, char *argv[]) break; case GRUB_INSTALL_PLATFORM_I386_EFI: - { - char *dst = grub_util_path_concat (2, efidir, "grub.efi"); - /* For old macs. Suggested by Peter Jones. */ - grub_install_copy_file (imgfile, dst, 1); - free (dst); - } + if (!efidir_is_mac) + { + char *dst = grub_util_path_concat (2, efidir, "grub.efi"); + /* For old macs. Suggested by Peter Jones. */ + grub_install_copy_file (imgfile, dst, 1); + free (dst); + } case GRUB_INSTALL_PLATFORM_X86_64_EFI: + if (efidir_is_mac) + { + char *boot_efi; + char *core_services = grub_util_path_concat (4, efidir, + "System", "Library", + "CoreServices"); + char *mach_kernel = grub_util_path_concat (2, efidir, + "mach_kernel"); + FILE *f; + grub_device_t ins_dev; + + grub_install_mkdir_p (core_services); + + boot_efi = grub_util_path_concat (2, core_services, "boot.efi"); + grub_install_copy_file (imgfile, boot_efi, 1); + + f = grub_util_fopen (mach_kernel, "r+"); + if (!f) + grub_util_error ("Can't create file: %s", strerror (errno)); + fclose (f); + + fill_core_services(core_services); + + ins_dev = grub_device_open (install_drive); + + bless (ins_dev, boot_efi, 1); + if (!removable && update_nvram) + { + char * efidir_disk; + int efidir_part; + + /* Try to make this image bootable using the EFI Boot Manager, if available. */ + efidir_disk = grub_util_get_os_disk (efidir_device_names[0]); + efidir_part = efidir_grub_dev->disk->partition ? efidir_grub_dev->disk->partition->number + 1 : 1; + grub_install_register_efi (efidir_disk, efidir_part, + "\\System\\Library\\CoreServices", + efi_distributor); + } + + grub_device_close (ins_dev); + free (boot_efi); + free (mach_kernel); + break; + } case GRUB_INSTALL_PLATFORM_ARM_EFI: case GRUB_INSTALL_PLATFORM_ARM64_EFI: case GRUB_INSTALL_PLATFORM_IA64_EFI: diff --git a/util/grub-macbless.c b/util/grub-macbless.c new file mode 100644 index 000000000..9869d0bbd --- /dev/null +++ b/util/grub-macbless.c @@ -0,0 +1,199 @@ +/* grub-probe.c - probe device information for a given path */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2005,2006,2007,2008,2009,2010 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 + +#define _GNU_SOURCE 1 +#include + +#include "progname.h" + +static void +bless (const char *path, int x86) +{ + char *drive_name = NULL; + char **devices; + char *grub_path = NULL; + char *filebuf_via_grub = NULL, *filebuf_via_sys = NULL; + grub_device_t dev = NULL; + grub_err_t err; + struct stat st; + + grub_path = canonicalize_file_name (path); + + if (stat (grub_path, &st) < 0) + grub_util_error (N_("cannot stat `%s': %s"), + grub_path, strerror (errno)); + + devices = grub_guess_root_devices (grub_path); + + if (! devices || !devices[0]) + grub_util_error (_("cannot find a device for %s (is /dev mounted?)"), path); + + drive_name = grub_util_get_grub_dev (devices[0]); + if (! drive_name) + grub_util_error (_("cannot find a GRUB drive for %s. Check your device.map"), + devices[0]); + + grub_util_info ("opening %s", drive_name); + dev = grub_device_open (drive_name); + if (! dev) + grub_util_error ("%s", grub_errmsg); + + err = grub_mac_bless_inode (dev, st.st_ino, S_ISDIR (st.st_mode), x86); + if (err) + grub_util_error ("%s", grub_errmsg); + free (grub_path); + free (filebuf_via_grub); + free (filebuf_via_sys); + free (drive_name); +} + +static struct argp_option options[] = { + {"x86", 'x', 0, 0, + N_("bless for x86-based macs"), 0}, + {"ppc", 'p', 0, 0, + N_("bless for ppc-based macs"), 0}, + {"verbose", 'v', 0, 0, N_("print verbose messages."), 0}, + { 0, 0, 0, 0, 0, 0 } +}; + +struct arguments +{ + char *arg; + int ppc; +}; + +static error_t +argp_parser (int key, char *arg, struct argp_state *state) +{ + /* Get the input argument from argp_parse, which we + know is a pointer to our arguments structure. */ + struct arguments *arguments = state->input; + + switch (key) + { + case 'v': + verbosity++; + break; + + case 'x': + arguments->ppc = 0; + break; + + case 'p': + arguments->ppc = 1; + break; + + case ARGP_KEY_NO_ARGS: + fprintf (stderr, "%s", _("No path or device is specified.\n")); + argp_usage (state); + break; + + case ARGP_KEY_ARG: + if (arguments->arg) + { + fprintf (stderr, _("Unknown extra argument `%s'."), arg); + fprintf (stderr, "\n"); + return ARGP_ERR_UNKNOWN; + } + arguments->arg = xstrdup (arg); + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static struct argp argp = { + options, argp_parser, N_("--ppc PATH|--x86 FILE"), + N_("Mac-style bless on HFS or HFS+"), + NULL, NULL, NULL +}; + +int +main (int argc, char *argv[]) +{ + struct arguments arguments; + + grub_util_host_init (&argc, &argv); + + /* Check for options. */ + memset (&arguments, 0, sizeof (struct arguments)); + if (argp_parse (&argp, argc, argv, 0, 0, &arguments) != 0) + { + fprintf (stderr, "%s", _("Error in parsing command line arguments\n")); + exit(1); + } + + if (verbosity > 1) + grub_env_set ("debug", "all"); + + /* Initialize the emulated biosdisk driver. */ + grub_util_biosdisk_init (NULL); + + /* Initialize all modules. */ + grub_init_all (); + grub_gcry_init_all (); + + grub_lvm_fini (); + grub_mdraid09_fini (); + grub_mdraid1x_fini (); + grub_diskfilter_fini (); + grub_diskfilter_init (); + grub_mdraid09_init (); + grub_mdraid1x_init (); + grub_lvm_init (); + + /* Do it. */ + bless (arguments.arg, !arguments.ppc); + + /* Free resources. */ + grub_gcry_fini_all (); + grub_fini_all (); + grub_util_biosdisk_fini (); + + return 0; +} diff --git a/util/grub-mkrescue.c b/util/grub-mkrescue.c index b4ee8f598..cfe538bd4 100644 --- a/util/grub-mkrescue.c +++ b/util/grub-mkrescue.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -378,6 +379,10 @@ main (int argc, char *argv[]) if (!output_image) grub_util_error ("%s", _("output file must be specified")); + grub_init_all (); + grub_hostfs_init (); + grub_host_init (); + xorriso_push (xorriso); xorriso_push ("-as"); xorriso_push ("mkisofs"); diff --git a/util/grub-render-label.c b/util/grub-render-label.c index e2ca34980..9a8eedbc1 100644 --- a/util/grub-render-label.c +++ b/util/grub-render-label.c @@ -26,6 +26,7 @@ #include #include #include +#include #define _GNU_SOURCE 1 @@ -166,6 +167,10 @@ main (int argc, char *argv[]) fclose (in); } + grub_init_all (); + grub_hostfs_init (); + grub_host_init (); + grub_util_render_label (arguments.font, arguments.bgcolor, arguments.fgcolor, diff --git a/util/render-label.c b/util/render-label.c index 73f877442..9702ed953 100644 --- a/util/render-label.c +++ b/util/render-label.c @@ -167,10 +167,6 @@ grub_util_render_label (const char *label_font, fontfull = xasprintf ("(host)/%s", t); free (t); - grub_init_all (); - grub_hostfs_init (); - grub_host_init (); - grub_font_loader_init (); font = grub_font_load (fontfull); if (!font)