~rodsmith/refind/master

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
#!/usr/bin/env bash
#
# Linux script to move an existing rEFInd installation from one directory to
# another
#
# copyright (c) 2013-2015 by Roderick W. Smith
#
# This program is licensed under the terms of the GNU GPL, version 3,
# or (at your option) any later version.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

# Usage:
#
# ./mvrefind /path/to/source /path/to/destination
#
# Typically used to "hijack" or "unhijack" a Windows boot loader location or
# to help convert a rEFInd installation made in BIOS mode to one that works
# in EFI mode.
#
# Revision history:
#
# 0.11.0  -- Added creation of BOOT.CSV file to tasks
# 0.10.2  -- Fixed bug in moving bootmgfw.efi in some situations
# 0.10.1  -- Generalized to support ARM64 (aka AARCH64, aa64)
# 0.10.0  -- Renamed from mvrefind.sh to mvrefind
# 0.6.3   -- Initial release
#
# Note: mvrefind version numbers match those of the rEFInd package
# with which they first appeared.

RootDir="/"
SourceShim="shim.efi"
TargetShim=$SourceShim
SourceDir=`readlink -m ${1}`
TargetDir=`readlink -m ${2}`

# Identifies the ESP's location (/boot or /boot/efi); aborts if the ESP isn't
# mounted at either location. Also splits the ESP location from SourceDir and
# TargetDir, leaving them intact but creating new EspSourceDir and EspTargetDir
# variables containing only the ESP components thereof. These new variables
# are also converted to all-lowercase and any trailing slash is stripped, to
# assist in comparisons. (This is reasonable because FAT is case-insensitive.)
# Sets InstallDir to the ESP mount point.
FindLinuxESP() {
   EspLine=`df $RootDir/boot/efi 2> /dev/null | grep boot/efi`
   if [[ ! -n $EspLine ]] ; then
      EspLine=`df $RootDir/boot | grep boot`
   fi
   InstallDir=`echo $EspLine | cut -d " " -f 6`
   if [[ -n $InstallDir ]] ; then
      EspFilesystem=`grep $InstallDir /etc/mtab | grep -v autofs | cut -d " " -f 3`
   fi
   if [[ $EspFilesystem != 'vfat' ]] ; then
      echo "$RootDir/boot/efi doesn't seem to be on a VFAT filesystem. The ESP must be"
      echo "mounted at $RootDir/boot or $RootDir/boot/efi and it must be VFAT! Aborting!"
      exit 1
   fi

   # Sanity check on source & target....
   EspPathLength=`expr length $InstallDir`
   Temp=`echo $SourceDir | cut -c 1-$EspPathLength`
   if [[ $Temp != $InstallDir ]] ; then
      echo "$SourceDir isn't on the ESP ($InstallDir)! Aborting!"
      exit 1
   fi
   Temp=`echo $TargetDir | cut -c 1-$EspPathLength`
   if [[ $Temp != $InstallDir ]] ; then
      echo "$TargetDir isn't on the ESP ($InstallDir)! Aborting!"
      exit 1
   fi

   # Temporarily replace "/" in pathnames with ",", so as to enable sed to
   # work on them
   TempInstallDir=`echo $InstallDir | tr '/' ','`
   Temp=`echo $SourceDir | tr '/' ',' | sed s/${TempInstallDir}//g | tr ',' '/' | tr '[A-Z]' '[a-z]'`
   EspSourceDir=`dirname $Temp`/`basename $Temp`
   Temp=`echo $TargetDir | tr '/' ',' | sed s/${TempInstallDir}//g | tr ',' '/' | tr '[A-Z]' '[a-z]'`
   EspTargetDir=`dirname $Temp`/`basename $Temp`
   if [[ $EspSourceDir == $EspTargetDir ]] ; then
      echo "$SourceDir is the same as $TargetDir! Aborting!"
      exit 1
   fi
} # FindLinuxESP

DeterminePlatform() {
    CpuType=`uname -m`
    case "$CpuType" in
    aarch64)
        Platform="aa64"
        ;;
    x86_64)
        Platform="x64"
        ;;
    i?86)
        Platform="ia32"
        ;;
    *)
        echo "Unsupported CPU type; aborting!"
        exit 1
    esac
    Source="refind_$Platform.efi"
    Target=$Source
}

