-
Notifications
You must be signed in to change notification settings - Fork 2
/
backup.sh
187 lines (147 loc) · 4.12 KB
/
backup.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
#!/bin/sh
# Copyright (C) 2020 BISDN GmbH
# Author: Jonas Gorski <jonas.gorski@bisdn.de>
#
# SPDX-License-Identifier: GPL-2.0
SYSTEM_BACKUP_FILE="etc/default/system-backup.txt"
USER_BACKUP_FILE="etc/default/user-backup.txt"
# $1 src $2 dest
cp_path_with_attr() {
local owner attr
[ ! -d "$2" ] || return 0
src_base="$(dirname $2)"
dest_base="$(dirname $2)"
cp_path_with_attr "$(dirname $1)" "$(dirname $2)"
mkdir "$2"
owner="$(stat -c %u:%g $1)"
attr="$(stat -c %a $1)"
[ -n "$DEBUG" ] && echo "DEBUG: setting owner to $owner for file $2"
chown "$owner" "$2"
[ -n "$DEBUG" ] && echo "DEBUG: setting attr to $attr for file $2"
chmod "$attr" "$2"
}
# $1 path $2 src $3 dest
add_to_backup() {
local oldpath relpath newpath src_base dst_base
# realpath will resolve any symlinks, but symlinks may point to outside
# of the temporary filesystem. Busybox's realpath does not support not
# resolving symlinks, so refuse to touch them at all.
[ -L "$2/$1" ] && return 0
# we cannot back up what doesn't exist
[ -e "$2/$1" ] || return 0
oldpath="$(realpath $2/$1)"
case "$oldpath" in
"$2"*)
;;
*)
echo "WARNING: refusing to backup file outside of filesystem: $1 => $oldpath" >&2
return 0
;;
esac
relpath="${oldpath#${2}/}"
newpath="$3/$relpath"
[ -n "$DEBUG" ] && echo "DEBUG: adding $relpath to backup"
cp_path_with_attr "$(dirname $oldpath)" "$(dirname $newpath)"
cp -a "$oldpath" "$(dirname $newpath)"
}
# $1 path $2 src $3 dest
remove_from_backup() {
local oldpath relpath newpath
# realpath will resolve any symlinks, but symlinks may point to outside
# of the temporary filesystem. Busybox's realpath does not support not
# resolving symlinks, so refuse to touch them at all.
[ -L "$2/$1" ] && return 0
# no need to delete non-existing files
[ -e "$2/$1" ] || return 0
oldpath="$(realpath $2/$1)"
case "$oldpath" in
"$2"*)
;;
*)
echo "WARNING: refusing to delete file outside of filesystem: $1 => $oldpath" >&2
return 0
;;
esac
relpath="${oldpath#${2}/}"
newpath="$3/$relpath"
[ -n "$DEBUG" ] && echo "DEBUG: removing $relpath from backup"
rm -rf "$newpath"
}
# $1 file $2 mode (+|-) $3 src $4 dst
parse_file() {
if [ ! -f "$1" ]; then
return 0
fi
while read -r line; do
case "$line" in
"#"*)
# comment
;;
"-"*)
if [ "$2" = "-" ]; then
remove_from_backup "${line:1}" $3 $4
fi
;;
/*)
if [ "$2" = "+" ]; then
add_to_backup "$line" $3 $4
fi
;;
esac
done < $1
}
# $1 src $2 dst
apply_fixups() {
# releases pre 4.7.0 are missing gshadow in the backup list
if [ -f "$2/etc/group" ] && [ ! -f "$2/etc/gshadow" ]; then
[ -n "$DEBUG" ] && echo "DEBUG: /etc/group found but no /etc/gshadow"
add_to_backup "/etc/gshadow" $1 $2
fi
}
# $1 backup target $2 backup storage dir
create_backup()
{
# step 1 - copy files to keep
for file in $1/var/lib/opkg/info/*.conffiles; do
[ -f "$file" ] || break
parse_file $file "+" $1 $2
done
parse_file "$1/$SYSTEM_BACKUP_FILE" "+" $1 $2
parse_file "$1/$USER_BACKUP_FILE" "+" $1 $2
# step 2 - remove files to drop
parse_file "$1/$SYSTEM_BACKUP_FILE" "-" $1 $2
parse_file "$1/$USER_BACKUP_FILE" "-" $1 $2
apply_fixups $1 $2
# step 3 - check if anything is left
[ -n "$(find $2 -type f)" ] || return 0
# step 4 - remove empty directories
find $2 -depth -type d -exec rmdir -p --ignore-fail-on-non-empty {} \;
DO_RESTORE=true
}
# $1 backup storage dir $2 restore target
restore_backup()
{
# rename existing target files if different
for file in $(find $1 -type f); do
basefile="${file#${1}/}"
# nothing to do if it's a new file
[ -f "$2/$basefile" ] || continue
# nothing to do if they are the same
cmp -s "$file" "$2/$basefile" && continue
case "$basefile" in
"etc/sudoers.d/"*)
# sudo only ignores files with . or ending in ~
BACKUP_SUFFIX=".default"
;;
*)
BACKUP_SUFFIX="-default"
;;
esac
[ -n "$DEBUG" ] && echo "DEBUG: $2/$basefile is different, creating backup as $basefile$BACKUP_SUFFIX" >&2
mv "$2/$basefile" "$2/$basefile$BACKUP_SUFFIX"
done
# now copy the files
for file in $1/*; do
cp -a $file $2
done
}