Skip to content

Commit c9fbf7e

Browse files
committed
backup-lib: Backup and restore termux
Use special logic to provide better UX for restoring. Restoring incrementally causes issues like failures with ordinary binaries like 'ls' returning permission denied. Likely because chmod is not correct or permissions are not preserved when restored from cloud storage. Downside is a new tar.gz on each backup, so no incremental backup adavantages for backup of termux app itself. #17
1 parent 02211ce commit c9fbf7e

File tree

2 files changed

+107
-45
lines changed

2 files changed

+107
-45
lines changed

README.md

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,12 @@ Starting from there, backups made with older versions can no longer be restored.
5050

5151
```shell
5252
# Install packages
53-
apt install termux-api tsu openssh
54-
# Either install
55-
apt install rsync
56-
# or
57-
apt install rclone
58-
59-
# Fore remote backups, set up key
60-
ssh-keygen -t ecdsa -b 521
53+
pkg install -y git termux-api tsu rsync # or rclone
54+
55+
# Fore remote backups via rsync, set up key. For rclone see bellow.
56+
ssh-keygen
6157
# Copy key to the remote machine. Password authentication has to be enabled in order to install pubkey on remote machine.
62-
ssh-copy-id -p 22222 -i id_ecdsa.pub user@host
58+
ssh-copy-id -i id_ecdsa.pub user@host # Specify port, if necessary: -p 22222
6359
```
6460