# Adjust filename variables appropriately for their locations and detected
# presence (or lack thereof) of shim installation
AdjustFilenames() {
   if [[ -f $SourceDir/grub$Platform.efi ]] ; then
      Source="grub$Platform.efi"
      Target=$Source
      if [[ $EspSourceDir == "/efi/boot" ]] ; then
         SourceShim="boot$Platform.efi"
      elif [[ $EspSourceDir == "/efi/microsoft/boot" ]] ; then
         SourceShim="bootmgfw.efi"
      fi
   else
      SourceShim="none"
      TargetShim="none"
      if [[ $EspSourceDir == "/efi/boot" ]] ; then
         Source="boot$Platform.efi"
      elif [[ $EspSourceDir == "/efi/microsoft/boot" ]] ; then
         Source="bootmgfw.efi"
      fi
   fi

   if [[ $EspTargetDir == "/efi/boot" ]] ; then
      if [[ $TargetShim == "none" ]] ; then
         Target="boot$Platform.efi"
      else
         TargetShim="boot$Platform.efi"
      fi
   elif [[ $EspTargetDir == "/efi/microsoft/boot" ]] ; then
      if [[ $TargetShim == "none" ]] ; then
         Target="bootmgfw.efi"
      else
         TargetShim="bootmgfw.efi"
      fi
   fi
} # AdjustFilenames()

# Checks for the presence of necessary files, including both boot loaders
# and support utilities (efibootmgr, etc.)
CheckForFiles() {
   if [[ (! -f $SourceDir/$Source) ||
         ($SourceShim != "none" && ! -f $SourceDir/SourceShim) ||
         ! -f $SourceDir/refind.conf ]] ; then
      echo "There doesn't seem to be a rEFInd installation at $SourceDir!"
      echo "Aborting!"
      exit 1
   fi
   if [[ $EspTargetDir != "/efi/boot" && $EspTargetDir != "/efi/microsoft/boot" ]] ; then
      Efibootmgr="$(command -v efibootmgr 2> /dev/null)"
      if [[ ! -f $Efibootmgr ]] ; then
         echo "Moving to a non-default directory requires a working efibootmgr utility, but"
         echo "one can't be found! Aborting!"
         exit 1
      elif [[ ! -d "/sys/firmware/efi" ]] ; then
         echo "Moving to a non-default directory requires a boot into EFI mode, but we seem"
         echo "to be running in BIOS mode. (Perhaps typing 'modprobe efivars' will fix this."
         echo "Aborting!"
      fi
   fi
} # CheckForFiles()

# Do final checks & then move the files!
MoveFiles() {
   ExistingFiles=`find $TargetDir -name "*.efi" 2> /dev/null`
   if [[ -n $ExistingFiles && $EspTargetDir != "/efi/boot" && $EspTargetDir != "/efi/microsoft/boot" ]] ; then
      echo "$TargetDir isn't empty! Aborting!"
      exit 1
   fi

   if [[ $EspTargetDir == "/efi/boot" && -d $TargetDir ]] ; then
      if [[ -d $InstallDir/EFI/BOOT-rEFIndBackup ]] ; then
         echo ""
         echo "Caution: An existing backup of a default boot loader exists! If the current"
         echo "default boot loader and the backup are different boot loaders, the current"
         echo "one will become inaccessible."
         echo ""
         echo -n "Do you want to proceed with moving (Y/N)? "
         read YesNo
         if [[ $YesNo == "Y" || $YesNo == "y" ]] ; then
            echo "OK; continuing with the move..."
         else
            exit 0
         fi
      else
         mv $TargetDir $InstallDir/EFI/BOOT-refindBackup &> /dev/null
      fi
   fi

   if [[ $EspTargetDir == "/efi/microsoft/boot" && -d $TargetDir ]] ; then
      mv -n $TargetDir/bootmgfw.efi $InstallDir/EFI/Microsoft/
   fi

   mkdir -p $TargetDir
   mv $SourceDir/icons $TargetDir/ 2> /dev/null
   mv $SourceDir/icons-backup $TargetDir/ 2> /dev/null
   mv $SourceDir/drivers_* $TargetDir/ 2> /dev/null
   mv $SourceDir/keys $TargetDir 2> /dev/null
   mv $SourceDir/$Source $TargetDir/$Target 2> /dev/null
   mv $SourceDir/$SourceShim $TargetDir/$TargetShim 2> /dev/null
   mv $SourceDir/refind.conf* $TargetDir/ 2> /dev/null
   rm $SourceDir/BOOT.CSV
   rmdir $SourceDir 2> /dev/null
} # MoveFiles()

