Fixed bug that caused mvrefind to fail to move bootmgfw.efi in some
[refind] / mvrefind
1 #!/bin/bash
2 #
3 # Linux script to move an existing rEFInd installation from one directory to
4 # another
5 #
6 # copyright (c) 2013-2015 by Roderick W. Smith
7 #
8 # This program is licensed under the terms of the GNU GPL, version 3,
9 # or (at your option) any later version.
10 # You should have received a copy of the GNU General Public License
11 # along with this program. If not, see <http://www.gnu.org/licenses/>.
12
13 # Usage:
14 #
15 # ./mvrefind /path/to/source /path/to/destination
16 #
17 # Typically used to "hijack" or "unhijack" a Windows boot loader location or
18 # to help convert a rEFInd installation made in BIOS mode to one that works
19 # in EFI mode.
20 #
21 # Revision history:
22 #
23 # 0.10.2 -- Fixed bug in moving bootmgfw.efi in some situations
24 # 0.10.1 -- Generalized to support ARM64 (aka AARCH64, aa64)
25 # 0.10.0 -- Renamed from mvrefind.sh to mvrefind
26 # 0.6.3 -- Initial release
27 #
28 # Note: mvrefind version numbers match those of the rEFInd package
29 # with which they first appeared.
30
31 RootDir="/"
32 SourceShim="shim.efi"
33 TargetShim=$SourceShim
34 SourceDir=`readlink -f ${1}`
35 TargetDir=`readlink -f ${2}`
36
37 # Identifies the ESP's location (/boot or /boot/efi); aborts if the ESP isn't
38 # mounted at either location. Also splits the ESP location from SourceDir and
39 # TargetDir, leaving them intact but creating new EspSourceDir and EspTargetDir
40 # variables containing only the ESP components thereof. These new variables
41 # are also converted to all-lowercase and any trailing slash is stripped, to
42 # assist in comparisons. (This is reasonable because FAT is case-insensitive.)
43 # Sets InstallDir to the ESP mount point.
44 FindLinuxESP() {
45 EspLine=`df $RootDir/boot/efi 2> /dev/null | grep boot/efi`
46 if [[ ! -n $EspLine ]] ; then
47 EspLine=`df $RootDir/boot | grep boot`
48 fi
49 InstallDir=`echo $EspLine | cut -d " " -f 6`
50 if [[ -n $InstallDir ]] ; then
51 EspFilesystem=`grep $InstallDir /etc/mtab | cut -d " " -f 3`
52 fi
53 if [[ $EspFilesystem != 'vfat' ]] ; then
54 echo "$RootDir/boot/efi doesn't seem to be on a VFAT filesystem. The ESP must be"
55 echo "mounted at $RootDir/boot or $RootDir/boot/efi and it must be VFAT! Aborting!"
56 exit 1
57 fi
58
59 # Sanity check on source & target....
60 EspPathLength=`expr length $InstallDir`
61 Temp=`echo $SourceDir | cut -c 1-$EspPathLength`
62 if [[ $Temp != $InstallDir ]] ; then
63 echo "$SourceDir isn't on the ESP ($InstallDir)! Aborting!"
64 exit 1
65 fi
66 Temp=`echo $TargetDir | cut -c 1-$EspPathLength`
67 if [[ $Temp != $InstallDir ]] ; then
68 echo "$TargetDir isn't on the ESP ($InstallDir)! Aborting!"
69 exit 1
70 fi
71
72 # Temporarily replace "/" in pathnames with ",", so as to enable sed to
73 # work on them
74 TempInstallDir=`echo $InstallDir | tr '/' ','`
75 Temp=`echo $SourceDir | tr '/' ',' | sed s/${TempInstallDir}//g | tr ',' '/' | tr '[A-Z]' '[a-z]'`
76 EspSourceDir=`dirname $Temp`/`basename $Temp`
77 Temp=`echo $TargetDir | tr '/' ',' | sed s/${TempInstallDir}//g | tr ',' '/' | tr '[A-Z]' '[a-z]'`
78 EspTargetDir=`dirname $Temp`/`basename $Temp`
79 if [[ $EspSourceDir == $EspTargetDir ]] ; then
80 echo "$SourceDir is the same as $TargetDir! Aborting!"
81 exit 1
82 fi
83 } # FindLinuxESP
84
85 DeterminePlatform() {
86 CpuType=`uname -m`
87 case "$CpuType" in
88 aarch64)
89 Platform="aa64"
90 ;;
91 x86_64)
92 Platform="x64"
93 ;;
94 i?86)
95 Platform="ia32"
96 ;;
97 *)
98 echo "Unsupported CPU type; aborting!"
99 exit 1
100 esac
101 Source="refind_$Platform.efi"
102 Target=$Source
103 }
104
105 # Adjust filename variables appropriately for their locations and detected
106 # presence (or lack thereof) of shim installation
107 AdjustFilenames() {
108 if [[ -f $SourceDir/grub$Platform.efi ]] ; then
109 Source="grub$Platform.efi"
110 Target=$Source
111 if [[ $EspSourceDir == "/efi/boot" ]] ; then
112 SourceShim="boot$Platform.efi"
113 elif [[ $EspSourceDir == "/efi/microsoft/boot" ]] ; then
114 SourceShim="bootmgfw.efi"
115 fi
116 else
117 SourceShim="none"
118 TargetShim="none"
119 if [[ $EspSourceDir == "/efi/boot" ]] ; then
120 Source="boot$Platform.efi"
121 elif [[ $EspSourceDir == "/efi/microsoft/boot" ]] ; then
122 Source="bootmgfw.efi"
123 fi
124 fi
125
126 if [[ $EspTargetDir == "/efi/boot" ]] ; then
127 if [[ $TargetShim == "none" ]] ; then
128 Target="boot$Platform.efi"
129 else
130 TargetShim="boot$Platform.efi"
131 fi
132 elif [[ $EspTargetDir == "/efi/microsoft/boot" ]] ; then
133 if [[ $TargetShim == "none" ]] ; then
134 Target="bootmgfw.efi"
135 else
136 TargetShim="bootmgfw.efi"
137 fi
138 fi
139 } # AdjustFilenames()
140
141 # Checks for the presence of necessary files, including both boot loaders
142 # and support utilities (efibootmgr, etc.)
143 CheckForFiles() {
144 if [[ (! -f $SourceDir/$Source) ||
145 ($SourceShim != "none" && ! -f $SourceDir/SourceShim) ||
146 ! -f $SourceDir/refind.conf ]] ; then
147 echo "There doesn't seem to be a rEFInd installation at $SourceDir!"
148 echo "Aborting!"
149 exit 1
150 fi
151 if [[ $EspTargetDir != "/efi/boot" && $EspTargetDir != "/efi/microsoft/boot" ]] ; then
152 Efibootmgr=`which efibootmgr 2> /dev/null`
153 if [[ ! -f $Efibootmgr ]] ; then
154 echo "Moving to a non-default directory requires a working efibootmgr utility, but"
155 echo "one can't be found! Aborting!"
156 exit 1
157 elif [[ ! -d "/sys/firmware/efi" ]] ; then
158 echo "Moving to a non-default directory requires a boot into EFI mode, but we seem"
159 echo "to be running in BIOS mode. (Perhaps typing 'modprobe efivars' will fix this."
160 echo "Aborting!"
161 fi
162 fi
163 } # CheckForFiles()
164
165 # Do final checks & then move the files!
166 MoveFiles() {
167 ExistingFiles=`find $TargetDir -name "*.efi" 2> /dev/null`
168 if [[ -n $ExistingFiles && $EspTargetDir != "/efi/boot" && $EspTargetDir != "/efi/microsoft/boot" ]] ; then
169 echo "$TargetDir isn't empty! Aborting!"
170 exit 1
171 fi
172
173 if [[ $EspTargetDir == "/efi/boot" && -d $TargetDir ]] ; then
174 if [[ -d $InstallDir/EFI/BOOT-rEFIndBackup ]] ; then
175 echo ""
176 echo "Caution: An existing backup of a default boot loader exists! If the current"
177 echo "default boot loader and the backup are different boot loaders, the current"
178 echo "one will become inaccessible."
179 echo ""
180 echo -n "Do you want to proceed with moving (Y/N)? "
181 read YesNo
182 if [[ $YesNo == "Y" || $YesNo == "y" ]] ; then
183 echo "OK; continuing with the move..."
184 else
185 exit 0
186 fi
187 else
188 mv $TargetDir $InstallDir/EFI/BOOT-refindBackup &> /dev/null
189 fi
190 fi
191
192 if [[ $EspTargetDir == "/efi/microsoft/boot" && -d $TargetDir ]] ; then
193 mv -n $TargetDir/bootmgfw.efi $InstallDir/EFI/Microsoft/
194 fi
195
196 mkdir -p $TargetDir
197 mv $SourceDir/icons $TargetDir/ 2> /dev/null
198 mv $SourceDir/icons-backup $TargetDir/ 2> /dev/null
199 mv $SourceDir/drivers_* $TargetDir/ 2> /dev/null
200 mv $SourceDir/keys $TargetDir 2> /dev/null
201 mv $SourceDir/$Source $TargetDir/$Target 2> /dev/null
202 mv $SourceDir/$SourceShim $TargetDir/$TargetShim 2> /dev/null
203 mv $SourceDir/refind.conf* $TargetDir/ 2> /dev/null
204 rmdir $SourceDir 2> /dev/null
205 } # MoveFiles()
206
207 # Clean up after moving files -- mainly restoring old backed-up files, if present
208 PostMoveCleanup() {
209 if [[ $EfiSourceDir == "/efi/boot" && -d $InstallDir/EFI/BOOT-rEFIndBackup && ! -d $SourceDir ]] ; then
210 mv $InstallDir/EFI/BOOT-rEFIndBackup $SourceDir 2> /dev/null
211 fi
212 if [[ $EfiSourceDir == "/efi/microsoft/boot" && -f $InstallDir/EFI/Microsoft/bootmgfw.efi ]] ; then
213 mv -n $InstallDir/EFI/Microsoft/bootmgfw.efi $SourceDir/bootmgfw.efi
214 fi
215 } # PostMoveCleanup()
216
217 # If necessary, create a new NVRAM entry for the new location
218 AddNvramEntry() {
219 InstallIt="0"
220 Efibootmgr=`which efibootmgr 2> /dev/null`
221 InstallDisk=`grep $InstallDir /etc/mtab | cut -d " " -f 1 | cut -c 1-8`
222 PartNum=`grep $InstallDir /etc/mtab | cut -d " " -f 1 | cut -c 9-10`
223
224 if [[ $TargetShim != "none" ]] ; then
225 EntryFilename=$EspTargetDir/$TargetShim
226 else
227 EntryFilename=$EspTargetDir/$Target
228 fi # if/else
229
230 EfiEntryFilename=`echo ${EntryFilename//\//\\\}`
231 EfiEntryFilename2=`echo ${EfiEntryFilename} | sed s/\\\\\\\\/\\\\\\\\\\\\\\\\/g`
232 ExistingEntry=`$Efibootmgr -v | grep -i $EfiEntryFilename2`
233
234 if [[ $ExistingEntry ]] ; then
235 ExistingEntryBootNum=`echo $ExistingEntry | cut -c 5-8`
236 FirstBoot=`$Efibootmgr | grep BootOrder | cut -c 12-15`
237 if [[ $ExistingEntryBootNum != $FirstBoot ]] ; then
238 $Efibootmgr -b $ExistingEntryBootNum -B &> /dev/null
239 InstallIt="1"
240 fi
241 else
242 InstallIt="1"
243 fi
244
245 if [[ $InstallIt == "1" ]] ; then
246 if [[ $EfiTargetDir == "/efi/microsoft/boot" ]] ; then
247 # Name it the way some firmware expects -- see http://mjg59.dreamwidth.org/20187.html
248 $Efibootmgr -c -l $EfiEntryFilename -L "Windows Boot Manager" -d $InstallDisk -p $PartNum &> /dev/null
249 else
250 $Efibootmgr -c -l $EfiEntryFilename -L "rEFInd Boot Manager" -d $InstallDisk -p $PartNum &> /dev/null
251 fi
252 if [[ $? != 0 ]] ; then
253 EfibootmgrProblems=1
254 fi
255 fi
256
257 if [[ $EfibootmgrProblems ]] ; then
258 echo
259 echo "ALERT: There were problems running the efibootmgr program! Your moved rEFInd"
260 echo "might not run!"
261 echo
262 fi
263 } # AddNvramEntry
264
265 #
266 # Main body of script
267 #
268
269 if [[ $# != 2 ]] ; then
270 echo "Usage: $0 {source-directory} {target-directory}"
271 exit 1
272 fi
273 if [[ `whoami` != "root" ]] ; then
274 echo "Not running as root! Aborting!"
275 exit 1
276 fi
277
278 FindLinuxESP
279 DeterminePlatform
280 AdjustFilenames
281 CheckForFiles
282 MoveFiles
283 PostMoveCleanup
284 AddNvramEntry