]> code.delx.au - refind/blob - install.sh
Fixed bug in install.sh and mkrlconf.sh that caused mangling of
[refind] / install.sh
1 #!/bin/bash
2 #
3 # Linux/MacOS X script to install rEFInd
4 #
5 # Usage:
6 #
7 # ./install.sh [options]
8 #
9 # options include:
10 # "--esp" to install to the ESP rather than to the system's root
11 # filesystem. This is the default on Linux.
12 # "--usedefault {devicefile}" to install as default
13 # (/EFI/BOOT/BOOTX64.EFI and similar) to the specified device
14 # (/dev/sdd1 or whatever) without registering with the NVRAM.
15 # "--ownhfs {devicefile}" to install to an HFS+ volume that's NOT currently
16 # an OS X boot volume.
17 # "--root {dir}" to specify installation using the specified directory
18 # as the system's root
19 # "--alldrivers" to install all drivers along with regular files
20 # "--nodrivers" to suppress driver installation (default in Linux is
21 # driver used on /boot; --nodrivers is OS X default)
22 # "--shim {shimfile}" to install a shim.efi file for Secure Boot
23 # "--preloader" is synonymous with "--shim"
24 # "--localkeys" to re-sign x86-64 binaries with a locally-generated key
25 # "--yes" to assume a "yes" response to all prompts
26 #
27 # The "esp" option is valid only on Mac OS X; it causes
28 # installation to the EFI System Partition (ESP) rather than
29 # to the current OS X boot partition. Under Linux, this script
30 # installs to the ESP by default.
31 #
32 # This program is copyright (c) 2012 by Roderick W. Smith
33 # It is released under the terms of the GNU GPL, version 3,
34 # a copy of which should be included in the file COPYING.txt.
35 #
36 # Revision history:
37 #
38 # 0.7.7 -- Fixed bug that created mangled refind_linux.conf file
39 # 0.7.6 -- Added --ownhfs {device-filename} option
40 # 0.7.5 -- Fixed bug when installing to ESP on recent versions of OS X
41 # 0.7.2 -- Fixed code that could be confused by use of autofs to mount the ESP
42 # 0.7.0 -- Added support for the new Btrfs driver
43 # 0.6.12 -- Added support for PreLoader as well as for shim
44 # 0.6.11 -- Improvements in script's ability to handle directories with spaces
45 # in their names
46 # 0.6.9 -- Install gptsync on Macs
47 # 0.6.8 -- Bug fix: ESP scan now uses "uniq".
48 # 0.6.6 -- Bug fix: Upgrade drivers when installed to EFI/BOOT. Also enable
49 # copying shim.efi and MokManager.efi over themselves.
50 # 0.6.4 -- Copies ext2 driver rather than ext4 driver for ext2/3fs
51 # 0.6.3 -- Support for detecting rEFInd in EFI/BOOT and EFI/Microsoft/Boot
52 # directories & for installing to EFI/BOOT in BIOS mode
53 # 0.6.2-1 -- Added --yes option & tweaked key-copying for use with RPM install script
54 # 0.6.1 -- Added --root option; minor bug fixes
55 # 0.6.0 -- Changed --drivers to --alldrivers and added --nodrivers option;
56 # changed default driver installation behavior in Linux to install
57 # the driver needed to read /boot (if available)
58 # 0.5.1.2 -- Fixed bug that caused failure to generate refind_linux.conf file
59 # 0.5.1.1 -- Fixed bug that caused script failure under OS X
60 # 0.5.1 -- Added --shim & --localkeys options & create sample refind_linux.conf
61 # in /boot
62 # 0.5.0 -- Added --usedefault & --drivers options & changed "esp" option to "--esp"
63 # 0.4.5 -- Fixed check for rEFItBlesser in OS X
64 # 0.4.2 -- Added notice about BIOS-based OSes & made NVRAM changes in Linux smarter
65 # 0.4.1 -- Added check for rEFItBlesser in OS X
66 # 0.3.3.1 -- Fixed OS X 10.7 bug; also works as make target
67 # 0.3.2.1 -- Check for presence of source files; aborts if not present
68 # 0.3.2 -- Initial version
69 #
70 # Note: install.sh version numbers match those of the rEFInd package
71 # with which they first appeared.
72
73 RootDir="/"
74 TargetDir=/EFI/refind
75 LocalKeysBase="refind_local"
76 ShimSource="none"
77 ShimType="none"
78 TargetShim="default"
79 TargetX64="refind_x64.efi"
80 TargetIA32="refind_ia32.efi"
81 LocalKeys=0
82 DeleteRefindDir=0
83 AlwaysYes=0
84
85 #
86 # Functions used by both OS X and Linux....
87 #
88
89 GetParams() {
90 InstallToEspOnMac=0
91 if [[ $OSName == "Linux" ]] ; then
92 # Install the driver required to read /boot, if it's available
93 InstallDrivers="boot"
94 else
95 InstallDrivers="none"
96 fi
97 while [[ $# -gt 0 ]]; do
98 case $1 in
99 --esp | --ESP) InstallToEspOnMac=1
100 ;;
101 --ownhfs) OwnHfs=1
102 TargetPart="$2"
103 TargetDir=/System/Library/CoreServices
104 shift
105 ;;
106 --usedefault) TargetDir=/EFI/BOOT
107 TargetPart="$2"
108 TargetX64="bootx64.efi"
109 TargetIA32="bootia32.efi"
110 shift
111 ;;
112 --root) RootDir="$2"
113 shift
114 ;;
115 --localkeys) LocalKeys=1
116 ;;
117 --shim | --preloader) ShimSource="$2"
118 ShimType=`basename $ShimSource`
119 shift
120 ;;
121 --drivers | --alldrivers) InstallDrivers="all"
122 ;;
123 --nodrivers) InstallDrivers="none"
124 ;;
125 --yes) AlwaysYes=1
126 ;;
127 * ) echo "Usage: $0 [--esp | --usedefault {device-file} | --root {directory} |"
128 echo " --ownhfs {device-file} ]"
129 echo " [--nodrivers | --alldrivers] [--shim {shim-filename}]"
130 echo " [--localkeys] [--yes]"
131 exit 1
132 esac
133 shift
134 done
135
136 if [[ $InstallToEspOnMac == 1 && "$TargetDir" == '/EFI/BOOT' ]] ; then
137 echo "You may use --esp OR --usedefault, but not both! Aborting!"
138 exit 1
139 fi
140 if [[ "$RootDir" != '/' && "$TargetDir" == '/EFI/BOOT' ]] ; then
141 echo "You may use --usedefault OR --root, but not both! Aborting!"
142 exit 1
143 fi
144 if [[ "$RootDir" != '/' && $InstallToEspOnMac == 1 ]] ; then
145 echo "You may use --root OR --esp, but not both! Aborting!"
146 exit 1
147 fi
148 if [[ "$TargetDir" != '/System/Library/CoreServices' && "$OwnHfs" == '1' ]] ; then
149 echo "If you use --ownhfs, you may NOT use --usedefault! Aborting!"
150 exit 1
151 fi
152
153 RLConfFile="$RootDir/boot/refind_linux.conf"
154 EtcKeysDir="$RootDir/etc/refind.d/keys"
155 } # GetParams()
156
157 # Get a yes/no response from the user and place it in the YesNo variable.
158 # If the AlwaysYes variable is set to 1, skip the user input and set "Y"
159 # in the YesNo variable.
160 ReadYesNo() {
161 if [[ $AlwaysYes == 1 ]] ; then
162 YesNo="Y"
163 echo "Y"
164 else
165 read YesNo
166 fi
167 }
168
169 # Abort if the rEFInd files can't be found.
170 # Also sets $ConfFile to point to the configuration file,
171 # $IconsDir to point to the icons directory, and
172 # $ShimSource to the source of the shim.efi file (if necessary).
173 CheckForFiles() {
174 # Note: This check is satisfied if EITHER the 32- or the 64-bit version
175 # is found, even on the wrong platform. This is because the platform
176 # hasn't yet been determined. This could obviously be improved, but it
177 # would mean restructuring lots more code....
178 if [[ ! -f "$RefindDir/refind_ia32.efi" && ! -f "$RefindDir/refind_x64.efi" ]] ; then
179 echo "The rEFInd binary file is missing! Aborting installation!"
180 exit 1
181 fi
182
183 if [[ -f "$RefindDir/refind.conf-sample" ]] ; then
184 ConfFile="$RefindDir/refind.conf-sample"
185 elif [[ -f "$ThisDir/refind.conf-sample" ]] ; then
186 ConfFile="$ThisDir/refind.conf-sample"
187 else
188 echo "The sample configuration file is missing! Aborting installation!"
189 exit 1
190 fi
191
192 if [[ -d "$RefindDir/icons" ]] ; then
193 IconsDir="$RefindDir/icons"
194 elif [[ -d "$ThisDir/icons" ]] ; then
195 IconsDir="$ThisDir/icons"
196 else
197 echo "The icons directory is missing! Aborting installation!"
198 exit 1
199 fi
200
201 if [[ "$ShimSource" != "none" ]] ; then
202 if [[ -f "$ShimSource" ]] ; then
203 if [[ $ShimType == "shimx64.efi" || $ShimType == "shim.efi" ]] ; then
204 TargetX64="grubx64.efi"
205 MokManagerSource=`dirname "$ShimSource"`/MokManager.efi
206 elif [[ $ShimType == "preloader.efi" || $ShimType == "PreLoader.efi" ]] ; then
207 TargetX64="loader.efi"
208 MokManagerSource=`dirname "$ShimSource"`/HashTool.efi
209 else
210 echo "Unknown shim/PreBootloader filename: $ShimType!"
211 echo "Known filenames are shimx64.efi, shim.efi, and PreLoader.efi. Aborting!"
212 exit 1
213 fi
214 else
215 echo "The specified shim/PreBootloader file, $ShimSource, doesn't exist!"
216 echo "Aborting installation!"
217 exit 1
218 fi
219 fi
220 } # CheckForFiles()
221
222 # Helper for CopyRefindFiles; copies shim files (including MokManager, if it's
223 # available) to target.
224 CopyShimFiles() {
225 cp -fb "$ShimSource" "$InstallDir/$TargetDir/$TargetShim"
226 if [[ $? != 0 ]] ; then
227 Problems=1
228 fi
229 if [[ -f "$MokManagerSource" ]] ; then
230 cp -fb "$MokManagerSource" "$InstallDir/$TargetDir/"
231 fi
232 if [[ $? != 0 ]] ; then
233 Problems=1
234 fi
235 } # CopyShimFiles()
236
237 # Copy the public keys to the installation medium
238 CopyKeys() {
239 if [[ $LocalKeys == 1 ]] ; then
240 mkdir -p "$InstallDir/$TargetDir/keys/"
241 cp "$EtcKeysDir/$LocalKeysBase.cer" "$InstallDir/$TargetDir/keys/"
242 cp "$EtcKeysDir/$LocalKeysBase.crt" "$InstallDir/$TargetDir/keys/"
243 fi
244 } # CopyKeys()
245
246 # Copy drivers from $RefindDir/drivers_$1 to $InstallDir/$TargetDir/drivers_$1,
247 # honoring the $InstallDrivers condition. Must be passed a suitable
248 # architecture code (ia32 or x64).
249 CopyDrivers() {
250 if [[ $InstallDrivers == "all" ]] ; then
251 mkdir -p "$InstallDir/$TargetDir/drivers_$1"
252 cp "$ThisDir"/drivers_$1/*_$1.efi "$InstallDir/$TargetDir/drivers_$1/" 2> /dev/null
253 cp "$RefindDir"/drivers_$1/*_$1.efi "$InstallDir/$TargetDir/drivers_$1/" 2> /dev/null
254 elif [[ "$InstallDrivers" == "boot" && -x `which blkid` ]] ; then
255 BootPart=`df /boot | grep dev | cut -f 1 -d " "`
256 BootFS=`blkid -o export $BootPart 2> /dev/null | grep TYPE= | cut -f 2 -d =`
257 DriverType=""
258 case $BootFS in
259 ext2 | ext3) DriverType="ext2"
260 # Could use ext4, but that can create unwanted entries from symbolic
261 # links in / to /boot/vmlinuz if a separate /boot partition is used.
262 ;;
263 ext4) DriverType="ext4"
264 ;;
265 reiserfs) DriverType="reiserfs"
266 ;;
267 btrfs) DriverType="btrfs"
268 ;;
269 hfsplus) DriverType="hfs"
270 ;;
271 *) BootFS=""
272 esac
273 if [[ -n $BootFS ]] ; then
274 echo "Installing driver for $BootFS (${DriverType}_$1.efi)"
275 mkdir -p "$InstallDir/$TargetDir/drivers_$1"
276 cp "$ThisDir/drivers_$1/${DriverType}_$1.efi" "$InstallDir/$TargetDir/drivers_$1/" 2> /dev/null
277 cp "$RefindDir/drivers_$1/${DriverType}_$1.efi" "$InstallDir/$TargetDir/drivers_$1"/ 2> /dev/null
278 fi
279 fi
280 }
281
282 # Copy tools (currently only gptsync, and that only on Macs) to the EFI/tools
283 # directory on the ESP. Must be passed a suitable architecture code (ia32
284 # or x64).
285 CopyTools() {
286 mkdir -p $InstallDir/EFI/tools
287 if [[ $OSName == 'Darwin' ]] ; then
288 cp -f "$RefindDir/tools_$1/gptsync_$1.efi" "$InstallDir/EFI/tools/"
289 if [[ -f "$InstallDir/EFI/tools/gptsync.efi" ]] ; then
290 mv "$InstallDir/EFI/tools/gptsync.efi" "$InstallDir/EFI/tools/gptsync.efi-disabled"
291 echo "Found old gptsync.efi; disabling it by renaming it to gptsync.efi-disabled"
292 fi
293 fi
294 } # CopyTools()
295
296 # Copy the rEFInd files to the ESP or OS X root partition.
297 # Sets Problems=1 if any critical commands fail.
298 CopyRefindFiles() {
299 mkdir -p "$InstallDir/$TargetDir"
300 if [[ "$TargetDir" == '/EFI/BOOT' ]] ; then
301 cp "$RefindDir/refind_ia32.efi" "$InstallDir/$TargetDir/$TargetIA32" 2> /dev/null
302 if [[ $? != 0 ]] ; then
303 echo "Note: IA32 (x86) binary not installed!"
304 fi
305 cp "$RefindDir/refind_x64.efi" "$InstallDir/$TargetDir/$TargetX64" 2> /dev/null
306 if [[ $? != 0 ]] ; then
307 Problems=1
308 fi
309 if [[ "$ShimSource" != "none" ]] ; then
310 TargetShim="bootx64.efi"
311 CopyShimFiles
312 fi
313 if [[ $InstallDrivers == "all" ]] ; then
314 cp -r "$RefindDir"/drivers_* "$InstallDir/$TargetDir/" 2> /dev/null
315 cp -r "$ThisDir"/drivers_* "$InstallDir/$TargetDir/" 2> /dev/null
316 elif [[ $Upgrade == 1 ]] ; then
317 if [[ $Platform == 'EFI64' ]] ; then
318 CopyDrivers x64
319 CopyTools x64
320 else
321 CopyDrivers ia32
322 CopyTools ia32
323 fi
324 fi
325 Refind=""
326 CopyKeys
327 elif [[ $Platform == 'EFI64' || $TargetDir == "/EFI/Microsoft/Boot" ]] ; then
328 cp "$RefindDir/refind_x64.efi" "$InstallDir/$TargetDir/$TargetX64"
329 if [[ $? != 0 ]] ; then
330 Problems=1
331 fi
332 CopyDrivers x64
333 CopyTools x64
334 Refind="refind_x64.efi"
335 CopyKeys
336 if [[ "$ShimSource" != "none" ]] ; then
337 if [[ "$TargetShim" == "default" ]] ; then
338 TargetShim=`basename "$ShimSource"`
339 fi
340 CopyShimFiles
341 Refind="$TargetShim"
342 if [[ $LocalKeys == 0 ]] ; then
343 echo "Storing copies of rEFInd Secure Boot public keys in $EtcKeysDir"
344 mkdir -p "$EtcKeysDir"
345 cp "$ThisDir/keys/refind.cer" "$EtcKeysDir" 2> /dev/null
346 cp "$ThisDir/keys/refind.crt" "$EtcKeysDir" 2> /dev/null
347 fi
348 fi
349 if [[ "$TargetDir" == '/System/Library/CoreServices' ]] ; then
350 SetupMacHfs $TargetX64
351 fi
352 elif [[ $Platform == 'EFI32' ]] ; then
353 cp "$RefindDir/refind_ia32.efi" "$InstallDir/$TargetDir/$TargetIA32"
354 if [[ $? != 0 ]] ; then
355 Problems=1
356 fi
357 CopyDrivers ia32
358 CopyTools ia32
359 Refind="refind_ia32.efi"
360 if [[ "$TargetDir" == '/System/Library/CoreServices' ]] ; then
361 SetupMacHfs $TargetIA32
362 fi
363 else
364 echo "Unknown platform! Aborting!"
365 exit 1
366 fi
367 echo "Copied rEFInd binary files"
368 echo ""
369 if [[ -d "$InstallDir/$TargetDir/icons" ]] ; then
370 rm -rf "$InstallDir/$TargetDir/icons-backup" &> /dev/null
371 mv -f "$InstallDir/$TargetDir/icons" "$InstallDir/$TargetDir/icons-backup"
372 echo "Notice: Backed up existing icons directory as icons-backup."
373 fi
374 cp -r "$IconsDir" "$InstallDir/$TargetDir"
375 if [[ $? != 0 ]] ; then
376 Problems=1
377 fi
378 mkdir -p "$InstallDir/$TargetDir/keys"
379 cp -rf "$ThisDir"/keys/*.[cd]er "$InstallDir/$TargetDir/keys/" 2> /dev/null
380 cp -rf "$EtcKeysDir"/*.[cd]er "$InstallDir/$TargetDir/keys/" 2> /dev/null
381 if [[ -f "$InstallDir/$TargetDir/refind.conf" ]] ; then
382 echo "Existing refind.conf file found; copying sample file as refind.conf-sample"
383 echo "to avoid overwriting your customizations."
384 echo ""
385 cp -f "$ConfFile" "$InstallDir/$TargetDir"
386 if [[ $? != 0 ]] ; then
387 Problems=1
388 fi
389 else
390 echo "Copying sample configuration file as refind.conf; edit this file to configure"
391 echo "rEFInd."
392 echo ""
393 cp -f "$ConfFile" "$InstallDir/$TargetDir/refind.conf"
394 if [[ $? != 0 ]] ; then
395 Problems=1
396 fi
397 fi
398 if [[ $DeleteRefindDir == 1 ]] ; then
399 echo "Deleting the temporary directory $RefindDir"
400 rm -r "$RefindDir"
401 fi
402 } # CopyRefindFiles()
403
404 # Mount the partition the user specified with the --usedefault or --ownhfs option
405 MountDefaultTarget() {
406 InstallDir=/tmp/refind_install
407 mkdir -p "$InstallDir"
408 UnmountEsp=1
409 if [[ $OSName == 'Darwin' ]] ; then
410 if [[ $OwnHfs == '1' ]] ; then
411 Temp=`diskutil info "$TargetPart" | grep "Mount Point"`
412 InstallDir=`echo $Temp | cut -f 3-30 -d ' '`
413 if [[ $InstallDir == '' ]] ; then
414 InstallDir=/tmp/refind_install
415 mount -t hfs "$TargetPart" "$InstallDir"
416 else
417 UnmountEsp=0
418 fi
419 else
420 mount -t msdos "$TargetPart" "$InstallDir"
421 fi
422 elif [[ $OSName == 'Linux' ]] ; then
423 mount -t vfat "$TargetPart" "$InstallDir"
424 fi
425 if [[ $? != 0 ]] ; then
426 echo "Couldn't mount $TargetPart ! Aborting!"
427 rmdir "$InstallDir"
428 exit 1
429 fi
430 echo "UnmountEsp = $UnmountEsp"
431 } # MountDefaultTarget()
432
433 #
434 # A series of OS X support functions....
435 #
436
437 # Mount the ESP at /Volumes/ESP or determine its current mount
438 # point.
439 # Sets InstallDir to the ESP mount point
440 # Sets UnmountEsp if we mounted it
441 MountOSXESP() {
442 # Identify the ESP. Note: This returns the FIRST ESP found;
443 # if the system has multiple disks, this could be wrong!
444 Temp=`diskutil list | grep " EFI " | grep -o 'disk.*'`
445 Esp=/dev/`echo $Temp`
446 # If the ESP is mounted, use its current mount point....
447 Temp=`df -P | grep "$Esp"`
448 InstallDir=`echo $Temp | cut -f 6 -d ' '`
449 if [[ "$InstallDir" == '' ]] ; then
450 mkdir /Volumes/ESP &> /dev/null
451 mount -t msdos "$Esp" /Volumes/ESP
452 if [[ $? != 0 ]] ; then
453 echo "Unable to mount ESP! Aborting!\n"
454 exit 1
455 fi
456 UnmountEsp=1
457 InstallDir="/Volumes/ESP"
458 fi
459 } # MountOSXESP()
460
461 # Set up for booting from Mac HFS+ volume that boots rEFInd in MJG's way
462 # (http://mjg59.dreamwidth.org/7468.html)
463 # Must be passed the original rEFInd binary filename (without a path).
464 SetupMacHfs() {
465 if [[ -s "$InstallDir/mach_kernel" ]] ; then
466 echo "Attempt to install rEFInd to a partition with a /mach_kernel file! Aborting!"
467 exit 1
468 fi
469 cp -n "$InstallDir/$TargetDir/boot.efi" "$InstallDir/$TargetDir/boot.efi-backup" &> /dev/null
470 ln -f "$InstallDir/$TargetDir/$1" "$InstallDir/$TargetDir/boot.efi"
471 touch "$InstallDir/mach_kernel"
472 cp -n "$RefindDir/icons/os_refind.icns" "$InstallDir/.VolumeIcon.icns" &> /dev/null
473 rm "$InstallDir/$TargetDir/SystemVersion.plist" &> /dev/null
474 cat - << ENDOFHERE >> "$InstallDir/$TargetDir/SystemVersion.plist"
475 <xml version="1.0" encoding="UTF-8"?>
476 <plist version="1.0">
477 <dict>
478 <key>ProductBuildVersion</key>
479 <string></string>
480 <key>ProductName</key>
481 <string>rEFInd</string>
482 <key>ProductVersion</key>
483 <string>0.7.6</string>
484 </dict>
485 </plist>
486 ENDOFHERE
487 } # SetupMacHfs()
488
489 # Control the OS X installation.
490 # Sets Problems=1 if problems found during the installation.
491 InstallOnOSX() {
492 echo "Installing rEFInd on OS X...."
493 if [[ "$TargetDir" == "/EFI/BOOT" || "$OwnHfs" == '1' ]] ; then
494 MountDefaultTarget
495 elif [[ "$InstallToEspOnMac" == "1" ]] ; then
496 MountOSXESP
497 else
498 InstallDir="$RootDir/"
499 fi
500 echo "Installing rEFInd to the partition mounted at $InstallDir"
501 Platform=`ioreg -l -p IODeviceTree | grep firmware-abi | cut -d "\"" -f 4`
502 CopyRefindFiles
503 if [[ $InstallToEspOnMac == "1" ]] ; then
504 bless --mount "$InstallDir" --setBoot --file "$InstallDir/$TargetDir/$Refind"
505 elif [[ "$TargetDir" != "/EFI/BOOT" ]] ; then
506 bless --setBoot --folder "$InstallDir/$TargetDir" --file "$InstallDir/$TargetDir/$Refind"
507 fi
508 if [[ $? != 0 ]] ; then
509 Problems=1
510 fi
511 if [[ -f /Library/StartupItems/rEFItBlesser || -d /Library/StartupItems/rEFItBlesser ]] ; then
512 echo
513 echo "/Library/StartupItems/rEFItBlesser found!"
514 echo "This program is part of rEFIt, and will cause rEFInd to fail to work after"
515 echo -n "its first boot. Do you want to remove rEFItBlesser (Y/N)? "
516 ReadYesNo
517 if [[ $YesNo == "Y" || $YesNo == "y" ]] ; then
518 echo "Deleting /Library/StartupItems/rEFItBlesser..."
519 rm -r /Library/StartupItems/rEFItBlesser
520 else
521 echo "Not deleting rEFItBlesser."
522 fi
523 fi
524 echo
525 echo "WARNING: If you have an Advanced Format disk, *DO NOT* attempt to check the"
526 echo "bless status with 'bless --info', since this is known to cause disk corruption"
527 echo "on some systems!!"
528 echo
529 } # InstallOnOSX()
530
531
532 #
533 # Now a series of Linux support functions....
534 #
535
536 # Check for evidence that we're running in Secure Boot mode. If so, and if
537 # appropriate options haven't been set, warn the user and offer to abort.
538 # If we're NOT in Secure Boot mode but the user HAS specified the --shim
539 # or --localkeys option, warn the user and offer to abort.
540 #
541 # FIXME: Although I checked the presence (and lack thereof) of the
542 # /sys/firmware/efi/vars/SecureBoot* files on my Secure Boot test system
543 # before releasing this script, I've since found that they are at least
544 # sometimes present when Secure Boot is absent. This means that the first
545 # test can produce false alarms. A better test is highly desirable.
546 CheckSecureBoot() {
547 VarFile=`ls -d /sys/firmware/efi/vars/SecureBoot* 2> /dev/null`
548 if [[ -n "$VarFile" && "$TargetDir" != '/EFI/BOOT' && "$ShimSource" == "none" ]] ; then
549 echo ""
550 echo "CAUTION: Your computer appears to support Secure Boot, but you haven't"
551 echo "specified a valid shim.efi file source. If you've disabled Secure Boot and"
552 echo "intend to leave it disabled, this is fine; but if Secure Boot is active, the"
553 echo "resulting installation won't boot. You can read more about this topic at"
554 echo "http://www.rodsbooks.com/refind/secureboot.html."
555 echo ""
556 echo -n "Do you want to proceed with installation (Y/N)? "
557 ReadYesNo
558 if [[ $YesNo == "Y" || $YesNo == "y" ]] ; then
559 echo "OK; continuing with the installation..."
560 else
561 exit 0
562 fi
563 fi
564
565 if [[ "$ShimSource" != "none" && ! -n "$VarFile" ]] ; then
566 echo ""
567 echo "You've specified installing using a shim.efi file, but your computer does not"
568 echo "appear to be running in Secure Boot mode. Although installing in this way"
569 echo "should work, it's unnecessarily complex. You may continue, but unless you"
570 echo "plan to enable Secure Boot, you should consider stopping and omitting the"
571 echo "--shim option. You can read more about this topic at"
572 echo "http://www.rodsbooks.com/refind/secureboot.html."
573 echo ""
574 echo -n "Do you want to proceed with installation (Y/N)? "
575 ReadYesNo
576 if [[ $YesNo == "Y" || $YesNo == "y" ]] ; then
577 echo "OK; continuing with the installation..."
578 else
579 exit 0
580 fi
581 fi
582
583 if [[ $LocalKeys != 0 && ! -n "$VarFile" ]] ; then
584 echo ""
585 echo "You've specified re-signing your rEFInd binaries with locally-generated keys,"
586 echo "but your computer does not appear to be running in Secure Boot mode. The"
587 echo "keys you generate will be useless unless you enable Secure Boot. You may"
588 echo "proceed with this installation, but before you do so, you may want to read"
589 echo "more about it at http://www.rodsbooks.com/refind/secureboot.html."
590 echo ""
591 echo -n "Do you want to proceed with installation (Y/N)? "
592 ReadYesNo
593 if [[ $YesNo == "Y" || $YesNo == "y" ]] ; then
594 echo "OK; continuing with the installation..."
595 else
596 exit 0
597 fi
598 fi
599
600 } # CheckSecureBoot()
601
602 # Check for the presence of locally-generated keys from a previous installation in
603 # $EtcKeysDir (/etc/refind.d/keys). If they're not present, generate them using
604 # openssl.
605 GenerateKeys() {
606 PrivateKey="$EtcKeysDir/$LocalKeysBase.key"
607 CertKey="$EtcKeysDir/$LocalKeysBase.crt"
608 DerKey="$EtcKeysDir/$LocalKeysBase.cer"
609 OpenSSL=`which openssl 2> /dev/null`
610
611 # Do the work only if one or more of the necessary keys is missing
612 # TODO: Technically, we don't need the DerKey; but if it's missing and openssl
613 # is also missing, this will fail. This could be improved.
614 if [[ ! -f "$PrivateKey" || ! -f "$CertKey" || ! -f "$DerKey" ]] ; then
615 echo "Generating a fresh set of local keys...."
616 mkdir -p "$EtcKeysDir"
617 chmod 0700 "$EtcKeysDir"
618 if [[ ! -x "$OpenSSL" ]] ; then
619 echo "Can't find openssl, which is required to create your private signing keys!"
620 echo "Aborting!"
621 exit 1
622 fi
623 if [[ -f "$PrivateKey" ]] ; then
624 echo "Backing up existing $PrivateKey"
625 cp -f "$PrivateKey" "$PrivateKey.backup" 2> /dev/null
626 fi
627 if [[ -f "$CertKey" ]] ; then
628 echo "Backing up existing $CertKey"
629 cp -f "$CertKey" "$CertKey.backup" 2> /dev/null
630 fi
631 if [[ -f "$DerKey" ]] ; then
632 echo "Backing up existing $DerKey"
633 cp -f "$DerKey" "$DerKey.backup" 2> /dev/null
634 fi
635 "$OpenSSL" req -new -x509 -newkey rsa:2048 -keyout "$PrivateKey" -out "$CertKey" \
636 -nodes -days 3650 -subj "/CN=Locally-generated rEFInd key/"
637 "$OpenSSL" x509 -in "$CertKey" -out "$DerKey" -outform DER
638 chmod 0600 "$PrivateKey"
639 else
640 echo "Using existing local keys...."
641 fi
642 }
643
644 # Sign a single binary. Requires parameters:
645 # $1 = source file
646 # $2 = destination file
647 # Also assumes that the SBSign, PESign, UseSBSign, UsePESign, and various key variables are set
648 # appropriately.
649 # Aborts script on error
650 SignOneBinary() {
651 $SBSign --key "$PrivateKey" --cert "$CertKey" --output "$2" "$1"
652 if [[ $? != 0 ]] ; then
653 echo "Problem signing the binary $1! Aborting!"
654 exit 1
655 fi
656 }
657
658 # Re-sign the x86-64 binaries with a locally-generated key, First look for appropriate
659 # key files in $EtcKeysDir. If they're present, use them to re-sign the binaries. If
660 # not, try to generate new keys and store them in $EtcKeysDir.
661 ReSignBinaries() {
662 SBSign=`which sbsign 2> /dev/null`
663 echo "Found sbsign at $SBSign"
664 TempDir="/tmp/refind_local"
665 if [[ ! -x "$SBSign" ]] ; then
666 echo "Can't find sbsign, which is required to sign rEFInd with your own keys!"
667 echo "Aborting!"
668 exit 1
669 fi
670 GenerateKeys
671 mkdir -p "$TempDir/drivers_x64"
672 cp "$RefindDir/refind.conf-sample $TempDir" 2> /dev/null
673 cp "$ThisDir/refind.conf-sample $TempDir" 2> /dev/null
674 cp "$RefindDir/refind_ia32.efi $TempDir" 2> /dev/null
675 cp -a "$RefindDir/drivers_ia32 $TempDir" 2> /dev/null
676 cp -a "$ThisDir/drivers_ia32 $TempDir" 2> /dev/null
677 SignOneBinary "$RefindDir/refind_x64.efi" "$TempDir/refind_x64.efi"
678 SaveIFS=$IFS
679 IFS=$(echo -en "\n\b")
680 for Driver in `ls "$RefindDir"/drivers_x64/*.efi "$ThisDir"/drivers_x64/*.efi 2> /dev/null` ; do
681 TempName=`basename "$Driver"`
682 SignOneBinary "$Driver" "$TempDir/drivers_x64/$TempName"
683 done
684 IFS=$SaveIFS
685 RefindDir="$TempDir"
686 DeleteRefindDir=1
687 }
688
689 # Identifies the ESP's location (/boot or /boot/efi, or these locations under
690 # the directory specified by --root); aborts if the ESP isn't mounted at
691 # either location.
692 # Sets InstallDir to the ESP mount point.
693 FindLinuxESP() {
694 EspLine=`df "$RootDir/boot/efi" 2> /dev/null | grep boot/efi`
695 if [[ ! -n "$EspLine" ]] ; then
696 EspLine=`df "$RootDir"/boot | grep boot`
697 fi
698 InstallDir=`echo $EspLine | cut -d " " -f 6`
699 if [[ -n "$InstallDir" ]] ; then
700 EspFilesystem=`grep "$InstallDir" /etc/mtab | uniq | grep -v autofs | cut -d " " -f 3`
701 fi
702 if [[ $EspFilesystem != 'vfat' ]] ; then
703 echo "$RootDir/boot/efi doesn't seem to be on a VFAT filesystem. The ESP must be"
704 echo "mounted at $RootDir/boot or $RootDir/boot/efi and it must be VFAT! Aborting!"
705 exit 1
706 fi
707 echo "ESP was found at $InstallDir using $EspFilesystem"
708 } # FindLinuxESP
709
710 # Uses efibootmgr to add an entry for rEFInd to the EFI's NVRAM.
711 # If this fails, sets Problems=1
712 AddBootEntry() {
713 InstallIt="0"
714 Efibootmgr=`which efibootmgr 2> /dev/null`
715 if [[ "$Efibootmgr" ]] ; then
716 InstallDisk=`grep "$InstallDir" /etc/mtab | cut -d " " -f 1 | cut -c 1-8`
717 PartNum=`grep "$InstallDir" /etc/mtab | cut -d " " -f 1 | cut -c 9-10`
718 EntryFilename="$TargetDir/$Refind"
719 EfiEntryFilename=`echo ${EntryFilename//\//\\\}`
720 EfiEntryFilename2=`echo ${EfiEntryFilename} | sed s/\\\\\\\\/\\\\\\\\\\\\\\\\/g`
721 ExistingEntry=`"$Efibootmgr" -v | grep -i "$EfiEntryFilename2"`
722
723 if [[ "$ExistingEntry" ]] ; then
724 ExistingEntryBootNum=`echo "$ExistingEntry" | cut -c 5-8`
725 FirstBoot=`"$Efibootmgr" | grep BootOrder | cut -c 12-15`
726 if [[ "$ExistingEntryBootNum" != "$FirstBoot" ]] ; then
727 echo "An existing rEFInd boot entry exists, but isn't set as the default boot"
728 echo "manager. The boot order is being adjusted to make rEFInd the default boot"
729 echo "manager. If this is NOT what you want, you should use efibootmgr to"
730 echo "manually adjust your EFI's boot order."
731 "$Efibootmgr" -b $ExistingEntryBootNum -B &> /dev/null
732 InstallIt="1"
733 fi
734 else
735 InstallIt="1"
736 fi
737
738 if [[ $InstallIt == "1" ]] ; then
739 echo "Installing it!"
740 "$Efibootmgr" -c -l "$EfiEntryFilename" -L "rEFInd Boot Manager" -d $InstallDisk -p $PartNum &> /dev/null
741 if [[ $? != 0 ]] ; then
742 EfibootmgrProblems=1
743 Problems=1
744 fi
745 fi
746
747 else # efibootmgr not found
748 EfibootmgrProblems=1
749 Problems=1
750 fi
751
752 if [[ $EfibootmgrProblems ]] ; then
753 echo
754 echo "ALERT: There were problems running the efibootmgr program! You may need to"
755 echo "rename the $Refind binary to the default name (EFI/boot/bootx64.efi"
756 echo "on x86-64 systems or EFI/boot/bootia32.efi on x86 systems) to have it run!"
757 echo
758 else
759 echo "rEFInd has been set as the default boot manager."
760 fi
761 } # AddBootEntry()
762
763 # Create a minimal/sample refind_linux.conf file in /boot.
764 GenerateRefindLinuxConf() {
765 if [[ -f "$RLConfFile" ]] ; then
766 echo "Existing $RLConfFile found; not overwriting."
767 else
768 echo "Creating $RLConfFile; edit it to adjust kernel options."
769 if [[ -f "$RootDir/etc/default/grub" ]] ; then
770 # We want the default options used by the distribution, stored here....
771 source "$RootDir/etc/default/grub"
772 echo "Setting default boot options based on $RootDir/etc/default/grub"
773 fi
774 RootFS=`df "$RootDir" | grep dev | cut -f 1 -d " "`
775 StartOfDevname=`echo "$RootFS" | cut -b 1-7`
776 if [[ "$StartOfDevname" == "/dev/sd" || "$StartOfDevName" == "/dev/hd" ]] ; then
777 # Identify root filesystem by UUID rather than by device node, if possible
778 Uuid=`blkid -o export -s UUID "$RootFS" 2> /dev/null | grep UUID=`
779 if [[ -n $Uuid ]] ; then
780 RootFS="$Uuid"
781 fi
782 fi
783 DefaultOptions="$GRUB_CMDLINE_LINUX $GRUB_CMDLINE_LINUX_DEFAULT"
784 echo "\"Boot with standard options\" \"ro root=$RootFS $DefaultOptions \"" > $RLConfFile
785 echo "\"Boot to single-user mode\" \"ro root=$RootFS $DefaultOptions single\"" >> $RLConfFile
786 echo "\"Boot with minimal options\" \"ro root=$RootFS\"" >> $RLConfFile
787 fi
788 }
789
790 # Set varaibles for installation in EFI/BOOT directory
791 SetVarsForBoot() {
792 TargetDir="/EFI/BOOT"
793 if [[ $ShimSource == "none" ]] ; then
794 TargetX64="bootx64.efi"
795 TargetIA32="bootia32.efi"
796 else
797 if [[ $ShimType == "shim.efi" || $ShimType == "shimx64.efi" ]] ; then
798 TargetX64="grubx64.efi"
799 elif [[ $ShimType == "preloader.efi" || $ShimType == "PreLoader.efi" ]] ; then
800 TargetX64="loader.efi"
801 else
802 echo "Unknown shim/PreBootloader type: $ShimType"
803 echo "Aborting!"
804 exit 1
805 fi
806 TargetIA32="bootia32.efi"
807 TargetShim="bootx64.efi"
808 fi
809 } # SetFilenamesForBoot()
810
811 # Set variables for installation in EFI/Microsoft/Boot directory
812 SetVarsForMsBoot() {
813 TargetDir="/EFI/Microsoft/Boot"
814 if [[ $ShimSource == "none" ]] ; then
815 TargetX64="bootmgfw.efi"
816 else
817 if [[ $ShimType == "shim.efi" || $ShimType == "shimx64.efi" ]] ; then
818 TargetX64="grubx64.efi"
819 elif [[ $ShimType == "preloader.efi" || $ShimType == "PreLoader.efi" ]] ; then
820 TargetX64="loader.efi"
821 else
822 echo "Unknown shim/PreBootloader type: $ShimType"
823 echo "Aborting!"
824 exit 1
825 fi
826 TargetShim="bootmgfw.efi"
827 fi
828 }
829
830 # TargetDir defaults to /EFI/refind; however, this function adjusts it as follows:
831 # - If an existing refind.conf is available in /EFI/BOOT or /EFI/Microsoft/Boot,
832 # install to that directory under the suitable name; but DO NOT do this if
833 # refind.conf is also in /EFI/refind.
834 # - If booted in BIOS mode and the ESP lacks any other EFI files, install to
835 # /EFI/BOOT
836 # - If booted in BIOS mode and there's no refind.conf file and there is a
837 # /EFI/Microsoft/Boot/bootmgfw.efi file, move it down one level and
838 # install under that name, "hijacking" the Windows boot loader filename
839 DetermineTargetDir() {
840 Upgrade=0
841
842 if [[ -f $InstallDir/EFI/BOOT/refind.conf ]] ; then
843 SetVarsForBoot
844 Upgrade=1
845 fi
846 if [[ -f $InstallDir/EFI/Microsoft/Boot/refind.conf ]] ; then
847 SetVarsForMsBoot
848 Upgrade=1
849 fi
850 if [[ -f $InstallDir/EFI/refind/refind.conf ]] ; then
851 TargetDir="/EFI/refind"
852 Upgrade=1
853 fi
854 if [[ $Upgrade == 1 ]] ; then
855 echo "Found rEFInd installation in $InstallDir$TargetDir; upgrading it."
856 fi
857
858 if [[ ! -d /sys/firmware/efi && $Upgrade == 0 ]] ; then # BIOS-mode
859 FoundEfiFiles=`find "$InstallDir/EFI/BOOT" -name "*.efi" 2> /dev/null`
860 FoundConfFiles=`find "$InstallDir" -name "refind\.conf" 2> /dev/null`
861 if [[ ! -n "$FoundConfFiles" && -f "$InstallDir/EFI/Microsoft/Boot/bootmgfw.efi" ]] ; then
862 mv -n "$InstallDir/EFI/Microsoft/Boot/bootmgfw.efi" "$InstallDir/EFI/Microsoft" &> /dev/null
863 SetVarsForMsBoot
864 echo "Running in BIOS mode with a suspected Windows installation; moving boot loader"
865 echo "files so as to install to $InstallDir$TargetDir."
866 elif [[ ! -n "$FoundEfiFiles" ]] ; then # In BIOS mode and no default loader; install as default loader
867 SetVarsForBoot
868 echo "Running in BIOS mode with no existing default boot loader; installing to"
869 echo $InstallDir$TargetDir
870 else
871 echo "Running in BIOS mode with an existing default boot loader; backing it up and"
872 echo "installing rEFInd in its place."
873 if [[ -d "$InstallDir/EFI/BOOT-rEFIndBackup" ]] ; then
874 echo ""
875 echo "Caution: An existing backup of a default boot loader exists! If the current"
876 echo "default boot loader and the backup are different boot loaders, the current"
877 echo "one will become inaccessible."
878 echo ""
879 echo -n "Do you want to proceed with installation (Y/N)? "
880 ReadYesNo
881 if [[ $YesNo == "Y" || $YesNo == "y" ]] ; then
882 echo "OK; continuing with the installation..."
883 else
884 exit 0
885 fi
886 fi
887 mv -n "$InstallDir/EFI/BOOT" "$InstallDir/EFI/BOOT-rEFIndBackup"
888 SetVarsForBoot
889 fi
890 fi # BIOS-mode
891 } # DetermineTargetDir()
892
893 # Controls rEFInd installation under Linux.
894 # Sets Problems=1 if something goes wrong.
895 InstallOnLinux() {
896 if [[ "$TargetDir" == "/System/Library/CoreServices" ]] ; then
897 echo "You may not use the --ownhfs option under Linux! Aborting!"
898 exit 1
899 fi
900 echo "Installing rEFInd on Linux...."
901 modprobe efivars &> /dev/null
902 if [[ $TargetDir == "/EFI/BOOT" ]] ; then
903 MountDefaultTarget
904 else
905 FindLinuxESP
906 DetermineTargetDir
907 fi
908 CpuType=`uname -m`
909 if [[ $CpuType == 'x86_64' ]] ; then
910 Platform="EFI64"
911 elif [[ ($CpuType == 'i386' || $CpuType == 'i486' || $CpuType == 'i586' || $CpuType == 'i686') ]] ; then
912 Platform="EFI32"
913 # If we're in EFI mode, do some sanity checks, and alert the user or even
914 # abort. Not in BIOS mode, though, since that could be used on an emergency
915 # disc to try to recover a troubled Linux installation.
916 if [[ -d /sys/firmware/efi ]] ; then
917 if [[ "$ShimSource" != "none" && "$TargetDir" != "/BOOT/EFI" ]] ; then
918 echo ""
919 echo "CAUTION: shim does not currently supports 32-bit systems, so you should not"
920 echo "use the --shim option to install on such systems. Aborting!"
921 echo ""
922 exit 1
923 fi
924 echo
925 echo "CAUTION: This Linux installation uses a 32-bit kernel. 32-bit EFI-based"
926 echo "computers are VERY RARE. If you've installed a 32-bit version of Linux"
927 echo "on a 64-bit computer, you should manually install the 64-bit version of"
928 echo "rEFInd. If you're installing on a Mac, you should do so from OS X. If"
929 echo "you're positive you want to continue with this installation, answer 'Y'"
930 echo "to the following question..."
931 echo
932 echo -n "Are you sure you want to continue (Y/N)? "
933 ReadYesNo
934 if [[ $YesNo == "Y" || $YesNo == "y" ]] ; then
935 echo "OK; continuing with the installation..."
936 else
937 exit 0
938 fi
939 fi # in EFI mode
940 else
941 echo "Unknown CPU type '$CpuType'; aborting!"
942 exit 1
943 fi
944
945 if [[ $LocalKeys == 1 ]] ; then
946 ReSignBinaries
947 fi
948
949 CheckSecureBoot
950 CopyRefindFiles
951 if [[ "$TargetDir" != "/EFI/BOOT" && "$TargetDir" != "/EFI/Microsoft/Boot" ]] ; then
952 AddBootEntry
953 GenerateRefindLinuxConf
954 fi
955 } # InstallOnLinux()
956
957 #
958 # The main part of the script. Sets a few environment variables,
959 # performs a few startup checks, and then calls functions to
960 # install under OS X or Linux, depending on the detected platform.
961 #
962
963 OSName=`uname -s`
964 GetParams "$@"
965 ThisDir="$( cd -P "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
966 RefindDir="$ThisDir/refind"
967 ThisScript="$ThisDir/`basename $0`"
968 if [[ `whoami` != "root" ]] ; then
969 echo "Not running as root; attempting to elevate privileges via sudo...."
970 sudo "$ThisScript" "$@"
971 if [[ $? != 0 ]] ; then
972 echo "This script must be run as root (or using sudo). Exiting!"
973 exit 1
974 else
975 exit 0
976 fi
977 fi
978 CheckForFiles
979 if [[ $OSName == 'Darwin' ]] ; then
980 if [[ "$ShimSource" != "none" ]] ; then
981 echo "The --shim option is not supported on OS X! Exiting!"
982 exit 1
983 fi
984 if [[ "$LocalKeys" != 0 ]] ; then
985 echo "The --localkeys option is not supported on OS X! Exiting!"
986 exit 1
987 fi
988 InstallOnOSX $1
989 elif [[ $OSName == 'Linux' ]] ; then
990 InstallOnLinux
991 else
992 echo "Running on unknown OS; aborting!"
993 fi
994
995 if [[ $Problems ]] ; then
996 echo
997 echo "ALERT:"
998 echo "Installation has completed, but problems were detected. Review the output for"
999 echo "error messages and take corrective measures as necessary. You may need to"
1000 echo "re-run this script or install manually before rEFInd will work."
1001 echo
1002 else
1003 echo
1004 echo "Installation has completed successfully."
1005 echo
1006 fi
1007
1008 if [[ $UnmountEsp == '1' ]] ; then
1009 echo "Unmounting install dir"
1010 umount $InstallDir
1011 fi
1012
1013 if [[ "$InstallDir" == /tmp/refind_install ]] ; then
1014 # sleep 5
1015 rmdir "$InstallDir"
1016 fi