# Clean up after moving files -- mainly restoring old backed-up files, if present
PostMoveCleanup() {
   if [[ "$EspSourceDir" == "/efi/boot" && -d "$InstallDir/EFI/BOOT-rEFIndBackup" && ! -d $SourceDir ]] ; then
      mv "$InstallDir/EFI/BOOT-rEFIndBackup" "$SourceDir" 2> /dev/null
   fi
   if [[ "$EspSourceDir" == "/efi/microsoft/boot" && -f "$InstallDir/EFI/Microsoft/bootmgfw.efi" ]] ; then
      mv -n "$InstallDir/EFI/Microsoft/bootmgfw.efi" "$SourceDir/bootmgfw.efi"
   fi
} # PostMoveCleanup()

# Create a BOOT.CSV file in the same directory as rEFInd, to help in recovery
# should the system's boot entry list be lost
CreateBootCsvFile() {
   IConv="$(command -v iconv 2> /dev/null)"
   if [[ -x "$IConv" && -d "$TargetDir" ]] ; then
      echo "$Target,rEFInd boot manager,,This is the boot entry for rEFInd" | \
           $IConv -t UCS-2 > "$TargetDir/BOOT.CSV"
   fi
} # CreateBootCsvFile()

# If necessary, create a new NVRAM entry for the new location
AddNvramEntry() {
   InstallIt="0"
   Efibootmgr="$(command -v efibootmgr 2> /dev/null)"
   InstallDisk=`grep $InstallDir /etc/mtab | grep -v autofs | cut -d " " -f 1 | cut -c 1-8`
   PartNum=`grep $InstallDir /etc/mtab | grep -v autofs | cut -d " " -f 1 | cut -c 9-10`

   if [[ $TargetShim != "none" ]] ; then
      EntryFilename=$EspTargetDir/$TargetShim
   else
      EntryFilename=$EspTargetDir/$Target
   fi # if/else

   EfiEntryFilename=`echo ${EntryFilename//\//\\\}`
   EfiEntryFilename2=`echo ${EfiEntryFilename} | sed s/\\\\\\\\/\\\\\\\\\\\\\\\\/g`
   ExistingEntry=`$Efibootmgr -v | grep -i $EfiEntryFilename2`

   if [[ $ExistingEntry ]] ; then
      ExistingEntryBootNum=`echo $ExistingEntry | cut -c 5-8`
      FirstBoot=`$Efibootmgr | grep BootOrder | cut -c 12-15`
      if [[ $ExistingEntryBootNum != $FirstBoot ]] ; then
         $Efibootmgr -b $ExistingEntryBootNum -B &> /dev/null
         InstallIt="1"
      fi
   else
      InstallIt="1"
   fi

   if [[ $InstallIt == "1" ]] ; then
      if [[ $EspTargetDir == "/efi/microsoft/boot" ]] ; then
         # Name it the way some firmware expects -- see http://mjg59.dreamwidth.org/20187.html
         $Efibootmgr -c -l $EfiEntryFilename -L "Windows Boot Manager" -d $InstallDisk -p $PartNum &> /dev/null
      else
         $Efibootmgr -c -l $EfiEntryFilename -L "rEFInd Boot Manager" -d $InstallDisk -p $PartNum &> /dev/null
      fi
      if [[ $? != 0 ]] ; then
         EfibootmgrProblems=1
      fi
   fi

   if [[ $EfibootmgrProblems ]] ; then
      echo
      echo "ALERT: There were problems running the efibootmgr program! Your moved rEFInd"
      echo "might not run!"
      echo
   fi
} # AddNvramEntry

#
# Main body of script
#

if [[ $# != 2 ]] ; then
   echo "Usage: $0 {source-directory} {target-directory}"
   exit 1
fi
if [[ `whoami` != "root" ]] ; then
   echo "Not running as root! Aborting!"
   exit 1
fi

FindLinuxESP
DeterminePlatform
AdjustFilenames
CheckForFiles
MoveFiles
PostMoveCleanup
CreateBootCsvFile
AddNvramEntry