Compare commits

...

36 Commits

Author SHA1 Message Date
Brian C. Lane
6a533127c5 New version 2012-05-03 15:51:16 -07:00
Matthew Garrett
7d67df3ad3 mkefiboot: Add support for disk label files
The Apple boot picker provides drive names by reading small icon files
off the filesystem. Add support for including them.
2012-05-03 13:25:01 -07:00
Matthew Garrett
a37389d574 mkefiboot: Copy Mac bootloader, rather than linking it
The Mac firmware needs special-casing for hardlinked bootloaders, and
it's kind of awkward to handle that in python. Just copy them for now
instead.
2012-05-03 13:24:19 -07:00
Matthew Garrett
3996d9c9e0 mkefiboot: Make Apple boot images appear in the startup preferences
There's a small amount of additional metadata required for the Mac boot
images to appear as bootable devices in the startup preferencs, so add
support for generating that.

Signed-off-by: Brian C. Lane <bcl@redhat.com>
2012-05-03 13:22:49 -07:00
Brian C. Lane
1c623637d2 Add new requires for imgutils and mkefiboot 2012-03-07 14:54:19 -08:00
Brian C. Lane
f5d8da6927 New version 16.4.8 2012-03-06 10:00:56 -08:00
Brian C. Lane
c0eff2513b add mkefiboot and imgutils.py
livecd-creator needs mkefiboot to make images that are bootable on Mac
2012-03-06 09:58:34 -08:00
Martin Gracik
90bd100e0d Do not remove /usr/bin/env 2011-10-18 12:18:59 +02:00
Martin Gracik
418dde89c1 New version 2011-10-17 19:14:34 +02:00
Martin Gracik
573ae69e0a Changes required for grub2 (dgilmore) 2011-10-17 19:11:51 +02:00
Martin Gracik
0cef2ecfe6 Add fpaste to install environment (#727842) 2011-10-17 14:00:49 +02:00
Ales Kozumplik
b0e5b51be7 rsyslgod config: hardcode the hostname for virtio forwarding.
The value systemd provides at this early point when rsyslogd starts is
"(none)". This makes the receiving syslog unable to parse the incoming
messages.

Resolves: rhbz#744544
2011-10-13 09:36:17 +02:00
Brian C. Lane
2a58b2b5be New version 2011-10-11 10:36:18 -07:00
Martin Gracik
8f7c18b544 xen-kbdfront is a module not a package (#740378) 2011-10-11 14:13:44 +02:00
Brian C. Lane
7891fed73e copy kickstarts into sysroot (#743135)
The installer no longer has access to the initrd's root. We need to
copy any needed files over to /sysroot before switching root. This
copies *.cfg and *.ks files.

It also adds the ability to add dracut hook scripts to the initramfs
from /usr/share/lorax/dracut_hooks/
2011-10-05 09:59:33 -07:00
Martin Gracik
837fecf15f New version 2011-10-03 12:25:00 +02:00
Martin Gracik
1ee327e0f7 Ignore remove errors
On some nfs mounts the shutil.rmtree may traceback
when removing some files. This is not such a problem
so we can ignore these errors and continue.
2011-10-03 12:21:20 +02:00
Martin Gracik
21bbbe70e1 Check if /proc is mounted (#741722)
Needed for proper functionality in chroot,
so if it is not there, exit before doing anything.
2011-10-03 12:21:07 +02:00
Martin Gracik
f8f6a56ccd Add xen-kbdfront package to initrd (#740378) 2011-09-23 13:02:55 +02:00
Martin Gracik
a89ee2824e Don't use mk-s390-cdboot (dhorak)
Anaconda dropped the mk-s390-cdboot tool, so we can't call it in lorax.
2011-09-22 10:24:37 +02:00
Martin Gracik
4448e59f6e Use own addrsize implementation (dhorak)
Anaconda doesn't built the addrsize tool used to create the file
containing size and load address for initrd.img. So we implement
its function directly in lorax.
2011-09-22 10:24:29 +02:00
Martin Gracik
8e0d377992 New version 2011-09-19 15:22:48 +02:00
Martin Gracik
8f54d7c437 syslinux-vesa-splash changed filename (#739345) 2011-09-19 10:35:26 +02:00
Martin Gracik
89b838b71d New version 2011-09-16 13:43:00 +02:00
Martin Gracik
4bc95df854 Do not create the sysconfig/network file (#733425) 2011-09-09 15:54:29 +02:00
Martin Gracik
6ab2db09a4 New syslinux theme (#734170) 2011-09-09 12:27:29 +02:00
Martin Gracik
91b4c242b1 New version 2011-08-25 10:44:24 +02:00
Martin Gracik
1115512453 Do not remove ModemManager files (#727946) 2011-08-25 10:43:40 +02:00
Martin Gracik
33d0a7d9fc Raise an exception if isohybrid cannot be run on x86 2011-08-25 10:43:40 +02:00
Martin Gracik
faca7dd166 Use --noprefix when calling dracut
Do not prefix initramfs files with /run/initramfs
when creating the dracut initramfs.
2011-08-25 10:43:40 +02:00
Martin Gracik
bd64a8a8b2 Do not remove the fedora-release packages 2011-08-25 10:43:40 +02:00
David Lehman
cfb04926db Remove fedora-storage-init so it can't start raid/lvm. (#729640) 2011-08-24 20:27:01 -05:00
Martin Gracik
92efe1b536 New version 2011-08-15 09:25:25 +02:00
Martin Gracik
a0837f58b6 Do not remove nss certificates (#730438)
libreport needs them to be able to file bugreports.
2011-08-15 09:19:17 +02:00
Martin Gracik
d9fe5b3b48 Remove dogtail from the image, as it's blocking tree composition. (clumens)
Related: rhbz#726743
2011-08-15 09:16:33 +02:00
Martin Gracik
b82b6d1867 Add libreport required packages (#729537) 2011-08-12 09:28:29 +02:00
13 changed files with 699 additions and 81 deletions

View File

@ -1,7 +1,7 @@
%define debug_package %{nil} %define debug_package %{nil}
Name: lorax Name: lorax
Version: 16.4 Version: 16.4.9
Release: 1%{?dist} Release: 1%{?dist}
Summary: Tool for creating the anaconda install images Summary: Tool for creating the anaconda install images
@ -21,12 +21,16 @@ Requires: findutils
Requires: GConf2 Requires: GConf2
Requires: isomd5sum Requires: isomd5sum
Requires: glibc Requires: glibc
Requires: util-linux-ng Requires: util-linux
Requires: dosfstools Requires: dosfstools
Requires: hfsplus-tools
Requires: genisoimage Requires: genisoimage
Requires: parted Requires: parted
Requires: gzip Requires: gzip
Requires: xz Requires: xz
Requires: squashfs-tools >= 4.2
Requires: e2fsprogs
Requires: yum
%ifarch %{ix86} x86_64 %ifarch %{ix86} x86_64
Requires: syslinux Requires: syslinux
@ -54,6 +58,7 @@ make DESTDIR=$RPM_BUILD_ROOT install
%{python_sitelib}/pylorax %{python_sitelib}/pylorax
%{python_sitelib}/*.egg-info %{python_sitelib}/*.egg-info
%{_sbindir}/lorax %{_sbindir}/lorax
%{_sbindir}/mkefiboot
%dir %{_sysconfdir}/lorax %dir %{_sysconfdir}/lorax
%config(noreplace) %{_sysconfdir}/lorax/lorax.conf %config(noreplace) %{_sysconfdir}/lorax/lorax.conf
%dir %{_datadir}/lorax %dir %{_datadir}/lorax
@ -61,6 +66,53 @@ make DESTDIR=$RPM_BUILD_ROOT install
%changelog %changelog
* Thu May 03 2012 Brian C. Lane <bcl@redhat.com> 16.4.9-1
- mkefiboot: Add support for disk label files (mjg)
- mkefiboot: Copy Mac bootloader, rather than linking it (mjg)
- mkefiboot: Make Apple boot images appear in the startup preferences (mjg)
* Wed Mar 07 2012 Brian C. Lane <bcl@redhat.com> 16.4.8-2
- Add new requires for imgutils and mkefiboot
* Tue Mar 06 2012 Brian C. Lane <bcl@redhat.com> 16.4.8-1
- add mkefiboot and imgutils.py (bcl)
- Do not remove /usr/bin/env (mgracik)
* Mon Oct 17 2011 Martin Gracik <mgracik@redhat.com> 16.4.7-1
- Changes required for grub2 (dgilmore)
- Add fpaste to install environment (#727842)
- rsyslgod config: hardcode the hostname for virtio forwarding.
* Tue Oct 11 2011 Brian C. Lane <bcl@brianlane.com> 16.4.6-1
- xen-kbdfront is a module not a package (#740378) (mgracik)
- copy kickstarts into sysroot (#743135) (bcl)
* Mon Oct 03 2011 Martin Gracik <mgracik@redhat.com> 16.4.5-1
- Ignore remove errors
- Check if /proc is mounted (#741722)
- Add xen-kbdfront package to initrd (#740378)
- Don't use mk-s390-cdboot (dhorak)
- Use own addrsize implementation (dhorak)
* Mon Sep 19 2011 Martin Gracik <mgracik@redhat.com> 16.4.4-1
- syslinux-vesa-splash changed filename (#739345)
* Fri Sep 16 2011 Martin Gracik <mgracik@redhat.com> 16.4.3-1
- Do not create the sysconfig/network file (#733425)
- New syslinux theme (#734170)
* Thu Aug 25 2011 Martin Gracik <mgracik@redhat.com> 16.4.2-1
- Do not remove ModemManager files (#727946)
- Raise an exception if isohybrid cannot be run on x86
- Use --noprefix when calling dracut
- Do not remove the fedora-release packages
- Remove fedora-storage-init so it can't start raid/lvm. (#729640) (dlehman)
* Mon Aug 15 2011 Martin Gracik <mgracik@redhat.com> 16.4.1-1
- Do not remove nss certificates (#730438)
- Remove dogtail from the image, as it's blocking tree composition. (clumens)
- Add libreport required packages (#729537)
* Tue Jul 26 2011 Martin Gracik <mgracik@redhat.com> 16.4-1 * Tue Jul 26 2011 Martin Gracik <mgracik@redhat.com> 16.4-1
- Add nss libraries to the image. - Add nss libraries to the image.

View File

@ -15,7 +15,7 @@ for root, dnames, fnames in os.walk("share"):
[os.path.join(root, fname)])) [os.path.join(root, fname)]))
# executable # executable
data_files.append(("/usr/sbin", ["src/sbin/lorax"])) data_files.append(("/usr/sbin", ["src/sbin/lorax", "src/sbin/mkefiboot"]))
setup(name="lorax", setup(name="lorax",
version="0.1", version="0.1",

View File

@ -0,0 +1,107 @@
default vesamenu.c32
timeout 600
display boot.msg
# Clear the screen when exiting the menu, instead of leaving the menu displayed.
# For vesamenu, this means the graphical background is still displayed without
# the menu itself for as long as the screen remains in graphics mode.
menu clear
menu background splash.png
menu title @PRODUCT@ @VERSION@
menu vshift 8
menu rows 18
menu margin 8
#menu hidden
menu helpmsgrow 15
menu tabmsgrow 13
# Border Area
menu color border * #00000000 #00000000 none
# Selected item
menu color sel 0 #ffffffff #00000000 none
# Title bar
menu color title 0 #ff7ba3d0 #00000000 none
# Press [Tab] message
menu color tabmsg 0 #ff3a6496 #00000000 none
# Unselected menu item
menu color unsel 0 #84b8ffff #00000000 none
# Selected hotkey
menu color hotsel 0 #84b8ffff #00000000 none
# Unselected hotkey
menu color hotkey 0 #ffffffff #00000000 none
# Help text
menu color help 0 #ffffffff #00000000 none
# A scrollbar of some type? Not sure.
menu color scrollbar 0 #ffffffff #ff355594 none
# Timeout msg
menu color timeout 0 #ffffffff #00000000 none
menu color timeout_msg 0 #ffffffff #00000000 none
# Command prompt text
menu color cmdmark 0 #84b8ffff #00000000 none
menu color cmdline 0 #ffffffff #00000000 none
# Do not display the actual menu unless the user presses a key. All that is displayed is a timeout message.
menu tabmsg Press Tab for full configuration options on menu items.
menu separator # insert an empty line
menu separator # insert an empty line
label linux
menu label ^Install or upgrade @PRODUCT@
menu default
kernel vmlinuz
append initrd=initrd.img quiet
menu separator # insert an empty line
# utilities submenu
menu begin ^Troubleshooting
menu title Troubleshooting
label vesa
menu indent count 5
menu label Install @PRODUCT@ in ^basic graphics mode.
text help
Try this option out if you're having trouble installing
@PRODUCT@.
endtext
kernel vmlinuz
append initrd=initrd.img xdriver=vesa nomodeset quiet
label rescue
menu indent count 5
menu label ^Rescue a @PRODUCT@ system.
text help
If the system will not boot, this lets you access files
and edit config files to try to get it booting again.
endtext
kernel vmlinuz
append initrd=initrd.img rescue quiet
label memtest
menu label Run a ^memory test.
text help
If your system is having issues, a problem with your
system's memory may be the cause. Use this utility to
see if the memory is working correctly.
endtext
kernel memtest
menu separator # insert an empty line
label local
menu label Boot from ^local drive.
localboot 0xffff
menu separator # insert an empty line
menu separator # insert an empty line
label returntomain
menu label Return to ^main menu.
menu exit
menu end
#label local
# menu label Exit this menu and boot from ^local disk.
# localboot 0xffff

View File

@ -1 +0,0 @@
HOSTNAME=localhost.localdomain

View File

@ -23,7 +23,7 @@ $ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
$template anaconda_tty4, "%syslogseverity-text:::uppercase% %programname%:%msg%\n" $template anaconda_tty4, "%syslogseverity-text:::uppercase% %programname%:%msg%\n"
$template anaconda_syslog, "%timestamp:8:$:date-rfc3164%,%timestamp:1:3:date-subseconds% %syslogseverity-text:::uppercase% %programname%:%msg%\n" $template anaconda_syslog, "%timestamp:8:$:date-rfc3164%,%timestamp:1:3:date-subseconds% %syslogseverity-text:::uppercase% %programname%:%msg%\n"
$template virtio_ForwardFormat, "<%PRI%>%TIMESTAMP:::date-rfc3339% %HOSTNAME% %syslogtag:1:32%%msg:::sp-if-no-1st-sp%%msg%\n" $template virtio_ForwardFormat, "<%PRI%>%TIMESTAMP:::date-rfc3339% localhost %syslogtag:1:32%%msg:::sp-if-no-1st-sp%%msg%\n"
#### RULES #### #### RULES ####
# log everything except anaconda-specific records from local1 (those are stored # log everything except anaconda-specific records from local1 (those are stored

View File

@ -0,0 +1,4 @@
#!/bin/sh
# Copy over kickstart files from the initrd to the sysroot before pivot
cp /*cfg /*ks /sysroot/ 2> /dev/null

View File

@ -18,8 +18,8 @@ install "kernel"
% if basearch == "i386" or basearch == "x86_64": % if basearch == "i386" or basearch == "x86_64":
install "biosdevname" install "biosdevname"
install "efibootmgr" install "efibootmgr"
install "firstaidkit-plugin-grub" install "grub2"
install "grub" install "grub-efi"
install "memtest86+" install "memtest86+"
% endif % endif
@ -64,13 +64,13 @@ install "dbus-libs"
install "dbus-x11" install "dbus-x11"
install "dejavu-sans-fonts" install "dejavu-sans-fonts"
install "dejavu-sans-mono-fonts" install "dejavu-sans-mono-fonts"
install "dogtail"
install "dump" install "dump"
install "ethtool" install "ethtool"
install "fedora-gnome-theme" install "fedora-gnome-theme"
install "fedora-icon-theme" install "fedora-icon-theme"
install "firstaidkit-engine" install "firstaidkit-engine"
install "firstboot" install "firstboot"
install "fpaste"
install "ftp" install "ftp"
install "gdb-gdbserver" install "gdb-gdbserver"
install "gdk-pixbuf" install "gdk-pixbuf"
@ -132,6 +132,11 @@ install "python-imaging"
install "python-volume_key" install "python-volume_key"
install "rdate" install "rdate"
install "reiserfs-utils" install "reiserfs-utils"
install "libreport-gtk"
install "libreport-newt"
install "libreport-plugin-bugzilla"
install "libreport-plugin-reportuploader"
install "libreport-python"
install "rsh" install "rsh"
install "rsync" install "rsync"
install "rsyslog" install "rsyslog"
@ -205,8 +210,6 @@ remove "device-mapper-event"
remove "diffutils" remove "diffutils"
remove "dmraid-events" remove "dmraid-events"
remove "dracut" remove "dracut"
remove "fedora-release"
remove "fedora-release-rawhide"
remove "fence-agents" remove "fence-agents"
remove "fence-virt" remove "fence-virt"
remove "file" remove "file"
@ -284,7 +287,6 @@ remove "net-snmp-perl"
remove "net-snmp-utils" remove "net-snmp-utils"
remove "netcf-libs" remove "netcf-libs"
remove "nfs-utils-lib" remove "nfs-utils-lib"
remove "nss-sysinit"
remove "nss-tools" remove "nss-tools"
remove "ntp" remove "ntp"
remove "numactl" remove "numactl"
@ -438,6 +440,7 @@ module "usb-storage"
module "usbhid" module "usbhid"
module "vfat" module "vfat"
module "virtio_pci" module "virtio_pci"
module "xen-kbdfront"
module "xfs" module "xfs"
module "xts" module "xts"
module "yenta_socket" module "yenta_socket"
@ -510,9 +513,6 @@ remove "GConf2" "/usr/libexec/gconf-defaults-mechanism"
remove "GConf2" "/usr/share/GConf/*" remove "GConf2" "/usr/share/GConf/*"
remove "GConf2" "/usr/share/locale/*" remove "GConf2" "/usr/share/locale/*"
remove "GConf2" "/usr/share/sgml/*" remove "GConf2" "/usr/share/sgml/*"
remove "ModemManager" "/usr/${libdir}/*"
remove "ModemManager" "/usr/sbin/*"
remove "ModemManager" "/usr/share/icons/*"
remove "NetworkManager" "/etc/rc.d/*" remove "NetworkManager" "/etc/rc.d/*"
remove "NetworkManager" "/usr/bin/*" remove "NetworkManager" "/usr/bin/*"
remove "NetworkManager" "/usr/share/NetworkManager/*" remove "NetworkManager" "/usr/share/NetworkManager/*"
@ -580,7 +580,6 @@ remove "coreutils" "/usr/bin/cut"
remove "coreutils" "/usr/bin/dir" remove "coreutils" "/usr/bin/dir"
remove "coreutils" "/usr/bin/dircolors" remove "coreutils" "/usr/bin/dircolors"
remove "coreutils" "/usr/bin/dirname" remove "coreutils" "/usr/bin/dirname"
remove "coreutils" "/usr/bin/env"
remove "coreutils" "/usr/bin/expand" remove "coreutils" "/usr/bin/expand"
remove "coreutils" "/usr/bin/factor" remove "coreutils" "/usr/bin/factor"
remove "coreutils" "/usr/bin/fmt" remove "coreutils" "/usr/bin/fmt"
@ -648,10 +647,6 @@ remove "dhclient" "/usr/*"
remove "dmz-cursor-themes" "/usr/share/icons/dmz/*" remove "dmz-cursor-themes" "/usr/share/icons/dmz/*"
remove "dnsmasq" "/etc/rc.d/*" remove "dnsmasq" "/etc/rc.d/*"
remove "dnsmasq" "/usr/sbin/*" remove "dnsmasq" "/usr/sbin/*"
remove "dogtail" "/usr/bin/*"
remove "dogtail" "/usr/share/applications/*"
remove "dogtail" "/usr/share/dogtail/*"
remove "dogtail" "/usr/share/icons/*"
remove "dosfstools" "/sbin/dosfsck" remove "dosfstools" "/sbin/dosfsck"
remove "dosfstools" "/sbin/fsck.*" remove "dosfstools" "/sbin/fsck.*"
remove "dump" "/etc/*" remove "dump" "/etc/*"
@ -800,8 +795,6 @@ remove "groff" "/usr/bin/tfmtodit"
remove "groff" "/usr/bin/troff" remove "groff" "/usr/bin/troff"
remove "groff" "/usr/bin/zsoelim" remove "groff" "/usr/bin/zsoelim"
remove "groff" "/usr/share/*" remove "groff" "/usr/share/*"
remove "grub" "/sbin/grub-*"
remove "grub" "/usr/bin/*"
remove "gstreamer" "/usr/bin/*" remove "gstreamer" "/usr/bin/*"
remove "gstreamer" "/usr/${libdir}/gstreamer-0.10/*" remove "gstreamer" "/usr/${libdir}/gstreamer-0.10/*"
remove "gstreamer" "/usr/${libdir}/libgst*" remove "gstreamer" "/usr/${libdir}/libgst*"
@ -844,6 +837,7 @@ remove "hwdata" "/usr/share/hwdata/oui.txt"
remove "hwdata" "/usr/share/hwdata/pnp.ids" remove "hwdata" "/usr/share/hwdata/pnp.ids"
remove "hwdata" "/usr/share/hwdata/upgradelist" remove "hwdata" "/usr/share/hwdata/upgradelist"
remove "initscripts" "/etc/ppp/*" remove "initscripts" "/etc/ppp/*"
remove "initscripts" "/lib/systemd/fedora-storage-init"
remove "initscripts" "/lib/systemd/system/fedora-readonly.service" remove "initscripts" "/lib/systemd/system/fedora-readonly.service"
remove "initscripts" "/lib/systemd/system/fedora-storage-init.service" remove "initscripts" "/lib/systemd/system/fedora-storage-init.service"
remove "initscripts" "/lib/systemd/system/fedora-wait-storage.service" remove "initscripts" "/lib/systemd/system/fedora-wait-storage.service"
@ -1000,7 +994,6 @@ remove "nfs-utils" "/var/lib/nfs/xtab"
remove "notification-daemon" "/usr/${libdir}/*" remove "notification-daemon" "/usr/${libdir}/*"
remove "notification-daemon" "/usr/libexec/*" remove "notification-daemon" "/usr/libexec/*"
remove "notification-daemon" "/usr/share/locale/*" remove "notification-daemon" "/usr/share/locale/*"
remove "nss" "/etc/pki/*"
remove "nss-softokn" "/usr/${libdir}/nss/*" remove "nss-softokn" "/usr/${libdir}/nss/*"
remove "ntfs-3g" "/bin/lowntfs-3g" remove "ntfs-3g" "/bin/lowntfs-3g"
remove "ntfs-3g" "/bin/ntfs-3g.*" remove "ntfs-3g" "/bin/ntfs-3g.*"

View File

@ -170,6 +170,13 @@ class Lorax(BaseLoraxClass):
logger.critical("no root privileges") logger.critical("no root privileges")
sys.exit(1) sys.exit(1)
# check if /proc is mounted
logger.info("checking /proc")
if (not os.path.isdir("/proc") and not os.path.exists("/proc/mounts")
and not os.path.exists("/proc/devices")):
logger.critical("missing required /proc files (not mounted?)")
sys.exit(1)
# do we have all lorax required commands? # do we have all lorax required commands?
self.lcmds = constants.LoraxRequiredCommands() self.lcmds = constants.LoraxRequiredCommands()
@ -203,7 +210,8 @@ class Lorax(BaseLoraxClass):
# set up install tree # set up install tree
logger.info("setting up install tree") logger.info("setting up install tree")
self.installtree = LoraxInstallTree(self.yum, self.basearch, self.installtree = LoraxInstallTree(self.yum, self.basearch,
self.libdir, self.workdir) self.libdir, self.workdir,
self.conf)
# set up required build parameters # set up required build parameters
logger.info("setting up build parameters") logger.info("setting up build parameters")

View File

@ -27,6 +27,7 @@ import subprocess
import shutil import shutil
import glob import glob
import collections import collections
import struct
from base import DataHolder from base import DataHolder
from sysutils import joinpaths, cpfile, replace from sysutils import joinpaths, cpfile, replace
@ -65,7 +66,7 @@ ISOLINUXDIR = "isolinux"
PXEBOOTDIR = "images/pxeboot" PXEBOOTDIR = "images/pxeboot"
ISOLINUX_BIN = "usr/share/syslinux/isolinux.bin" ISOLINUX_BIN = "usr/share/syslinux/isolinux.bin"
SYSLINUX_CFG = "usr/share/anaconda/boot/syslinux.cfg" ISOLINUX_CFG = "usr/share/anaconda/boot/isolinux.cfg"
ISOHYBRID = "isohybrid" ISOHYBRID = "isohybrid"
@ -384,9 +385,9 @@ class X86(object):
self.reqs["isolinux_bin"] = cpfile(isolinux_bin, workdir) self.reqs["isolinux_bin"] = cpfile(isolinux_bin, workdir)
# syslinux.cfg # isolinux.cfg
syslinux_cfg = joinpaths(self.installtree.root, SYSLINUX_CFG) isolinux_cfg = joinpaths(self.installtree.root, ISOLINUX_CFG)
self.reqs["syslinux_cfg"] = cpfile(syslinux_cfg, workdir) self.reqs["isolinux_cfg"] = cpfile(isolinux_cfg, workdir)
# memtest # memtest
memtest = glob.glob(joinpaths(self.installtree.root, "boot", memtest = glob.glob(joinpaths(self.installtree.root, "boot",
@ -409,10 +410,10 @@ class X86(object):
# splash # splash
splash = joinpaths(self.installtree.root, ANABOOTDIR, splash = joinpaths(self.installtree.root, ANABOOTDIR,
"syslinux-vesa-splash.jpg") "syslinux-splash.png")
if not splash: if not splash:
raise Exception("syslinux-vesa-splash.jpg not present") raise Exception("syslinux-splash.png not present")
self.reqs["splash"] = cpfile(splash, workdir) self.reqs["splash"] = cpfile(splash, workdir)
@ -436,10 +437,9 @@ class X86(object):
cpfile(self.reqs["isolinux_bin"], cpfile(self.reqs["isolinux_bin"],
joinpaths(self.outputroot, ISOLINUXDIR)) joinpaths(self.outputroot, ISOLINUXDIR))
# copy syslinux.cfg to isolinux dir (XXX rename to isolinux.cfg) # copy isolinux.cfg to isolinux dir
isolinux_cfg = cpfile(self.reqs["syslinux_cfg"], isolinux_cfg = cpfile(self.reqs["isolinux_cfg"],
joinpaths(self.outputroot, ISOLINUXDIR, joinpaths(self.outputroot, ISOLINUXDIR))
"isolinux.cfg"))
replace(isolinux_cfg, r"@PRODUCT@", self.product) replace(isolinux_cfg, r"@PRODUCT@", self.product)
replace(isolinux_cfg, r"@VERSION@", self.version) replace(isolinux_cfg, r"@VERSION@", self.version)
@ -449,29 +449,24 @@ class X86(object):
cpfile(self.reqs["memtest"], cpfile(self.reqs["memtest"],
joinpaths(self.outputroot, ISOLINUXDIR)) joinpaths(self.outputroot, ISOLINUXDIR))
with open(isolinux_cfg, "a") as f: #with open(isolinux_cfg, "a") as f:
f.write("label memtest86\n") # f.write("label memtest86\n")
f.write(" menu label ^Memory test\n") # f.write(" menu label ^Memory test\n")
f.write(" kernel memtest\n") # f.write(" kernel memtest\n")
f.write(" append -\n") # f.write(" append -\n")
# copy *.msg files # copy *.msg files
for src in self.reqs["msgfiles"]: for src in self.reqs["msgfiles"]:
dst = cpfile(src, joinpaths(self.outputroot, ISOLINUXDIR)) dst = cpfile(src, joinpaths(self.outputroot, ISOLINUXDIR))
replace(dst, r"@VERSION@", self.version) replace(dst, r"@VERSION@", self.version)
# copy syslinux-vesa-splash.jpg
splash = cpfile(self.reqs["splash"], splash = cpfile(self.reqs["splash"],
joinpaths(self.outputroot, ISOLINUXDIR, "splash.jpg")) joinpaths(self.outputroot, ISOLINUXDIR, "splash.png"))
# copy vesamenu.c32 # copy vesamenu.c32
cpfile(self.reqs["vesamenu"], cpfile(self.reqs["vesamenu"],
joinpaths(self.outputroot, ISOLINUXDIR)) joinpaths(self.outputroot, ISOLINUXDIR))
# set up isolinux.cfg
replace(isolinux_cfg, r"default linux", "default vesamenu.c32")
replace(isolinux_cfg, r"prompt 1", "#prompt 1")
# copy grub.conf # copy grub.conf
grubconf = cpfile(self.reqs["grubconf"], grubconf = cpfile(self.reqs["grubconf"],
joinpaths(self.outputroot, ISOLINUXDIR)) joinpaths(self.outputroot, ISOLINUXDIR))
@ -569,11 +564,14 @@ class X86(object):
stdout=subprocess.PIPE) stdout=subprocess.PIPE)
p.wait() p.wait()
if os.path.exists(ISOHYBRID): try:
# run isohybrid # run isohybrid
cmd = [ISOHYBRID, boot_fpath] cmd = [ISOHYBRID, boot_fpath]
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, p = subprocess.Popen(cmd, stdin=subprocess.PIPE,
stdout=subprocess.PIPE) stdout=subprocess.PIPE)
except OSError as e:
raise Exception("cannot run isohybrid: %s" % e)
else:
p.wait() p.wait()
# run implantisomd5 # run implantisomd5
@ -638,16 +636,13 @@ class S390(object):
logger.info("compressing the install tree") logger.info("compressing the install tree")
self.installtree.compress(initrd, kernel, self.ctype, self.cargs) self.installtree.compress(initrd, kernel, self.ctype, self.cargs)
# run addrsize # create initrd.addrsize
addrsize = joinpaths(self.installtree.root, "usr/libexec", addrsize = open(joinpaths(self.outputroot, IMAGESDIR,
"anaconda", "addrsize") "initrd.addrsize"), "wb")
addrsize_data = struct.pack(">iiii", 0, int(INITRD_ADDRESS, 16), 0,
cmd = [addrsize, INITRD_ADDRESS, initrd.fpath, os.stat(initrd.fpath).st_size)
joinpaths(self.outputroot, IMAGESDIR, "initrd.addrsize")] addrsize.write(addrsize_data)
addrsize.close()
p = subprocess.Popen(cmd, stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
p.wait()
# add kernel and initrd to .treeinfo # add kernel and initrd to .treeinfo
kernel_arch = kernel.version.split(".")[-1] kernel_arch = kernel.version.split(".")[-1]
@ -660,24 +655,6 @@ class S390(object):
"generic.ins": os.path.basename(generic_ins)} "generic.ins": os.path.basename(generic_ins)}
self.treeinfo.add_section(section, data) self.treeinfo.add_section(section, data)
# create cdboot.img
bootiso_fpath = joinpaths(self.outputroot, IMAGESDIR, "cdboot.img")
# run mks390cdboot
mks390cdboot = joinpaths(self.installtree.root, "usr/libexec",
"anaconda", "mk-s390-cdboot")
cmd = [mks390cdboot, "-i", kernel.fpath, "-r", initrd.fpath,
"-p", generic_prm, "-o", bootiso_fpath]
p = subprocess.Popen(cmd, stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
p.wait()
# add cdboot.img to treeinfo
data = {"cdboot.img": joinpaths(IMAGESDIR, "cdboot.img")}
self.treeinfo.add_section(section, data)
def create_boot(self, efiboot=None): def create_boot(self, efiboot=None):
pass pass

294
src/pylorax/imgutils.py Normal file
View File

@ -0,0 +1,294 @@
# imgutils.py - utility functions/classes for building disk images
#
# Copyright (C) 2011 Red Hat, Inc.
#
# This program 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 2 of the License, or
# (at your option) any later version.
#
# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
#
# Author(s): Will Woods <wwoods@redhat.com>
import logging
logger = logging.getLogger("pylorax.imgutils")
import os, tempfile
from os.path import join, dirname
from pylorax.sysutils import cpfile
from subprocess import *
import traceback
######## Functions for making container images (cpio, squashfs) ##########
def mkcpio(rootdir, outfile, compression="xz", compressargs=["-9"]):
'''Make a compressed CPIO archive of the given rootdir.
compression should be "xz", "gzip", "lzma", or None.
compressargs will be used on the compression commandline.'''
if compression not in (None, "xz", "gzip", "lzma"):
raise ValueError, "Unknown compression type %s" % compression
chdir = lambda: os.chdir(rootdir)
if compression == "xz":
compressargs.insert(0, "--check=crc32")
if compression is None:
compression = "cat" # this is a little silly
compressargs = []
find = Popen(["find", ".", "-print0"], stdout=PIPE, preexec_fn=chdir)
cpio = Popen(["cpio", "--null", "--quiet", "-H", "newc", "-o"],
stdin=find.stdout, stdout=PIPE, preexec_fn=chdir)
comp = Popen([compression] + compressargs,
stdin=cpio.stdout, stdout=open(outfile, "wb"))
comp.wait()
return comp.returncode
def mksquashfs(rootdir, outfile, compression="default", compressargs=[]):
'''Make a squashfs image containing the given rootdir.'''
if compression != "default":
compressargs = ["-comp", compression] + compressargs
return call(["mksquashfs", rootdir, outfile] + compressargs)
######## Utility functions ###############################################
def mksparse(outfile, size):
'''use os.ftruncate to create a sparse file of the given size.'''
fobj = open(outfile, "w")
os.ftruncate(fobj.fileno(), size)
def loop_attach(outfile):
'''Attach a loop device to the given file. Return the loop device name.
Raises CalledProcessError if losetup fails.'''
dev = check_output(["losetup", "--find", "--show", outfile], stderr=PIPE)
return dev.strip()
def loop_detach(loopdev):
'''Detach the given loop device. Return False on failure.'''
return (call(["losetup", "--detach", loopdev]) == 0)
def dm_attach(dev, size, name=None):
'''Attach a devicemapper device to the given device, with the given size.
If name is None, a random name will be chosen. Returns the device name.
raises CalledProcessError if dmsetup fails.'''
if name is None:
name = tempfile.mktemp(prefix="lorax.imgutils.", dir="")
check_call(["dmsetup", "create", name, "--table",
"0 %i linear %s 0" % (size/512, dev)],
stdout=PIPE, stderr=PIPE)
return name
def dm_detach(dev):
'''Detach the named devicemapper device. Returns False if dmsetup fails.'''
dev = dev.replace("/dev/mapper/", "") # strip prefix, if it's there
return call(["dmsetup", "remove", dev], stdout=PIPE, stderr=PIPE)
def mount(dev, opts="", mnt=None):
'''Mount the given device at the given mountpoint, using the given opts.
opts should be a comma-separated string of mount options.
if mnt is none, a temporary directory will be created and its path will be
returned.
raises CalledProcessError if mount fails.'''
if mnt is None:
mnt = tempfile.mkdtemp(prefix="lorax.imgutils.")
mount = ["mount"]
if opts:
mount += ["-o", opts]
check_call(mount + [dev, mnt])
return mnt
def umount(mnt):
'''Unmount the given mountpoint. If the mount was a temporary dir created
by mount, it will be deleted. Returns false if the unmount fails.'''
rv = call(["umount", mnt])
if 'lorax.imgutils' in mnt:
os.rmdir(mnt)
return (rv == 0)
def copytree(src, dest, preserve=True):
'''Copy a tree of files using cp -a, thus preserving modes, timestamps,
links, acls, sparse files, xattrs, selinux contexts, etc.
If preserve is False, uses cp -R (useful for modeless filesystems)'''
chdir = lambda: os.chdir(src)
cp = ["cp", "-a"] if preserve else ["cp", "-R", "-L"]
check_call(cp + [".", os.path.abspath(dest)], preexec_fn=chdir)
def do_grafts(grafts, dest, preserve=True):
'''Copy each of the items listed in grafts into dest.
If the key ends with '/' it's assumed to be a directory which should be
created, otherwise just the leading directories will be created.'''
for imgpath, filename in grafts.items():
if imgpath[-1] == '/':
targetdir = join(dest, imgpath)
imgpath = imgpath[:-1]
else:
targetdir = join(dest, dirname(imgpath))
if not os.path.isdir(targetdir):
os.makedirs(targetdir)
if os.path.isdir(filename):
copytree(filename, join(dest, imgpath), preserve)
else:
cpfile(filename, join(dest, imgpath))
def round_to_blocks(size, blocksize):
'''If size isn't a multiple of blocksize, round up to the next multiple'''
diff = size % blocksize
if diff or not size:
size += blocksize - diff
return size
# TODO: move filesystem data outside this function
def estimate_size(rootdir, graft={}, fstype=None, blocksize=4096, overhead=128):
getsize = lambda f: os.lstat(f).st_size
if fstype == "btrfs":
overhead = 64*1024 # don't worry, it's all sparse
if fstype == "hfsplus":
overhead = 200 # hack to deal with two bootloader copies
if fstype in ("vfat", "msdos"):
blocksize = 2048
getsize = lambda f: os.stat(f).st_size # no symlinks, count as copies
total = overhead*blocksize
dirlist = graft.values()
if rootdir:
dirlist.append(rootdir)
for root in dirlist:
for top, dirs, files in os.walk(root):
for f in files + dirs:
total += round_to_blocks(getsize(join(top,f)), blocksize)
if fstype == "btrfs":
total = max(256*1024*1024, total) # btrfs minimum size: 256MB
return total
######## Execution contexts - use with the 'with' statement ##############
class LoopDev(object):
def __init__(self, filename, size=None):
self.filename = filename
if size:
mksparse(self.filename, size)
def __enter__(self):
self.loopdev = loop_attach(self.filename)
return self.loopdev
def __exit__(self, exc_type, exc_value, traceback):
loop_detach(self.loopdev)
class DMDev(object):
def __init__(self, dev, size, name=None):
(self.dev, self.size, self.name) = (dev, size, name)
def __enter__(self):
self.mapperdev = dm_attach(self.dev, self.size, self.name)
return self.mapperdev
def __exit__(self, exc_type, exc_value, traceback):
dm_detach(self.mapperdev)
class Mount(object):
def __init__(self, dev, opts="", mnt=None):
(self.dev, self.opts, self.mnt) = (dev, opts, mnt)
def __enter__(self):
self.mnt = mount(self.dev, self.opts, self.mnt)
return self.mnt
def __exit__(self, exc_type, exc_value, traceback):
umount(self.mnt)
class PartitionMount(object):
""" Mount a partitioned image file using kpartx """
def __init__(self, disk_img, mount_ok=None):
"""
disk_img is the full path to a partitioned disk image
mount_ok is a function that is passed the mount point and
returns True if it should be mounted.
"""
self.mount_dir = None
self.disk_img = disk_img
self.mount_ok = mount_ok
# Default is to mount partition with /etc/passwd
if not self.mount_ok:
self.mount_ok = lambda mount_dir: os.path.isfile(mount_dir+"/etc/passwd")
# Example kpartx output
# kpartx -p p -v -a /tmp/diskV2DiCW.im
# add map loop2p1 (253:2): 0 3481600 linear /dev/loop2 2048
# add map loop2p2 (253:3): 0 614400 linear /dev/loop2 3483648
cmd = [ "kpartx", "-v", "-p", "p", "-a", self.disk_img ]
logger.debug(cmd)
kpartx_output = check_output(cmd)
logger.debug(kpartx_output)
# list of (deviceName, sizeInBytes)
self.loop_devices = []
for line in kpartx_output.splitlines():
# add map loop2p3 (253:4): 0 7139328 linear /dev/loop2 528384
# 3rd element is size in 512 byte blocks
if line.startswith("add map "):
fields = line[8:].split()
self.loop_devices.append( (fields[0], int(fields[3])*512) )
def __enter__(self):
# Mount the device selected by mount_ok, if possible
mount_dir = tempfile.mkdtemp()
for dev, size in self.loop_devices:
try:
mount( "/dev/mapper/"+dev, mnt=mount_dir )
if self.mount_ok(mount_dir):
self.mount_dir = mount_dir
self.mount_dev = dev
self.mount_size = size
break
umount( mount_dir )
except CalledProcessError:
logger.debug(traceback.format_exc())
if self.mount_dir:
logger.info("Partition mounted on {0} size={1}".format(self.mount_dir, self.mount_size))
else:
logger.debug("Unable to mount anything from {0}".format(self.disk_img))
os.rmdir(mount_dir)
return self
def __exit__(self, exc_type, exc_value, traceback):
if self.mount_dir:
umount( self.mount_dir )
os.rmdir(self.mount_dir)
self.mount_dir = None
call(["kpartx", "-d", self.disk_img])
######## Functions for making filesystem images ##########################
def mkfsimage(fstype, rootdir, outfile, size=None, mkfsargs=[], mountargs="", graft={}):
'''Generic filesystem image creation function.
fstype should be a filesystem type - "mkfs.${fstype}" must exist.
graft should be a dict: {"some/path/in/image": "local/file/or/dir"};
if the path ends with a '/' it's assumed to be a directory.
Will raise CalledProcessError if something goes wrong.'''
preserve = (fstype not in ("msdos", "vfat"))
if not size:
size = estimate_size(rootdir, graft, fstype)
with LoopDev(outfile, size) as loopdev:
check_call(["mkfs.%s" % fstype] + mkfsargs + [loopdev],
stdout=PIPE, stderr=PIPE)
with Mount(loopdev, mountargs) as mnt:
if rootdir:
copytree(rootdir, mnt, preserve)
do_grafts(graft, mnt, preserve)
# convenience functions with useful defaults
def mkdosimg(rootdir, outfile, size=None, label="", mountargs="shortname=winnt,umask=0077", graft={}):
mkfsimage("msdos", rootdir, outfile, size, mountargs=mountargs,
mkfsargs=["-n", label], graft=graft)
def mkext4img(rootdir, outfile, size=None, label="", mountargs="", graft={}):
mkfsimage("ext4", rootdir, outfile, size, mountargs=mountargs,
mkfsargs=["-L", label, "-b", "1024", "-m", "0"], graft=graft)
def mkbtrfsimg(rootdir, outfile, size=None, label="", mountargs="", graft={}):
mkfsimage("btrfs", rootdir, outfile, size, mountargs=mountargs,
mkfsargs=["-L", label], graft=graft)
def mkhfsimg(rootdir, outfile, size=None, label="", mountargs="", graft={}):
mkfsimage("hfsplus", rootdir, outfile, size, mountargs=mountargs,
mkfsargs=["-v", label], graft=graft)

View File

@ -39,7 +39,7 @@ from sysutils import *
class LoraxInstallTree(BaseLoraxClass): class LoraxInstallTree(BaseLoraxClass):
def __init__(self, yum, basearch, libdir, workdir): def __init__(self, yum, basearch, libdir, workdir, conf=None):
BaseLoraxClass.__init__(self) BaseLoraxClass.__init__(self)
self.yum = yum self.yum = yum
self.root = self.yum.installroot self.root = self.yum.installroot
@ -47,9 +47,45 @@ class LoraxInstallTree(BaseLoraxClass):
self.libdir = libdir self.libdir = libdir
self.workdir = workdir self.workdir = workdir
self.initramfs = {} self.initramfs = {}
self.conf = conf
self.lcmds = constants.LoraxRequiredCommands() self.lcmds = constants.LoraxRequiredCommands()
@property
def dracut_hooks_path(self):
""" Return the path to the lorax dracut hooks scripts
Use the configured share dir if it is setup,
otherwise default to /usr/share/lorax/dracut_hooks
"""
if self.conf:
return joinpaths(self.conf.get("lorax", "sharedir"),
"dracut_hooks")
else:
return "/usr/share/lorax/dracut_hooks"
def copy_dracut_hooks(self, hooks):
""" Copy the hook scripts in hooks into the installroot's /tmp/
and return a list of commands to pass to dracut when creating the
initramfs
hooks is a list of tuples with the name of the hook script and the
target dracut hook directory
(eg. [("99anaconda-copy-ks.sh", "/lib/dracut/hooks/pre-pivot")])
"""
dracut_commands = []
for hook_script, dracut_path in hooks:
src = joinpaths(self.dracut_hooks_path, hook_script)
if not os.path.exists(src):
logger.error("Missing lorax dracut hook script %s" % (src))
continue
dst = joinpaths(self.root, "/tmp/", hook_script)
shutil.copy2(src, dst)
dracut_commands += ["--include", joinpaths("/tmp/", hook_script),
dracut_path]
return dracut_commands
def remove_locales(self): def remove_locales(self):
chroot = lambda: os.chroot(self.root) chroot = lambda: os.chroot(self.root)
@ -426,9 +462,8 @@ class LoraxInstallTree(BaseLoraxClass):
dst = joinpaths(self.root, "etc/selinux", "config") dst = joinpaths(self.root, "etc/selinux", "config")
shutil.copy2(src, dst) shutil.copy2(src, dst)
# get sysconfig files src = joinpaths(src_dir, "isolinux.cfg")
src = joinpaths(src_dir, "network") dst = joinpaths(self.root, "usr/share/anaconda/boot")
dst = joinpaths(self.root, "etc/sysconfig")
shutil.copy2(src, dst) shutil.copy2(src, dst)
def setup_sshd(self, src_dir): def setup_sshd(self, src_dir):
@ -549,12 +584,16 @@ class LoraxInstallTree(BaseLoraxClass):
def make_dracut_initramfs(self): def make_dracut_initramfs(self):
for kernel in self.kernels: for kernel in self.kernels:
hooks = [("99anaconda-copy-ks.sh", "/lib/dracut/hooks/pre-pivot")]
hook_commands = self.copy_dracut_hooks(hooks)
outfile = "/tmp/initramfs.img" # inside the chroot outfile = "/tmp/initramfs.img" # inside the chroot
logger.debug("chrooting into installtree to create initramfs.img") logger.debug("chrooting into installtree to create initramfs.img")
subprocess.check_call(["chroot", self.root, "/sbin/dracut", subprocess.check_call(["chroot", self.root, "/sbin/dracut",
"--nomdadmconf", "--nolvmconf", "--noprefix", "--nomdadmconf", "--nolvmconf",
"--xz", "--modules", "base dmsquash-live", "--xz", "--modules", "base dmsquash-live"] \
outfile, kernel.version]) + hook_commands \
+ [outfile, kernel.version])
# move output file into installtree workdir # move output file into installtree workdir
dstdir = joinpaths(self.workdir, "dracut-%s" % kernel.version) dstdir = joinpaths(self.workdir, "dracut-%s" % kernel.version)
os.makedirs(dstdir) os.makedirs(dstdir)

View File

@ -117,7 +117,7 @@ class LoraxYumHelper(object):
if os.path.islink(fname) or os.path.isfile(fname): if os.path.islink(fname) or os.path.isfile(fname):
os.unlink(fname) os.unlink(fname)
else: else:
shutil.rmtree(fname) shutil.rmtree(fname, ignore_errors=True)
logger.debug("removed {0}".format(fname)) logger.debug("removed {0}".format(fname))
count += 1 count += 1

145
src/sbin/mkefiboot Executable file
View File

@ -0,0 +1,145 @@
#!/usr/bin/python
# mkefiboot - a tool to make EFI boot images
# Copyright (C) 2011 Red Hat, Inc.
#
# This program 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 2 of the License, or
# (at your option) any later version.
#
# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
#
# Red Hat Author(s): Will Woods <wwoods@redhat.com>
import os, tempfile, argparse
from subprocess import check_call, PIPE
from pylorax.imgutils import mkdosimg, round_to_blocks, LoopDev, DMDev, dm_detach
from pylorax.imgutils import mkhfsimg, Mount
import struct, shutil, glob
def mkefiboot(bootdir, outfile, label):
'''Make an EFI boot image with the contents of bootdir in EFI/BOOT'''
mkdosimg(None, outfile, label=label, graft={'EFI/BOOT':bootdir})
def mkmacboot(bootdir, outfile, label, icon=None, product='Generic',
diskname=None):
'''Make an EFI boot image for Apple's EFI implementation'''
graft = {'EFI/BOOT':bootdir}
if icon and os.path.exists(icon):
graft['.VolumeIcon.icns'] = icon
if diskname and os.path.exists(diskname):
graft['EFI/BOOT/.disk_label'] = diskname
mkhfsimg(None, outfile, label=label, graft=graft)
macmunge(outfile, product)
# To make an HFS+ image bootable, we need to fill in parts of the
# HFSPlusVolumeHeader structure - specifically, finderInfo[0,1,5].
# For details, see Technical Note TN1150: HFS Plus Volume Format
# http://developer.apple.com/library/mac/#technotes/tn/tn1150.html
#
# Additionally, we want to do some fixups to make it play nicely with
# the startup disk preferences panel.
def macmunge(imgfile, product):
'''"bless" the EFI bootloader inside the given Mac EFI boot image, by
writing its inode info into the HFS+ volume header.'''
# Get the inode number for the boot image and its parent directory
with LoopDev(imgfile) as loopdev:
with Mount(loopdev) as mnt:
loader = glob.glob(os.path.join(mnt,'EFI/BOOT/BOOT*.efi'))[0]
config = glob.glob(os.path.join(mnt,'EFI/BOOT/BOOT*.conf'))[0]
blessnode = os.stat(loader).st_ino
dirnode = os.stat(os.path.dirname(loader)).st_ino
with open(os.path.join(mnt,'mach_kernel'), 'w') as kernel:
kernel.write('Dummy kernel for booting')
sysdir = os.path.join(mnt,'System/Library/CoreServices/')
os.makedirs(sysdir)
with open(os.path.join(sysdir,'SystemVersion.plist'), 'w') as plist:
plist.write('''<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>ProductBuildVersion</key>
<string></string>
<key>ProductName</key>
<string>Linux</string>
<key>ProductVersion</key>
<string>%s</string>
</dict>
</plist>
''' % (product,))
shutil.copy(loader, os.path.join(sysdir,'boot.efi'))
shutil.copy(config, os.path.join(sysdir,'boot.conf'))
# format data properly (big-endian UInt32)
nodedata = struct.pack(">i", blessnode)
dirdata = struct.pack(">i", dirnode)
# Write it to the volume header
with open(imgfile, "r+b") as img:
img.seek(0x450) # HFSPlusVolumeHeader->finderInfo
img.write(dirdata) # finderInfo[0]
img.write(nodedata) # finderInfo[1]
img.seek(0x464) #
img.write(dirdata) # finderInfo[5]
def mkefidisk(efiboot, outfile):
'''Make a bootable EFI disk image out of the given EFI boot image.'''
# pjones sez: "17408 is the size of the GPT tables parted creates"
partsize = os.path.getsize(efiboot) + 17408
disksize = round_to_blocks(17408 + partsize, 512)
with LoopDev(outfile, disksize) as loopdev:
with DMDev(loopdev, disksize) as dmdev:
check_call(["parted", "--script", "/dev/mapper/%s" % dmdev,
"mklabel", "gpt",
"unit", "b",
"mkpart", "'EFI System Partition'", "fat32", "17408", str(partsize),
"set", "1", "boot", "on"], stdout=PIPE, stderr=PIPE)
partdev = "/dev/mapper/{0}p1".format(dmdev)
with open(efiboot, "rb") as infile:
with open(partdev, "wb") as outfile:
outfile.write(infile.read())
dm_detach(dmdev+"p1")
if __name__ == '__main__':
parser = argparse.ArgumentParser(description="Make an EFI boot image from the given directory.")
parser.add_argument("-d", "--disk", action="store_true",
help="make a full EFI disk image (including partition table)")
parser.add_argument("-a", "--apple", action="store_const", const="apple",
dest="imgtype", default="default",
help="make an Apple EFI image (use hfs+, bless bootloader)")
parser.add_argument("-l", "--label", default="EFI",
help="filesystem label to use (default: %(default)s)")
parser.add_argument("-i", "--icon", metavar="ICONFILE",
help="icon file to include (for Apple EFI image)")
parser.add_argument("-n", "--diskname", metavar="DISKNAME",
help="disk name image to include (for Apple EFI image)")
parser.add_argument("-p", "--product", metavar="PRODUCT",
help="product name to use (for Apple EFI image)")
parser.add_argument("bootdir", metavar="EFIBOOTDIR",
help="input directory (will become /EFI/BOOT in the image)")
parser.add_argument("outfile", metavar="OUTPUTFILE",
help="output file to write")
opt = parser.parse_args()
# sanity checks
if not os.path.isdir(opt.bootdir):
parser.error("%s is not a directory" % opt.bootdir)
if os.getuid() > 0:
parser.error("need root permissions")
if opt.icon and not opt.imgtype == "apple":
print "Warning: --icon is only useful for Apple EFI images"
if opt.diskname and not opt.imgtype == "apple":
print "Warning: --diskname is only useful for Apple EFI images"
# do the thing!
if opt.imgtype == "apple":
mkmacboot(opt.bootdir, opt.outfile, opt.label, opt.icon, opt.product,
opt.diskname)
else:
mkefiboot(opt.bootdir, opt.outfile, opt.label)
if opt.disk:
efiboot = tempfile.NamedTemporaryFile(prefix="mkefiboot.").name
shutil.move(opt.outfile, efiboot)
mkefidisk(efiboot, opt.outfile)