-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathandroid-restore
executable file
·379 lines (330 loc) · 11.8 KB
/
android-restore
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
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
#!/bin/bash
#
# Author: Riyad Preukschas <riyad@informatik.uni-bremen.de>
# License: Mozilla Public License 2.0
# SPDX-License-Identifier: MPL-2.0
#
# Restores previously backed-up files to your Android phone.
# You still need to restore your apps and config by running your backup app
# (e.g. Neo Backup, TitaniumBackup).
set -o nounset # complain when reading unset vars
# set -o xtrace # print every command as it's executed
# log everything using syslog
exec 2>&1 | logger
# for easy listing of directory contents starting with '.'
GLOBIGNORE=.:..
readonly REMOTE_TEMP_DIR="/data/local/tmp" # you can't chmod +x files in /sdcard
readonly REMOTE_RSYNC_CONFIG="${REMOTE_TEMP_DIR}/rsync.conf"
readonly REMOTE_RSYNC_PORT=1873 # > 1024 so we don't need to be root
readonly LOCAL_RSYNC_PORT=6010
REMOTE_RSYNC_BIN="rsync"
readonly REMOTE_RSYNC_MODULE="android_backup"
readonly REMOTE_TEMP_RSYNC_BIN="${REMOTE_TEMP_DIR}/rsync"
readonly USER_RSYNC_FILTER_FILE="$HOME/.android_backup/rsync_restore.filter"
readonly LOCAL_RSYNC_FILTER_FILE=`mktemp -t "$(basename $0).XXXXXXXXXX"`
# on a running Android /sdcard is symlinked to /storage/emulated/[legacy|0]
# but in TWRP it's directly mounted to /sdcard (so the other dirs aren't there)
readonly ANDROID_DEFAULT_STORAGE="/sdcard"
REMOTE_STORAGES=("${ANDROID_DEFAULT_STORAGE}")
# these are relative to OPT_BACKUP_DIR
readonly BKP_APKS="Neo-Backup.apk TitaniumBackup.apk TitaniumBackupPro.apk"
# these are relative to OPT_BACKUP_DIR/STORAGE_NAME
readonly PRIO_DIRS=(".SeedVaultAndroidBackup" "Neo Backup" "TitaniumBackup" "Android/media/com.whatsapp/" "DCIM" "Pictures")
DEVICE_SERIALNO="---NO-DEVICE---"
ADB_RAW="adb"
ADB="${ADB_RAW} -s ${DEVICE_SERIALNO}"
ADB_SU_OPTIONS=""
echo_debug() {
if [[ ${OPT_DEBUG} ]]; then
echo "Debug: $1"
fi
}
echo_info() {
echo "$1"
}
echo_warn() {
echo "Warning: $1"
}
echo_error() {
echo "Error: $1"
}
adb_install() {
${ADB} install "$1"
}
adb_push() {
${ADB} push -p "$1" "$2"
}
rsync_cleanup_local() {
echo_debug "Removing local rsync filter file ..."
[[ -e "${LOCAL_RSYNC_FILTER_FILE}" ]] && rm "${LOCAL_RSYNC_FILTER_FILE}"
}
rsync_cleanup_remote() {
echo_debug "Killing rsync daemon ..."
${ADB} shell killall "${REMOTE_RSYNC_BIN}" 2>/dev/null
echo_debug "Removing port-forwarding ..."
${ADB} forward --remove tcp:${LOCAL_RSYNC_PORT}
echo_debug "Removing remote rsync daemon config ..."
${ADB} shell rm -f "${REMOTE_RSYNC_CONFIG}"
if [[ -f "${REMOTE_TEMP_RSYNC_BIN}" ]]; then
echo_debug "Removing provided remote rsync ..."
${ADB} shell rm -f "${REMOTE_TEMP_RSYNC_BIN}"
fi
}
rsync_push_module() {
local debug_options=''
# if [[ ${OPT_DEBUG} ]]; then
# debug_options=" --verbose --debug=ALL"
# fi
local root_options=''
if [[ ! ${OPT_ROOT} ]]; then
# needs --no-times against 'failed to set times on [...]: Operation not permitted (1)' errors
# needs --no-perms against 'failed to set permissions on [...]: Operation not permitted (1)' errors
root_options="--no-times --no-perms"
fi
# needs --temp-dir against 'mkstemp [...] failed: Operation not permitted (1)' errors
rsync ${debug_options} --partial --progress --archive --human-readable \
--filter=". ${LOCAL_RSYNC_FILTER_FILE}" --temp-dir="${REMOTE_TEMP_DIR}" ${root_options} \
"$1" "rsync://localhost:${LOCAL_RSYNC_PORT}/${REMOTE_RSYNC_MODULE}/$2"
: rc $?
}
rsync_setup_local() {
rsync_setup_local_filter_file
}
rsync_setup_local_filter_file() {
echo_debug "Creating local rsync filter file ..."
cat <<-EOT >> "${LOCAL_RSYNC_FILTER_FILE}"
# ignore known cache files
H .DS_Store
# don't sync custom files
H /TitaniumBackup.apk
H /TitaniumBackupPro.apk
# preserve standard Andoid directories
# P Alarms/
# P Android/
# P DCIM/
# P Download/
# P Movies/
# P Music/
# P Notifications/
# P Pictures/
# P Podcasts/
# P Ringtones/
# P storage/
EOT
# include user-defined rules
if [[ -e "${USER_RSYNC_FILTER_FILE}" ]]; then
echo "merge ${USER_RSYNC_FILTER_FILE}" >> "${LOCAL_RSYNC_FILTER_FILE}"
fi
if [[ ${OPT_DEBUG} ]]; then
echo_debug "Contents of local rsync filter file:"
cat "${LOCAL_RSYNC_FILTER_FILE}" && echo ""
fi
}
rsync_setup_remote() {
if [[ -z "$(${ADB} shell command -v \"${REMOTE_RSYNC_BIN}\")" ]]; then
echo_info "Device doesn't have rsync."
# try different locations for backup rsync
local script_dir
script_dir=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
for rsync_src_dir in "${script_dir}" "/usr/local/lib/android-backup"; do
local rsync_src="${rsync_src_dir}/rsync.bkp"
[[ -e "${rsync_src}" ]] && break
done
# have we found a backup rsync?
if [[ -e "${rsync_src}" ]]; then
echo_info "Providing temporary rsync from ${rsync_src} ..."
adb_push "${rsync_src}" "${REMOTE_TEMP_RSYNC_BIN}"
${ADB} shell chmod 755 "${REMOTE_TEMP_RSYNC_BIN}"
REMOTE_RSYNC_BIN="${REMOTE_TEMP_RSYNC_BIN}"
else
echo_error "Can't provide temporary rsync."
exit 4
fi
fi
echo_debug "Killing rsync daemon ..."
${ADB} shell ${ADB_SU_OPTIONS} "killall \"${REMOTE_RSYNC_BIN}\" 2>/dev/null"
# setup rsync daemon on the device
# from http://blog.josefsson.org/2015/11/28/automatic-android-replicant-backup-over-usb-using-rsync/
# and http://ptspts.blogspot.de/2015/03/how-to-use-rsync-over-adb-on-android.html
echo_debug "Writing remote rsync daemon config file ..."
if [[ ${OPT_ROOT} ]]; then
local root_options="use chroot = true\nuid = root\ngid = sdcard_rw\n"
else
local root_options="use chroot = false\n"
fi
${ADB} shell "echo \"address = 127.0.0.1\nport = ${REMOTE_RSYNC_PORT}\n\n[${REMOTE_RSYNC_MODULE}]\npath = /\n${root_options}read only = false\nlist = false\"" \> "${REMOTE_RSYNC_CONFIG}"
echo_debug "Setting up port-forwarding ..."
# forward a local port to the rsync daemon on the device
${ADB} forward tcp:${LOCAL_RSYNC_PORT} tcp:${REMOTE_RSYNC_PORT}
sleep 2
echo_debug "Starting remote rsync daemon ..."
local debug_options=''
# if [[ ${OPT_DEBUG} ]]; then
# debug_options="--verbose" # for debugging --no-detach --log-file=/proc/self/fd/2
# fi
${ADB} shell ${ADB_SU_OPTIONS} "${REMOTE_RSYNC_BIN} --daemon --config=\"${REMOTE_RSYNC_CONFIG}\" ${debug_options} &"
}
compare_serialno() {
local local_serialno
local local_serialno_location="${OPT_BACKUP_DIR}/device_serialno"
local remote_serialno
remote_serialno=$(${ADB_RAW} -d get-serialno)
# alternative: ${ADB} shell getprop | grep ro.boot.serialno | sed -r 's/^\[ro.boot.serialno\]: \[(.+)\]$/\1/g'
echo_debug "Device serial number: ${remote_serialno}"
if [[ ! -f "${local_serialno_location}" ]]; then
echo_warn "No device serial number in backup directory"
return
fi
local_serialno="$(<"${local_serialno_location}")"
echo_debug "Loaded device serial number from backup directory: ${local_serialno}"
DEVICE_SERIALNO="${local_serialno}"
if [[ "${local_serialno}" != "${remote_serialno}" ]]; then
echo_error "Device serial numbers don't match!"
echo_info "Serial number from backup directory: ${local_serialno}"
echo_info "Serial number from device: ${remote_serialno}"
echo_info "A mismatch can happen when you mix up backup directories or have a new device."
echo_info "If you you want to continue anyway, you can rename or delete \"${local_serialno_location}\" and rerun this script."
exit 3
fi
echo_info "Device serial numbers match."
}
find_remote_storages() {
local android_storage_base="/storage"
local storage_candidates
storage_candidates=$(${ADB} shell ls "${android_storage_base}")
for storage_name in ${storage_candidates}
do
# assume emulated/0 is covered by /sdcard and everything else isn't relevant
[[ "${storage_name}" = 'emulated' ]] && continue
[[ "${storage_name}" = 'self' ]] && continue
REMOTE_STORAGES+=("${android_storage_base}/${storage_name}")
done
}
parse_command_line_args() {
OPT_ASSUME_YES=''
OPT_BACKUP_DIR=''
OPT_DEBUG=''
OPT_ROOT=''
if [[ $# -eq 0 ]]; then
usage
exit 1
elif [[ $# -eq 1 ]]; then
case "$1" in
-h|--help)
usage
exit 1
;;
# default: fall through
esac
fi
while [[ $# -gt 1 ]]
do
case "$1" in
-y|--assume-yes)
readonly OPT_ASSUME_YES=1
;;
--debug)
readonly OPT_DEBUG=1
;;
-r|--use-root)
readonly OPT_ROOT=1
;;
*)
echo "Error: unknown argument \"$1\""
usage
exit 1
;;
esac
shift
done
# make sure we get a path to restore stuff from
if [[ -z "$1" ]]; then
echo "Please specify the directory your backups are in!"
exit 1
else
readonly OPT_BACKUP_DIR="$1"
fi
}
usage() {
echo "Usage: $0 [OPTIONS] <BACKUP_DIR>"
echo " Restore data from <BACKUP_DIR> to storages on the device."
echo " NOTE: this requires adb and rsync."
echo ""
echo "OPTIONS"
echo " -y,--assume-yes Assume \"yes\" to all questions"
echo " --debug Enable debug mode"
echo " -r,--use-root Use adb with root permissions"
}
main() {
parse_command_line_args "$@"
echo_debug "OPT_ASSUME_YES=${OPT_ASSUME_YES}"
echo_debug "OPT_BACKUP_DIR=${OPT_BACKUP_DIR}"
echo_debug "OPT_DEBUG=${OPT_DEBUG}"
echo_debug "OPT_ROOT=${OPT_ROOT}"
if [[ ! -d "${OPT_BACKUP_DIR}" ]]; then
echo_error "'${OPT_BACKUP_DIR}' is not a directory."
exit 2
fi
# make sure we don't overwrite anything by accident
if [[ -d "${OPT_BACKUP_DIR}" && ! ${OPT_ASSUME_YES} ]]; then
read -p "Are you sure you want to potentially overwrite files on your device? [Yes/No] " yn
case $yn in
'Yes'* ) ;; # just get through
'No'* ) exit;;
* ) exit 3;;
esac
fi
compare_serialno
ADB="${ADB_RAW} -s ${DEVICE_SERIALNO}"
if [[ ${OPT_ROOT} ]]; then
ADB_SU_OPTIONS="su -c"
fi
${ADB} wait-for-device
# will add entries to REMOTE_STORAGES
find_remote_storages
# TODO check if there are local directories that have no remote storage equivalent
rsync_setup_local
rsync_setup_remote
# install backup app so we can start restoring stuff on the device itself
echo_info "Restoring backup apps ..."
for apk in ${BKP_APKS}; do
if [[ -f "${OPT_BACKUP_DIR}/${apk}" ]]; then
adb_install "${OPT_BACKUP_DIR}/${apk}"
else
echo_warn "Can't find app ${apk}"
echo_info "... Skipped!"
fi
done
echo_info "Restoring some other things with priority ..."
for remote_storage in "${REMOTE_STORAGES[@]}"
do
local storage_name
storage_name=$(basename "${remote_storage}")
echo_info "... restoring ${storage_name} ..."
for dir in "${PRIO_DIRS[@]}"; do
local prio_dir_candidate="${storage_name}/${dir}"
echo_debug "... restoring data to '${OPT_BACKUP_DIR}/${prio_dir_candidate}' ..."
[[ ! -d "${OPT_BACKUP_DIR}/${prio_dir_candidate}" ]] && continue
rsync_push_module "${OPT_BACKUP_DIR}/${prio_dir_candidate}/" "${remote_storage}/${dir}/"
done
done
echo_info "... Done!"
echo_info "Restoring all the rest ..."
for remote_storage in "${REMOTE_STORAGES[@]}"
do
local storage_name
storage_name=$(basename "${remote_storage}")
echo_info "... restoring ${storage_name} ..."
rsync_push_module "${OPT_BACKUP_DIR}/${storage_name}/" "${remote_storage}/"
done
echo_info "... Done!"
# TODO: determine we're actually using Seedvault
echo_warn "If you're using Seedvault you might not be able to start restoring from the UI"
echo " In that case you can start the Seedvault restoration process by typing"
echo " *#*#7378673#*#* into the dialer app."
echo " see also https://github.com/seedvault-app/seedvault/wiki/FAQ#how-to-restore-after-passing-through-setup-wizard"
# see https://github.com/seedvault-app/seedvault/wiki/FAQ#how-to-restore-after-passing-through-setup-wizard
rsync_cleanup_remote
rsync_cleanup_local
}
main "$@"