Compare commits
36 Commits
master
...
f16-branch
Author | SHA1 | Date | |
---|---|---|---|
|
6a533127c5 | ||
|
7d67df3ad3 | ||
|
a37389d574 | ||
|
3996d9c9e0 | ||
|
1c623637d2 | ||
|
f5d8da6927 | ||
|
c0eff2513b | ||
|
90bd100e0d | ||
|
418dde89c1 | ||
|
573ae69e0a | ||
|
0cef2ecfe6 | ||
|
b0e5b51be7 | ||
|
2a58b2b5be | ||
|
8f7c18b544 | ||
|
7891fed73e | ||
|
837fecf15f | ||
|
1ee327e0f7 | ||
|
21bbbe70e1 | ||
|
f8f6a56ccd | ||
|
a89ee2824e | ||
|
4448e59f6e | ||
|
8e0d377992 | ||
|
8f54d7c437 | ||
|
89b838b71d | ||
|
4bc95df854 | ||
|
6ab2db09a4 | ||
|
91b4c242b1 | ||
|
1115512453 | ||
|
33d0a7d9fc | ||
|
faca7dd166 | ||
|
bd64a8a8b2 | ||
|
cfb04926db | ||
|
92efe1b536 | ||
|
a0837f58b6 | ||
|
d9fe5b3b48 | ||
|
b82b6d1867 |
56
lorax.spec
56
lorax.spec
@ -1,7 +1,7 @@
|
||||
%define debug_package %{nil}
|
||||
|
||||
Name: lorax
|
||||
Version: 16.4
|
||||
Version: 16.4.9
|
||||
Release: 1%{?dist}
|
||||
Summary: Tool for creating the anaconda install images
|
||||
|
||||
@ -21,12 +21,16 @@ Requires: findutils
|
||||
Requires: GConf2
|
||||
Requires: isomd5sum
|
||||
Requires: glibc
|
||||
Requires: util-linux-ng
|
||||
Requires: util-linux
|
||||
Requires: dosfstools
|
||||
Requires: hfsplus-tools
|
||||
Requires: genisoimage
|
||||
Requires: parted
|
||||
Requires: gzip
|
||||
Requires: xz
|
||||
Requires: squashfs-tools >= 4.2
|
||||
Requires: e2fsprogs
|
||||
Requires: yum
|
||||
|
||||
%ifarch %{ix86} x86_64
|
||||
Requires: syslinux
|
||||
@ -54,6 +58,7 @@ make DESTDIR=$RPM_BUILD_ROOT install
|
||||
%{python_sitelib}/pylorax
|
||||
%{python_sitelib}/*.egg-info
|
||||
%{_sbindir}/lorax
|
||||
%{_sbindir}/mkefiboot
|
||||
%dir %{_sysconfdir}/lorax
|
||||
%config(noreplace) %{_sysconfdir}/lorax/lorax.conf
|
||||
%dir %{_datadir}/lorax
|
||||
@ -61,6 +66,53 @@ make DESTDIR=$RPM_BUILD_ROOT install
|
||||
|
||||
|
||||
%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
|
||||
- Add nss libraries to the image.
|
||||
|
||||
|
2
setup.py
2
setup.py
@ -15,7 +15,7 @@ for root, dnames, fnames in os.walk("share"):
|
||||
[os.path.join(root, fname)]))
|
||||
|
||||
# executable
|
||||
data_files.append(("/usr/sbin", ["src/sbin/lorax"]))
|
||||
data_files.append(("/usr/sbin", ["src/sbin/lorax", "src/sbin/mkefiboot"]))
|
||||
|
||||
setup(name="lorax",
|
||||
version="0.1",
|
||||
|
107
share/config_files/isolinux.cfg
Normal file
107
share/config_files/isolinux.cfg
Normal 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
|
||||
|
@ -1 +0,0 @@
|
||||
HOSTNAME=localhost.localdomain
|
@ -23,7 +23,7 @@ $ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
|
||||
|
||||
$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 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 ####
|
||||
# log everything except anaconda-specific records from local1 (those are stored
|
||||
|
4
share/dracut_hooks/99anaconda-copy-ks.sh
Executable file
4
share/dracut_hooks/99anaconda-copy-ks.sh
Executable 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
|
||||
|
@ -18,8 +18,8 @@ install "kernel"
|
||||
% if basearch == "i386" or basearch == "x86_64":
|
||||
install "biosdevname"
|
||||
install "efibootmgr"
|
||||
install "firstaidkit-plugin-grub"
|
||||
install "grub"
|
||||
install "grub2"
|
||||
install "grub-efi"
|
||||
install "memtest86+"
|
||||
% endif
|
||||
|
||||
@ -64,13 +64,13 @@ install "dbus-libs"
|
||||
install "dbus-x11"
|
||||
install "dejavu-sans-fonts"
|
||||
install "dejavu-sans-mono-fonts"
|
||||
install "dogtail"
|
||||
install "dump"
|
||||
install "ethtool"
|
||||
install "fedora-gnome-theme"
|
||||
install "fedora-icon-theme"
|
||||
install "firstaidkit-engine"
|
||||
install "firstboot"
|
||||
install "fpaste"
|
||||
install "ftp"
|
||||
install "gdb-gdbserver"
|
||||
install "gdk-pixbuf"
|
||||
@ -132,6 +132,11 @@ install "python-imaging"
|
||||
install "python-volume_key"
|
||||
install "rdate"
|
||||
install "reiserfs-utils"
|
||||
install "libreport-gtk"
|
||||
install "libreport-newt"
|
||||
install "libreport-plugin-bugzilla"
|
||||
install "libreport-plugin-reportuploader"
|
||||
install "libreport-python"
|
||||
install "rsh"
|
||||
install "rsync"
|
||||
install "rsyslog"
|
||||
@ -205,8 +210,6 @@ remove "device-mapper-event"
|
||||
remove "diffutils"
|
||||
remove "dmraid-events"
|
||||
remove "dracut"
|
||||
remove "fedora-release"
|
||||
remove "fedora-release-rawhide"
|
||||
remove "fence-agents"
|
||||
remove "fence-virt"
|
||||
remove "file"
|
||||
@ -284,7 +287,6 @@ remove "net-snmp-perl"
|
||||
remove "net-snmp-utils"
|
||||
remove "netcf-libs"
|
||||
remove "nfs-utils-lib"
|
||||
remove "nss-sysinit"
|
||||
remove "nss-tools"
|
||||
remove "ntp"
|
||||
remove "numactl"
|
||||
@ -438,6 +440,7 @@ module "usb-storage"
|
||||
module "usbhid"
|
||||
module "vfat"
|
||||
module "virtio_pci"
|
||||
module "xen-kbdfront"
|
||||
module "xfs"
|
||||
module "xts"
|
||||
module "yenta_socket"
|
||||
@ -510,9 +513,6 @@ remove "GConf2" "/usr/libexec/gconf-defaults-mechanism"
|
||||
remove "GConf2" "/usr/share/GConf/*"
|
||||
remove "GConf2" "/usr/share/locale/*"
|
||||
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" "/usr/bin/*"
|
||||
remove "NetworkManager" "/usr/share/NetworkManager/*"
|
||||
@ -580,7 +580,6 @@ remove "coreutils" "/usr/bin/cut"
|
||||
remove "coreutils" "/usr/bin/dir"
|
||||
remove "coreutils" "/usr/bin/dircolors"
|
||||
remove "coreutils" "/usr/bin/dirname"
|
||||
remove "coreutils" "/usr/bin/env"
|
||||
remove "coreutils" "/usr/bin/expand"
|
||||
remove "coreutils" "/usr/bin/factor"
|
||||
remove "coreutils" "/usr/bin/fmt"
|
||||
@ -648,10 +647,6 @@ remove "dhclient" "/usr/*"
|
||||
remove "dmz-cursor-themes" "/usr/share/icons/dmz/*"
|
||||
remove "dnsmasq" "/etc/rc.d/*"
|
||||
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/fsck.*"
|
||||
remove "dump" "/etc/*"
|
||||
@ -800,8 +795,6 @@ remove "groff" "/usr/bin/tfmtodit"
|
||||
remove "groff" "/usr/bin/troff"
|
||||
remove "groff" "/usr/bin/zsoelim"
|
||||
remove "groff" "/usr/share/*"
|
||||
remove "grub" "/sbin/grub-*"
|
||||
remove "grub" "/usr/bin/*"
|
||||
remove "gstreamer" "/usr/bin/*"
|
||||
remove "gstreamer" "/usr/${libdir}/gstreamer-0.10/*"
|
||||
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/upgradelist"
|
||||
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-storage-init.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/libexec/*"
|
||||
remove "notification-daemon" "/usr/share/locale/*"
|
||||
remove "nss" "/etc/pki/*"
|
||||
remove "nss-softokn" "/usr/${libdir}/nss/*"
|
||||
remove "ntfs-3g" "/bin/lowntfs-3g"
|
||||
remove "ntfs-3g" "/bin/ntfs-3g.*"
|
||||
|
@ -170,6 +170,13 @@ class Lorax(BaseLoraxClass):
|
||||
logger.critical("no root privileges")
|
||||
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?
|
||||
self.lcmds = constants.LoraxRequiredCommands()
|
||||
|
||||
@ -203,7 +210,8 @@ class Lorax(BaseLoraxClass):
|
||||
# set up install tree
|
||||
logger.info("setting up install tree")
|
||||
self.installtree = LoraxInstallTree(self.yum, self.basearch,
|
||||
self.libdir, self.workdir)
|
||||
self.libdir, self.workdir,
|
||||
self.conf)
|
||||
|
||||
# set up required build parameters
|
||||
logger.info("setting up build parameters")
|
||||
|
@ -27,6 +27,7 @@ import subprocess
|
||||
import shutil
|
||||
import glob
|
||||
import collections
|
||||
import struct
|
||||
|
||||
from base import DataHolder
|
||||
from sysutils import joinpaths, cpfile, replace
|
||||
@ -65,7 +66,7 @@ ISOLINUXDIR = "isolinux"
|
||||
PXEBOOTDIR = "images/pxeboot"
|
||||
|
||||
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"
|
||||
|
||||
@ -384,9 +385,9 @@ class X86(object):
|
||||
|
||||
self.reqs["isolinux_bin"] = cpfile(isolinux_bin, workdir)
|
||||
|
||||
# syslinux.cfg
|
||||
syslinux_cfg = joinpaths(self.installtree.root, SYSLINUX_CFG)
|
||||
self.reqs["syslinux_cfg"] = cpfile(syslinux_cfg, workdir)
|
||||
# isolinux.cfg
|
||||
isolinux_cfg = joinpaths(self.installtree.root, ISOLINUX_CFG)
|
||||
self.reqs["isolinux_cfg"] = cpfile(isolinux_cfg, workdir)
|
||||
|
||||
# memtest
|
||||
memtest = glob.glob(joinpaths(self.installtree.root, "boot",
|
||||
@ -409,10 +410,10 @@ class X86(object):
|
||||
|
||||
# splash
|
||||
splash = joinpaths(self.installtree.root, ANABOOTDIR,
|
||||
"syslinux-vesa-splash.jpg")
|
||||
"syslinux-splash.png")
|
||||
|
||||
if not splash:
|
||||
raise Exception("syslinux-vesa-splash.jpg not present")
|
||||
raise Exception("syslinux-splash.png not present")
|
||||
|
||||
self.reqs["splash"] = cpfile(splash, workdir)
|
||||
|
||||
@ -436,10 +437,9 @@ class X86(object):
|
||||
cpfile(self.reqs["isolinux_bin"],
|
||||
joinpaths(self.outputroot, ISOLINUXDIR))
|
||||
|
||||
# copy syslinux.cfg to isolinux dir (XXX rename to isolinux.cfg)
|
||||
isolinux_cfg = cpfile(self.reqs["syslinux_cfg"],
|
||||
joinpaths(self.outputroot, ISOLINUXDIR,
|
||||
"isolinux.cfg"))
|
||||
# copy isolinux.cfg to isolinux dir
|
||||
isolinux_cfg = cpfile(self.reqs["isolinux_cfg"],
|
||||
joinpaths(self.outputroot, ISOLINUXDIR))
|
||||
|
||||
replace(isolinux_cfg, r"@PRODUCT@", self.product)
|
||||
replace(isolinux_cfg, r"@VERSION@", self.version)
|
||||
@ -449,29 +449,24 @@ class X86(object):
|
||||
cpfile(self.reqs["memtest"],
|
||||
joinpaths(self.outputroot, ISOLINUXDIR))
|
||||
|
||||
with open(isolinux_cfg, "a") as f:
|
||||
f.write("label memtest86\n")
|
||||
f.write(" menu label ^Memory test\n")
|
||||
f.write(" kernel memtest\n")
|
||||
f.write(" append -\n")
|
||||
#with open(isolinux_cfg, "a") as f:
|
||||
# f.write("label memtest86\n")
|
||||
# f.write(" menu label ^Memory test\n")
|
||||
# f.write(" kernel memtest\n")
|
||||
# f.write(" append -\n")
|
||||
|
||||
# copy *.msg files
|
||||
for src in self.reqs["msgfiles"]:
|
||||
dst = cpfile(src, joinpaths(self.outputroot, ISOLINUXDIR))
|
||||
replace(dst, r"@VERSION@", self.version)
|
||||
|
||||
# copy syslinux-vesa-splash.jpg
|
||||
splash = cpfile(self.reqs["splash"],
|
||||
joinpaths(self.outputroot, ISOLINUXDIR, "splash.jpg"))
|
||||
joinpaths(self.outputroot, ISOLINUXDIR, "splash.png"))
|
||||
|
||||
# copy vesamenu.c32
|
||||
cpfile(self.reqs["vesamenu"],
|
||||
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
|
||||
grubconf = cpfile(self.reqs["grubconf"],
|
||||
joinpaths(self.outputroot, ISOLINUXDIR))
|
||||
@ -569,11 +564,14 @@ class X86(object):
|
||||
stdout=subprocess.PIPE)
|
||||
p.wait()
|
||||
|
||||
if os.path.exists(ISOHYBRID):
|
||||
try:
|
||||
# run isohybrid
|
||||
cmd = [ISOHYBRID, boot_fpath]
|
||||
p = subprocess.Popen(cmd, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE)
|
||||
except OSError as e:
|
||||
raise Exception("cannot run isohybrid: %s" % e)
|
||||
else:
|
||||
p.wait()
|
||||
|
||||
# run implantisomd5
|
||||
@ -638,16 +636,13 @@ class S390(object):
|
||||
logger.info("compressing the install tree")
|
||||
self.installtree.compress(initrd, kernel, self.ctype, self.cargs)
|
||||
|
||||
# run addrsize
|
||||
addrsize = joinpaths(self.installtree.root, "usr/libexec",
|
||||
"anaconda", "addrsize")
|
||||
|
||||
cmd = [addrsize, INITRD_ADDRESS, initrd.fpath,
|
||||
joinpaths(self.outputroot, IMAGESDIR, "initrd.addrsize")]
|
||||
|
||||
p = subprocess.Popen(cmd, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE)
|
||||
p.wait()
|
||||
# create initrd.addrsize
|
||||
addrsize = open(joinpaths(self.outputroot, IMAGESDIR,
|
||||
"initrd.addrsize"), "wb")
|
||||
addrsize_data = struct.pack(">iiii", 0, int(INITRD_ADDRESS, 16), 0,
|
||||
os.stat(initrd.fpath).st_size)
|
||||
addrsize.write(addrsize_data)
|
||||
addrsize.close()
|
||||
|
||||
# add kernel and initrd to .treeinfo
|
||||
kernel_arch = kernel.version.split(".")[-1]
|
||||
@ -660,24 +655,6 @@ class S390(object):
|
||||
"generic.ins": os.path.basename(generic_ins)}
|
||||
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):
|
||||
pass
|
||||
|
||||
|
294
src/pylorax/imgutils.py
Normal file
294
src/pylorax/imgutils.py
Normal 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)
|
@ -39,7 +39,7 @@ from sysutils import *
|
||||
|
||||
class LoraxInstallTree(BaseLoraxClass):
|
||||
|
||||
def __init__(self, yum, basearch, libdir, workdir):
|
||||
def __init__(self, yum, basearch, libdir, workdir, conf=None):
|
||||
BaseLoraxClass.__init__(self)
|
||||
self.yum = yum
|
||||
self.root = self.yum.installroot
|
||||
@ -47,9 +47,45 @@ class LoraxInstallTree(BaseLoraxClass):
|
||||
self.libdir = libdir
|
||||
self.workdir = workdir
|
||||
self.initramfs = {}
|
||||
self.conf = conf
|
||||
|
||||
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):
|
||||
chroot = lambda: os.chroot(self.root)
|
||||
|
||||
@ -426,9 +462,8 @@ class LoraxInstallTree(BaseLoraxClass):
|
||||
dst = joinpaths(self.root, "etc/selinux", "config")
|
||||
shutil.copy2(src, dst)
|
||||
|
||||
# get sysconfig files
|
||||
src = joinpaths(src_dir, "network")
|
||||
dst = joinpaths(self.root, "etc/sysconfig")
|
||||
src = joinpaths(src_dir, "isolinux.cfg")
|
||||
dst = joinpaths(self.root, "usr/share/anaconda/boot")
|
||||
shutil.copy2(src, dst)
|
||||
|
||||
def setup_sshd(self, src_dir):
|
||||
@ -549,12 +584,16 @@ class LoraxInstallTree(BaseLoraxClass):
|
||||
|
||||
def make_dracut_initramfs(self):
|
||||
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
|
||||
logger.debug("chrooting into installtree to create initramfs.img")
|
||||
subprocess.check_call(["chroot", self.root, "/sbin/dracut",
|
||||
"--nomdadmconf", "--nolvmconf",
|
||||
"--xz", "--modules", "base dmsquash-live",
|
||||
outfile, kernel.version])
|
||||
"--noprefix", "--nomdadmconf", "--nolvmconf",
|
||||
"--xz", "--modules", "base dmsquash-live"] \
|
||||
+ hook_commands \
|
||||
+ [outfile, kernel.version])
|
||||
# move output file into installtree workdir
|
||||
dstdir = joinpaths(self.workdir, "dracut-%s" % kernel.version)
|
||||
os.makedirs(dstdir)
|
||||
|
@ -117,7 +117,7 @@ class LoraxYumHelper(object):
|
||||
if os.path.islink(fname) or os.path.isfile(fname):
|
||||
os.unlink(fname)
|
||||
else:
|
||||
shutil.rmtree(fname)
|
||||
shutil.rmtree(fname, ignore_errors=True)
|
||||
|
||||
logger.debug("removed {0}".format(fname))
|
||||
count += 1
|
||||
|
145
src/sbin/mkefiboot
Executable file
145
src/sbin/mkefiboot
Executable 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)
|
Loading…
Reference in New Issue
Block a user