-
Notifications
You must be signed in to change notification settings - Fork 17
/
EFIClone.sh
executable file
·321 lines (277 loc) · 13.5 KB
/
EFIClone.sh
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
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
#!/bin/bash
# EFI Partition Clone Script
# (c) 2018 - Ted Howe
# tedhowe@burke-howe.com
# wombat94 on TonyMacx86 forums and GitHub.
#Release Notes
# version 0.1beta3
# 2/24/2018
# - Updated to use rsync to handle the deletion of information from the desination drive. Basically,
# allow rsync to do its thing
# - Added additional check, based on clover boot log retrieved with bdmesg, to ensure that the
# destination EFI partition is NOT the EFI partition that was used to boot the computer.
# version 0.1beta2
# 2/23/2018
# Clean up and combine.
# -Combined the two versions of the script into a single script that detects whether it was called from
# Carbon Copy Cloner or SuperDuper! and behaves accordingly in the setup.
# -Added validation of the copied data through SHASUM hashing of all visible files in the source and
# destination EFI partitions after the delete/rsync copy is complete
# -Changed the method of copying files from cp to rsync
# -Modified the copy path of the source to include the root directory - not just the /EFI/ directory
# and subdirectories.
#----------------------------------------------------------------------------------------------------------
# version 0.1beta1
# 2/22/2018
# Initial Release
#----------------------------------------------------------------------------------------------------------
# This script is designed to be a "post-flight" script run automatically by CCC at the end of a
# clone task. It will copy the contents of the source drive's EFI partition to the destination drive's EFI
# partition. It will COMPLETELY DELETE and replace all data on the destination EFI partition.
#!!! This script DOES delete data and therefore has the potential to cause unintended data loss.
# I have tried to think of any scenario that would cause unintended data loss, but as with all software, ther
# may be bugs and therefore there is a small risk.
# ONLY USE THIS SCRIPT IN THE MANNER DESCRIBED. I PROVIDE NO WARRANTY ON THE USE OF THIS SCRIPT
# PROCEED AT YOUR OWN RISK.
#user variables
#Log File location. By default, this will write to the same directory where the cccEFIClone.sh script lives.
# you may edit it, but be sure the location will always exist and you have permissions to write to the location
# It appears that Carbon Copy Cloner runs the script in the root of the boot drive, so that is where the log will go
# if you don't put in a literal path below.
LOG_FILE="$PWD/EFIClone.log"
#test switch. The script is distributed with this switch set to "Y".
# the script will not delete or copy any data when this is set to Y
# in order to make the script take action, once you have verified that it can identify the right locations to
# copy from and to on your system, change this variable to N
TEST_SWITCH="Y"
#----- END OF USER VARIABLES
#----- THERE IS NO NEED TO EDIT BELOW THIS LINE
#----------------------------------------------
function writeTolog () {
echo "[`date`] - ${*}" >> ${LOG_FILE}
}
function getDiskNumber () {
echo "$( diskutil info "$1" | grep 'Part of Whole' | rev | cut -d ' ' -f1 | rev )"
}
function getCoreStoragePhysicalDiskNumber () {
echo "$( diskutil info "$1" | grep 'PV UUID' | rev | cut -d '(' -f1 | cut -d ')' -f2 | rev | cut -d 'k' -f2 | cut -d 's' -f1 )"
}
function getAPFSPhysicalDiskNumber () {
echo "$( diskutil apfs list | grep -A 9 "Container $1" | grep "APFS Physical Store" | rev | cut -d ' ' -f 1 | cut -d 's' -f 2 | cut -d 'k' -f 1 )"
}
function getEFIVolume () {
echo "$( diskutil list | grep "$1" | grep "EFI" | rev | cut -d ' ' -f 1 | rev )"
}
function getDiskMountPoint () {
echo "$( diskutil info "$1" | grep 'Mount Point' | rev | cut -d ':' -f 1 | rev | awk '{$1=$1;print}' )"
}
function getEFIDirectoryHash () {
#echo "$( find -s . -type f \( ! -iname ".*" \) -print0 | xargs -0 shasum | cut -d ' ' -f 1 | shasum )"
echo "$( find -s . -not -path '*/\.*' -type f \( ! -iname ".*" \) -print0 | xargs -0 shasum | shasum )"
}
function logEFIDirectoryHashDetails () {
#echo "$( find -s . -type f \( ! -iname ".*" \) -print0 | xargs -0 shasum | cut -d ' ' -f 1 | shasum )"
echo "$( find -s . -not -path '*/\.*' -type f \( ! -iname ".*" \) -print0 | xargs -0 shasum )" >> ${LOG_FILE}
}
function getSystemBootVolumeName () {
echo "$( system_profiler SPSoftwareDataType | grep 'Boot Volume' | rev | cut -d ':' -f 1 | rev | awk '{$1=$1;print}' )"
}
function getCurrentBootEFIVolumeUUID () {
echo "$( bdmesg | grep 'SelfDevicePath' | rev | cut -d ')' -f 2 | rev | cut -d ',' -f 3 )"
}
function getDeviceIDfromUUID () {
echo "$( diskutil info "$1" | grep 'Device Identifier' | rev | cut -d ' ' -f 1 | rev )"
}
function getDiskIDfromUUID () {
writeTolog "$1"
echo "$( diskutil info "$1" | grep 'Device Identifier' | rev | cut -d ' ' -f 1 | rev )"
}
#begin logging
writeTolog "***** EFI Clone Script start"
writeTolog "working directory = $PWD"
writeTolog "Running $0"
#determine which disk clone application called the script (based on number of parameters)
# - log details
# - set up initial parameters
# - if possible do calling app-specific sanity checks in order to exit without taking action if necessary
if [[ "$#" == "4" ]]
then
#log inputs to script
writeTolog "Called From Carbon Copy Cloner"
writeTolog "1: Source Path = $1" # Source path
writeTolog "2: Destination Path = $2" # Mounted disk image destination path
writeTolog "3: CCC Exit Status = $3" # Exit status
writeTolog "4: Disk image file path = $4" # Disk image file path
#sanity checks to determine whether to run
if [[ "$3" == "0" ]]
then
writeTolog "CCC completed with success, the EFI Clone Script will run"
else
writeTolog "CCC did not exit with success, the EFI Clone Script will not run"
osascript -e 'display notification "CCC Task failed, EFI Clone Script did not run" with title "EFI Clone Script"'
exit 0
fi
if [[ "$4" == "" ]]
then
writeTolog "CCC clone was not to a disk image. the EFI Clone Script will run"
else
writeTolog "CCC Clone destination was a disk image file. The EFI Clone Script will not run"
osascript -e 'display notification "CCC Clone destination was a disk image. Clone script did not run." with title "EFI Clone Script"'
exit 0
fi
#set source and destination from variables passed in
sourceVolume=$1
destinationVolume=$2
else
if [[ "$#" == "6" ]]
then
#log inputs to script
writeTolog "Called From SuperDuper!"
writeTolog "1: Source Disk Name = $1"
writeTolog "2: Source Mount Path = $2"
writeTolog "3: Destination Disk Name = $3"
writeTolog "4: Destination Mount Path = $4"
writeTolog "5: SuperDuper! Backup Script Used = $5"
writeTolog "6: Unused parameter 6 = $6"
#set source and destination from variables passed in
sourceVolume=$2
destinationVolume=$4
else
#an unknown number of parameters have been passed in. log that fact and then exit
writeTolog "$# parameters were passed in. This is an unsupported number of parameters. Exiting now"
echo "$# parameters were passed in. This is an unsupported number of parameters. Exiting now"
osascript -e 'display notification "Unsupported set of parameters passed in. EFI Clone script did not run!" with title "EFI Clone Script"'
exit 0
fi
fi
writeTolog sourceVolume = $sourceVolume
sourceVolumeDisk="$( getDiskNumber "$sourceVolume" )"
writeTolog sourceVolumeDisk = $sourceVolumeDisk
writeTolog destinationVolume = $destinationVolume
destinationVolumeDisk="$( getDiskNumber "$destinationVolume" )"
writeTolog destinationVolumeDisk = $destinationVolumeDisk
sourceDisk=$sourceVolumeDisk
sourceEFIPartition="$( getEFIVolume "$sourceDisk" )"
#If we don't find an EFI partition on the disk that was identified by the volume path,
# then check to see if it is a coreStoreage volume and get the disk number from there
if [[ "$sourceEFIPartition" == "" ]]; then
sourceDisk=""
sourceDisk=disk"$( getCoreStoragePhysicalDiskNumber "$sourceVolumeDisk" )"
if [[ "$sourceDisk" == "disk" ]]
then
sourceDisk=""
sourceDisk=$sourceVolumeDisk
fi
sourceEFIPartition="$( getEFIVolume "$sourceDisk" )"
fi
#If we still don't have an EFI partition then look to see if the sourceVolumeDisk is an APFS
# volume and find the physical disk
if [[ "$sourceEFIPartition" == "" ]]; then
sourceDisk=""
sourceDisk=disk"$( getAPFSPhysicalDiskNumber "$sourceVolumeDisk" )"
sourceEFIPartition="$( getEFIVolume "$sourceDisk" )"
fi
writeTolog sourceEFIPartition = $sourceEFIPartition
destinationDisk=$destinationVolumeDisk
destinationEFIPartition="$( getEFIVolume "$destinationDisk" )"
#If we don't find an EFI partition on the disk that was identified by the volume path,
# then check to see if it is a coreStoreage volume and get the disk number from there
if [[ "$destinationEFIPartition" == "" ]]; then
destinationDisk=""
destinationDisk=disk"$( getCoreStoragePhysicalDiskNumber "$destinationVolumeDisk" )"
if [[ "$destionationDisk" == "disk" ]]
then
destinationDisk=""
destinationDisk=$destinationVolumeDisk
fi
destinationEFIPartition="$( getEFIVolume "$sourceDisk" )"
fi
#If we still don't have an EFI partition then look to see if the sourceVolumeDisk is an APFS
# volume and find the physical disk
if [[ "$destinationEFIPartition" == "" ]]; then
destinationDisk=""
destinationDisk=disk"$( getAPFSPhysicalDiskNumber "$destinationVolumeDisk" )"
destinationEFIPartition="$( getEFIVolume "$sourceDisk" )"
fi
writeTolog destinationEFIPartition = $destinationEFIPartition
efiBootPartitionUUID="$( getCurrentBootEFIVolumeUUID )"
writeTolog "efiBootPartitionUUID = $efiBootPartitionUUID"
efiBootPartionDisk="$( getDeviceIDfromUUID "$efiBootPartitionUUID" )"
writeTolog "efiBootPartitionDisk = $efiBootPartionDisk"
if [[ "$efiBootPartitionDisk" == "$destinationDisk" ]]
then
writeTolog "Destination disk is the current EFI partition that was used to boot the computer, script exiting."
osascript -e 'display notification "No source EFI Partition found. EFI Clone Script did not run!." with title "EFI Clone Script"'
fi
if [[ "$sourceEFIPartition" == "" ]]
then
writeTolog "No SourceEFIPartition Found, script exiting."
osascript -e 'display notification "No source EFI Partition found. EFI Clone Script did not run!." with title "EFI Clone Script"'
exit 0
fi
if [[ "$destinationEFIPartition" == "" ]]
then
writeTolog "No DestinationEFIPartition Found, script exiting."
osascript -e 'display notification "No destination EFI Partition found. EFI Clone Script did not run!." with title "EFI Clone Script"'
exit 0
fi
if [[ "$sourceEFIPartition" == "$destinationEFIPartition" ]]
then
writeTolog "Source and Destination EFI Partitions are the same. Script exiting."
osascript -e 'display notification "Source and Destination EFI partitions are the same. EFI Clone Script did not run!." with title "EFI Clone Script"'
exit 0
fi
diskutil mount /dev/$sourceEFIPartition
diskutil mount /dev/$destinationEFIPartition
writeTolog "drives Mounted"
sourceEFIMountPoint="$( getDiskMountPoint "$sourceEFIPartition" )"
writeTolog sourceEFIMountPoint = $sourceEFIMountPoint
destinationEFIMountPoint="$( getDiskMountPoint "$destinationEFIPartition" )"
writeTolog destinationEFIMountPoint = $destinationEFIMountPoint
if [[ "$TEST_SWITCH" == "Y" ]]
then
writeTolog "********* Test simulation - file delete/copy would happen here. "
writeTolog "rsync command will be executed with the --dry-run option"
writeTolog "rsync command calculated is..."
writeTolog "rsync -av --exclude='.*'' "$sourceEFIMountPoint/" "$destinationEFIMountPoint/""
writeTolog "THE BELOW OUTPUT IS FROM AN RSYNC DRY RUN! NO DATA HAS BEEN MODIFIED!"
rsync --dry-run -av --exclude=".*" --delete "$sourceEFIMountPoint/" "$destinationEFIMountPoint/" >> ${LOG_FILE}
writeTolog "********* Test Simulation - end of file delete/copy section."
else
writeTolog "Synchronizing all files with rsync --delete option"
writeTolog "from $sourceEFIMountPoint/EFI to $destinationEFIMountPoint. Details follow..."
writeTolog "--------------------------------------------------------------------"
rsync -av --exclude=".*" --delete "$sourceEFIMountPoint/" "$destinationEFIMountPoint/" >> ${LOG_FILE}
writeTolog "--------------------------------------------------------------------"
writeTolog "Contents of Source EFI Partition copied to Destination EFI Partition"
fi
writeTolog "Compare the checksums of the EFI directories on the source and destination partitions"
writeTolog "-------------------------------------------------------------------------------------"
pushd "$sourceEFIMountPoint/"
sourceEFIHash="$( getEFIDirectoryHash "$sourceEFIMountPoint/EFI" )"
writeTolog "Source directory hash: $sourceEFIHash"
temp="$( logEFIDirectoryHashDetails "$sourceEFIMountPoint" )"
popd
pushd "$destinationEFIMountPoint/"
destinationEFIHash="$( getEFIDirectoryHash "$destinationEFIMountPoint/EFI" )"
writeTolog "Destination directory hash: $destinationEFIHash"
temp="$( logEFIDirectoryHashDetails "$sourceEFIMountPoint" )"
popd
if [[ "$sourceEFIHash" == "$destinationEFIHash" ]]
then
writeTolog "Directory hashes match! file copy successful"
else
writeTolog "Directory hashes differ! file copy unsuccessful"
fi
writeTolog "-------------------------------------------------------------------------------------"
diskutil unmount /dev/$destinationEFIPartition
diskutil unmount /dev/$sourceEFIPartition
writeTolog "EFI Partitions Unmounted"
writeTolog "EFIClone.sh complete"
if [[ "$sourceEFIHash" == "$destinationEFIHash" ]]
then
osascript -e 'display notification "EFI Clone Script completed successfully." with title "EFI Clone Script"'
else
osascript -e 'display notification "EFI Clone failed - destionation data did not match source after copy." with title "EFI Clone Scipt"'
fi
exit 0