6561
### Usage
@@ -116,18 +112,36 @@ export LOG_LEVEL='INFO' # Options: TRACE, INFO WARN, OFF. Default: INFO
116112
# Restores all apps from a folder (except termux, because this would cancel restore process! See bellow)
117113
# It the app does not work as expected after restore, consider restoring the keystore (see above)
118114
./restore-all.sh user@host:/my/folder/backup/
115+
```
116+
117+
### Restore termux
119118

120-
# Restore termux separately, if necessary
121-
pkg install rsync
122-
# Uncomment if needed
123-
#RSYNC_ARGS=-e "ssh -p 22222 -i $HOME/.ssh/mykey"
124-
rsync --stats --progress --human-readable -r --times $RSYNC_ARGS user@host:/my/folder/backup/com.termux/data/data/com.termux/files/home/ ~
125-
rsync --stats --progress --human-readable -r --times $RSYNC_ARGS user@host:/my/folder/backup/com.termux/data/data/com.termux/files/usr ../usr/
126-
# Packages are there but don't seem to work, so install them again
127-
for pkg in `dpkg --get-selections | awk '{print $1}' | egrep -v '(dpkg|apt|mysql|mythtv)'` ; do apt-get -y --force-yes install --reinstall $pkg ; done
128-
# If you have been using a different shell, re-enable it, for example:
129-
#chsh -s zsh
119+
* Install termux app either
120+
* manually from backup,
121+
* directly via apk from [fdroid](https://f-droid.org/de/packages/com.termux/) or [github](https://github.com/termux/termux-app/releases/) or
122+
* via an app store app
123+
* [Install packages](#preparation)
124+
* `git clone` this repo.
125+
💡 To make sure no to interfere with the version that is restored from the backup clone it to a different directory
126+
127+
```shell
128+
termux-setup-storage
129+
# Restore .ssh and or `.suroot/.config/rclone/rclone.conf` if necessary
130+
# e.g. mkdir -p .suroot/.config/rclone && cp storage/downloads/rclone.conf
131+
# or cp -r storage/downloads/.ssh .
132+
133+
cd storage/downloads
134+
git clone https://github.com/schnatterer/termux-scripts/
135+
cd termux-scripts/backup
136+
./restore-app.sh com.termux
137+
# or
138+
./restore-app.sh user@host:/my/folder/backup/com.termux --data
139+
# or
140+
./restore-app.sh --rclone remote-encrypted:/my/folder/backup/com.termux --data
141+
142+
# chsh if necessary and restart app
130143
```
144+
131145
### Rclone
132146

133147
* `--rclone` - use `rclone` instead of `rsync`

scripts/backup/backup-lib.sh

Lines changed: 74 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,47 @@ function backupApp() {
1010
fi
1111

1212
if [[ "${APK}" != 'true' ]]; then
13-
14-
# Backup to target paths as short as possible to avoid "path too long" errors
15-
backupFolder "/data/data/${packageName}" "${baseDestFolder}/data/"
16-
17-
backupFolder "/sdcard/Android/data/${packageName}" "${baseDestFolder}/sdcard/"
18-
19-
# Backing up to /sdcard/data and /sdcard/media would have been more intuitive
20-
# But: media was added later and migrating data would be too much effort for me right now
21-
backupFolder "/sdcard/Android/media/${packageName}" "${baseDestFolder}/sdcard-media/"
13+
if [[ "${packageName}" != 'com.termux' ]]; then
14+
# Backup to target paths as short as possible to avoid "path too long" errors
15+
backupFolder "/data/data/${packageName}" "${baseDestFolder}/data/"
16+
17+
backupFolder "/sdcard/Android/data/${packageName}" "${baseDestFolder}/sdcard/"
18+
19+
# Backing up to /sdcard/data and /sdcard/media would have been more intuitive
20+
# But: media was added later and migrating data would be too much effort for me right now
21+
backupFolder "/sdcard/Android/media/${packageName}" "${baseDestFolder}/sdcard-media/"
22+
else
23+
backupTermux "$@"
24+
fi
2225
fi
2326

2427
if [[ "${DATA}" != 'true' ]]; then
25-
# Backup all APKs from path (can be multiple for split-apks!)
26-
apkPath=$(dirname "$(sudo pm path "$packageName" | head -n1 | sed 's/package://')")
27-
# Only sync APKs, libs, etc are extracted during install
28-
# shellcheck disable=SC2046
29-
# This might return multiple parameters that we don't want quoted here
30-
doSync "$apkPath/" "$baseDestFolder/" $(includeOnlyApk)
28+
# Backup all APKs from path (can be multiple for split-apks!)
29+
apkPath=$(dirname "$(sudo pm path "$packageName" | head -n1 | sed 's/package://')")
30+
# Only sync APKs, libs, etc are extracted during install
31+
# shellcheck disable=SC2046
32+
# This might return multiple parameters that we don't want quoted here
33+
doSync "$apkPath/" "$baseDestFolder/" $(includeOnlyApk)
3134
fi
3235
}
3336

37+
function backupTermux() {
38+
local tmpFolder packageName="$1"
39+
local baseDestFolder="$2/$1"
40+
# Create in HOME, because usr/tmp is part of the backup
41+
# For local backups we could get rid of the temp folder, but this would make the code more complicated
42+
tmpFolder=$(mktemp -d "--tmpdir=$HOME")
43+
44+
backupFolder "/data/data/${packageName}/files/home" "${baseDestFolder}/data/files/home"
45+
46+
trace "Writing termux.tgz to ${tmpFolder}"
47+
trap "rm -rf ${tmpFolder}" 0
48+
termux-backup "${tmpFolder}/termux.tgz"
49+
doSync "${tmpFolder}/termux.tgz" "${baseDestFolder}/"
50+
51+
rm -rf "${tmpFolder}"
52+
}
53+
3454
function backupFolder() {
3555
srcFolder="$1"
3656
rootDestFolder="$2"
@@ -60,35 +80,63 @@ function restoreApp() {
6080
fi
6181

6282
if [[ "${APK}" != 'true' ]]; then
63-
user=$(sudo stat -c '%U' "/data/data/$packageName")
64-
group=$(sudo stat -c '%G' "/data/data/$packageName")
83+
user=$(sudo stat -c '%U' "/data/data/${packageName}")
84+
group=$(sudo stat -c '%G' "/data/data/${packageName}")
6585

66-
restoreFolder "${rootSrcFolder}" "data" "/data/data"
86+
if [[ "${packageName}" != 'com.termux' ]]; then
87+
restoreFolder "${rootSrcFolder}" "data" "/data/data"
6788

68-
restoreFolder "${rootSrcFolder}" "sdcard" "/sdcard/Android/data"
89+
restoreFolder "${rootSrcFolder}" "sdcard" "/sdcard/Android/data"
6990

70-
restoreFolder "${rootSrcFolder}" "sdcard-media" "/sdcard/Android/media"
91+
restoreFolder "${rootSrcFolder}" "sdcard-media" "/sdcard/Android/media"
92+
else
93+
restoreTermux "$@"
94+
fi
7195
fi
7296
}
7397

98+
function restoreTermux() {
99+
local tmpFolder rootSrcFolder="$1"
100+
101+
restoreFolder "${rootSrcFolder}" "data/files/home" "/data/data"
102+
103+
# Create in HOME, because usr/tmp is part of the restore.
104+
# This might not be strictly necessary here but, still might avoid trouble
105+
# For restores from local source we could get rid of the temp folder, but this would make the code more complicated
106+
tmpFolder=$(mktemp -d "--tmpdir=$HOME")
107+
108+
trace "Fetching termux.tgz to ${tmpFolder}"
109+
trap "rm -rf ${tmpFolder}" 0
110+
doSync "${rootSrcFolder}/termux.tgz" "${tmpFolder}" '--include=*.tgz'
111+
sudo chown -R "$(id -u):$(id -g)" "${tmpFolder}"
112+
rm -rf " ${tmpFolder}"
113+
114+
trace "Restoring termux from ${tmpFolder}"
115+
termux-restore "${tmpFolder}/termux.tgz"
116+
117+
info "Restored termux. Pleas restart termux app."
118+
info "Optional: if you used a shell other than bash, set it as default again, e.g. chsh zsh"
119+
}
120+
74121
function restoreFolder() {
75122
# e.g. /folder/com.nxp.taginfolite
76123
# or remote:/folder/com.nxp.taginfolite
77124
# or remote:/folder/com.nxp.taginfolite/
78125
local packageName rootSrcFolder="$1"
79126
# e.g. com.nxp.taginfolite
80127
packageName=$(extractAppNameFromFolder "$rootSrcFolder")
81-
# e.g. data
128+
# e.g. data or data/foo/bar
82129
local relativeSrcFolder="$2"
130+
# e.g. '' or 'foo/bar'
131+
local subFolder=''
132+
[[ "${relativeSrcFolder}" == */* ]] && subFolder="/${relativeSrcFolder#*/}"
83133
# e.g. /data/data
84134
local rootDestFolder="$3"
85135

86-
87136
# e.g. /folder/com.nxp.taginfolite/data/
88137
local actualSrcFolder="${rootSrcFolder}/${relativeSrcFolder}"
89138
# e.g. /data/data/com.nxp.taginfolite
90-
local actualDestFolder="${rootDestFolder}/${packageName}"
91-
139+
local actualDestFolder="${rootDestFolder}/${packageName}${subFolder}"
92140

93141
if [[ "$(checkActualSourceFolderExists "${actualSrcFolder}")" == 'true' ]]; then
94142
trace "Restoring data to ${actualDestFolder}"
@@ -101,8 +149,7 @@ function restoreFolder() {
101149
fi
102150
else
103151
info "Backup does not contain folder '${actualSrcFolder}'. Skipping"
104-
fi
105-
152+
fi
106153
}
107154

108155
function extractAppNameFromFolder() {
@@ -160,6 +207,7 @@ function backupFolderSyncArgs() {
160207
}
161208

162209
function includeOnlyApk() {
210+
# TODO simplify using '--include=*.apk'?
163211
if [[ "${RCLONE}" == 'true' ]]; then
164212
# Avoid fuss with whitespaces inside the filter rules by importing them from a file
165213
echo --filter-from="${LIB_DIR}/rclone-apk-filter.txt"

0 commit comments

Comments
 (0)