-
Notifications
You must be signed in to change notification settings - Fork 5
/
install-magisk
executable file
·228 lines (193 loc) · 7.62 KB
/
install-magisk
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
#!/bin/bash
set -euxo pipefail
# Settings for adb and curl.
export ANDROID_SERIAL="172.16.23.223:5555" # see adb help
readonly CURL_ARGS=(-L) # follow location
# Root shell loader and temporary script path for run_as_root.
readonly LOADER_PATH="/data/local/tmp/loader"
readonly LOADER_BUILD_DIR="$(dirname "$(readlink -f "$0")")" # script directory
readonly SCRIPT_TMP_PATH="/data/local/tmp/magiskinstall-tmp"
# Magisk version and URL. URL may be overriden from $1.
readonly MAGISK_VERSION="22.0"
readonly MAGISK_URL="${1:-https://github.com/topjohnwu/Magisk/releases/download/v${MAGISK_VERSION}/Magisk-v${MAGISK_VERSION}.apk}"
# boot, recovery and recoveryB partitions. boot is backed up to recoveryB before patching, whose
# content should match recovery.
readonly BOOT_PARTNO="5"
readonly BOOT_DEV="/dev/mmcblk0p${BOOT_PARTNO}"
readonly RECOVERY_PARTNO="4"
readonly RECOVERY_DEV="/dev/mmcblk0p${RECOVERY_PARTNO}"
readonly RECOVERY_B_PARTNO="8"
readonly RECOVERY_B_DEV="/dev/mmcblk0p${RECOVERY_B_PARTNO}"
# Directory to place old and patched boot image in.
readonly PATCH_DIR="/sdcard/magiskinstall.$(date +%F_%T)"
# Temporary directory for Magisk installer, must be executable.
readonly INSTALLER_DIR="/data/local/tmp/magiskinstall.tmp"
# Magisk installer script and output filename.
readonly INSTALLER_SCRIPT="$INSTALLER_DIR/boot_patch.sh"
readonly MAGISK_PATCHED_PATH="$INSTALLER_DIR/new-boot.img"
# Pushes a temporary shell script with the passed arguments (first being PS4)
# to $SCRIPT_TMP_PATH, because getting something escaped properly through adb
# shell, (the loader’s argv parsing), and then the android shell is tricky.
push_tmp_script() {
local ps4="$1"; shift
local tmp="$(mktemp)"
printf '#!/system/bin/sh\nPS4="('"$ps4"') + "\nset -x\n' > "$tmp"
printf '%q ' "$@" >> "$tmp" # Need to print "%q " individually; the entire format is repeated per arg
printf '\n' >> "$tmp"
chmod +x "$tmp"
adb push "$tmp" "$SCRIPT_TMP_PATH" > /dev/null
rm -f "$tmp"
}
# Runs the passed arguments as user.
run_as_user() {
push_tmp_script "user" "$@"
adb shell "$SCRIPT_TMP_PATH"
}
# Runs the passed arguments as root.
run_as_root() {
push_tmp_script "root" "$@"
adb shell "$LOADER_PATH -trigger_exec $SCRIPT_TMP_PATH"
}
# Tests whether run_as_root escapes arguments and returns the return code properly.
test_run_as_root() {
local have="$(run_as_root sh -c 'echo $1' "foo bar" "bar baz" "baz qux")"
local want="bar baz"
if [ "$have" != "$want" ]; then
echo "ERROR: run_as_root broken: got >>>$have<<<, want >>>$want<<<" >&2
exit 99
fi
if run_as_root false; then
echo "ERROR: 'run_as_root false' succeeded" >&2
exit 99
fi
}
# Installs the root shell loader on the device.
prepare_root() {
if ! adb shell test -x "$LOADER_PATH"; then
echo "A root shell loader is not installed. Building and installing."
make -C "$LOADER_BUILD_DIR" loader
adb push "$LOADER_BUILD_DIR"/loader "$LOADER_PATH"
fi
test_run_as_root
}
# Returns the name of the given partition number from the device’s /proc/partinfo.
get_partname() {
local partno="$1"
# ex.: part05: 00012800 0000a000 "boot"
set -x
run_as_root cat /proc/partinfo | \
awk '/^part0*'"$partno"':/ { gsub("\"", ""); print $NF }'
}
# Queries the user for a response and returns 0 for yes, 1 for no.
ask_yesno() {
local prompt="$1"
local reply
while true; do
printf "%s [y/n]: " "$prompt"
read reply
case "$reply" in
[yY])
return 0
;;
[nN])
return 1
;;
*)
echo "Invalid response. Please enter 'y' or 'n'."
;;
esac
done
}
# Checks whether a given partition is an Android boot image.
is_android_bootimg() {
local part="$1"
test "$(run_as_root dd if="$part" bs=8 count=1)" = 'ANDROID!'
}
### Main script starts here ###
echo "Preparing root environment..."
prepare_root
echo "Sanity-checking partitions..."
declare -rA want_partnames=(["$BOOT_PARTNO"]="boot"
["$RECOVERY_PARTNO"]="recovery"
["$RECOVERY_B_PARTNO"]="recoveryB")
for partno in "${!want_partnames[@]}"; do
want_name="${want_partnames[$partno]}"
have_name="$(get_partname "$partno")"
if [ "$have_name" != "$want_name" ]; then
echo "Partition $partno is named '$have_name', expected '$want_name'. Aborting." >&2
exit 1
fi
echo "Partition $partno has expected name '$want_name'."
done
for dev in "$BOOT_DEV" "$RECOVERY_DEV" "$RECOVERY_B_DEV"; do
if ! is_android_bootimg "$dev"; then
echo "Device $dev does not appear to be an Android boot image. Aborting." >&2
exit 1
fi
echo "Device $dev looks like an Android boot image."
done
echo "Creating patch directory $PATCH_DIR..."
adb shell mkdir "$PATCH_DIR"
echo "(Re-)creating installer directory $INSTALLER_DIR..."
adb shell rm -rf "$INSTALLER_DIR"
adb shell mkdir -p "$INSTALLER_DIR"
echo "Downloading Magisk installer locally..."
tmp="$(mktemp -d)"
trap "rm -rf '$tmp'" EXIT
if [ -f "$MAGISK_URL" ]; then
cp "$MAGISK_URL" "$tmp/Magisk.zip"
else
curl "${CURL_ARGS[@]}" "$MAGISK_URL" > "$tmp/Magisk.zip"
fi
unzip -d "$tmp/m" "$tmp/Magisk.zip"
echo "Patching boot_patch.sh..." # https://github.com/topjohnwu/Magisk/issues/3961
find "$tmp/m" -name boot_patch.sh -exec sed -i.orig -re 's,/system/lib64,/system/XXXXXlib64XXXXX,g' {} \;
echo "Copying Magisk installer to device..."
if echo "$MAGISK_URL" | grep -q 'apk$'; then
adb push "$tmp/m/assets"/*.sh "$INSTALLER_DIR"
for i in "$tmp/m/lib/armeabi-v7a"/*.so; do
n="$(printf "%s" "$i" | sed -re 's/.*lib(.*)\.so/\1/')" # .../libfoo.so -> foo
adb push "$i" "$INSTALLER_DIR/$n"
done
else
adb push "$tmp/m/common"/* "$INSTALLER_DIR"
adb push "$tmp/m/arm"/* "$INSTALLER_DIR"
fi
adb shell chmod +x "$INSTALLER_DIR/"'*'
echo "Patching image..."
orig_img="$PATCH_DIR/boot_orig.img"
run_as_root dd if="$BOOT_DEV" of="$orig_img"
run_as_root sh -c 'getprop > "$1"/getprop.txt' argv0 "$PATCH_DIR"
run_as_user sh -c '"$1" "$2"' argv0 "$INSTALLER_SCRIPT" "$orig_img"
if ! adb shell test -e "$MAGISK_PATCHED_PATH"; then
echo "Patched image not found at '$MAGISK_PATCHED_PATH'. Aborting." >&2
exit 1
elif ! adb shell test "$MAGISK_PATCHED_PATH" -nt "$orig_img"; then
echo "Patched image ($MAGISK_PATCHED_PATH) is not newer than $orig_img. Aborting." >&2
exit 1
fi
if adb shell cmp "$MAGISK_PATCHED_PATH" "$orig_img"; then
echo "Patched image $MAGISK_PATCHED_PATH equal to original image $orig_img. Aborting." >&2
exit 1
fi
if ! is_android_bootimg "$MAGISK_PATCHED_PATH"; then
echo "Patched image $MAGISK_PATCHED_PATH does not look like an Android boot image. Aborting." >&2
exit 1
fi
patched_img="$PATCH_DIR/boot_patched.img"
echo "Patched image $MAGISK_PATCHED_PATH looks sane, moving to $patched_img"
adb shell mv "$MAGISK_PATCHED_PATH" "$patched_img"
if run_as_root cmp "$RECOVERY_DEV" "$RECOVERY_B_DEV" || \
ask_yesno "Partitions 'recovery' and 'recoveryB' differ (after ~20MB seems normal). Do you still want to back up 'boot' to 'recoveryB'?"; then
run_as_root dd if="$BOOT_DEV" of="$RECOVERY_B_DEV"
echo "Backed up $BOOT_DEV (boot) to $RECOVERY_B_DEV (recoveryB). See README.md for recovery hints."
else
if ! ask_yesno "Not backing up boot partition. Are you sure?"; then
exit 1
fi
fi
if ! ask_yesno "Do you want to write $patched_img to $BOOT_DEV? This is your last chance to quit."; then
exit 1
fi
run_as_root dd if="$patched_img" of="$BOOT_DEV"
echo "Boot partition ($BOOT_DEV) has been patched. Enjoy!"