/
filesystem
428 lines (386 loc) · 15.5 KB
/
filesystem
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
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
#!/bin/bash
source /usr/share/yunohost/helpers.d/getopts
CAN_BIND=${CAN_BIND:-1}
# Add a file or a directory to the list of paths to backup
#
# Note: this helper could be used in backup hook or in backup script inside an
# app package
#
# Details: ynh_backup writes SRC and the relative DEST into a CSV file. And it
# creates the parent destination directory
#
# If DEST is ended by a slash it complete this path with the basename of SRC.
#
# usage: ynh_backup --src_path=src_path [--dest_path=dest_path] [--is_big] [--not_mandatory]
# | arg: -s, --src_path - file or directory to bind or symlink or copy. it shouldn't be in
# the backup dir.
# | arg: -d, --dest_path - destination file or directory inside the
# backup dir
# | arg: -b, --is_big - Indicate data are big (mail, video, image ...)
# | arg: -m, --not_mandatory - Indicate that if the file is missing, the backup can ignore it.
# | arg: arg - Deprecated arg
#
# example:
# # Wordpress app context
#
# ynh_backup "/etc/nginx/conf.d/$domain.d/$app.conf"
# # => This line will be added into CSV file
# # "/etc/nginx/conf.d/$domain.d/$app.conf","apps/wordpress/etc/nginx/conf.d/$domain.d/$app.conf"
#
# ynh_backup "/etc/nginx/conf.d/$domain.d/$app.conf" "conf/nginx.conf"
# # => "/etc/nginx/conf.d/$domain.d/$app.conf","apps/wordpress/conf/nginx.conf"
#
# ynh_backup "/etc/nginx/conf.d/$domain.d/$app.conf" "conf/"
# # => "/etc/nginx/conf.d/$domain.d/$app.conf","apps/wordpress/conf/$app.conf"
#
# ynh_backup "/etc/nginx/conf.d/$domain.d/$app.conf" "conf"
# # => "/etc/nginx/conf.d/$domain.d/$app.conf","apps/wordpress/conf"
#
# #Deprecated usages (maintained for retro-compatibility)
# ynh_backup "/etc/nginx/conf.d/$domain.d/$app.conf" "${backup_dir}/conf/nginx.conf"
# # => "/etc/nginx/conf.d/$domain.d/$app.conf","apps/wordpress/conf/nginx.conf"
#
# ynh_backup "/etc/nginx/conf.d/$domain.d/$app.conf" "/conf/"
# # => "/etc/nginx/conf.d/$domain.d/$app.conf","apps/wordpress/conf/$app.conf"
#
ynh_backup() {
# TODO find a way to avoid injection by file strange naming !
# Declare an array to define the options of this helper.
local legacy_args=sdbm
declare -Ar args_array=( [s]=src_path= [d]=dest_path= [b]=is_big [m]=not_mandatory )
local src_path
local dest_path
local is_big
local not_mandatory
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
local dest_path="${dest_path:-}"
local is_big="${is_big:-0}"
local not_mandatory="${not_mandatory:-0}"
BACKUP_CORE_ONLY=${BACKUP_CORE_ONLY:-0}
# If backing up core only (used by ynh_backup_before_upgrade),
# don't backup big data items
if [ "$is_big" == "1" ] && [ "$BACKUP_CORE_ONLY" == "1" ] ; then
echo "$src_path will not be saved, because backup_core_only is set." >&2
return 0
fi
# ==============================================================================
# Format correctly source and destination paths
# ==============================================================================
# Be sure the source path is not empty
[[ -e "${src_path}" ]] || {
echo "Source path '${src_path}' does not exist" >&2
if [ "$not_mandatory" == "0" ]
then
echo "Source path '${SRC_PATH}' does not exist" >&2
# This is a temporary fix for fail2ban config files missing after the migration to stretch.
if echo "${src_path}" | grep --quiet "/etc/fail2ban"
then
touch "${src_path}"
echo "The missing file will be replaced by a dummy one for the backup !!!" >&2
else
return 1
fi
else
return 0
fi
}
# Transform the source path as an absolute path
# If it's a dir remove the ending /
src_path=$(realpath "$src_path")
# If there is no destination path, initialize it with the source path
# relative to "/".
# eg: src_path=/etc/yunohost -> dest_path=etc/yunohost
if [[ -z "$dest_path" ]]; then
dest_path="${src_path#/}"
else
if [[ "${dest_path:0:1}" == "/" ]]; then
# If the destination path is an absolute path, transform it as a path
# relative to the current working directory ($YNH_CWD)
#
# If it's an app backup script that run this helper, YNH_CWD is equal to
# $YNH_BACKUP_DIR/apps/APP_INSTANCE_NAME/backup/
#
# If it's a system part backup script, YNH_CWD is equal to $YNH_BACKUP_DIR
dest_path="${dest_path#$YNH_CWD/}"
# Case where $2 is an absolute dir but doesn't begin with $YNH_CWD
[[ "${dest_path:0:1}" == "/" ]] \
&& dest_path="${dest_path#/}"
fi
# Complete dest_path if ended by a /
[[ "${dest_path: -1}" == "/" ]] \
&& dest_path="${dest_path}/$(basename $src_path)"
fi
# Check if dest_path already exists in tmp archive
[[ ! -e "${dest_path}" ]] || {
echo "Destination path '${dest_path}' already exist" >&2
return 1
}
# Add the relative current working directory to the destination path
local rel_dir="${YNH_CWD#$YNH_BACKUP_DIR}"
rel_dir="${rel_dir%/}/"
dest_path="${rel_dir}${dest_path}"
dest_path="${dest_path#/}"
# ==============================================================================
# ==============================================================================
# Write file to backup into backup_list
# ==============================================================================
local src=$(echo "${src_path}" | sed -r 's/"/\"\"/g')
local dest=$(echo "${dest_path}" | sed -r 's/"/\"\"/g')
echo "\"${src}\",\"${dest}\"" >> "${YNH_BACKUP_CSV}"
# ==============================================================================
# Create the parent dir of the destination path
# It's for retro compatibility, some script consider ynh_backup creates this dir
mkdir -p $(dirname "$YNH_BACKUP_DIR/${dest_path}")
}
# Restore all files linked to the restore hook or to the restore app script
#
# usage: ynh_restore
#
ynh_restore () {
# Deduce the relative path of $YNH_CWD
local REL_DIR="${YNH_CWD#$YNH_BACKUP_DIR/}"
REL_DIR="${REL_DIR%/}/"
# For each destination path begining by $REL_DIR
cat ${YNH_BACKUP_CSV} | tr -d $'\r' | grep -ohP "^\".*\",\"$REL_DIR.*\"$" | \
while read line; do
local ORIGIN_PATH=$(echo "$line" | grep -ohP "^\"\K.*(?=\",\".*\"$)")
local ARCHIVE_PATH=$(echo "$line" | grep -ohP "^\".*\",\"$REL_DIR\K.*(?=\"$)")
ynh_restore_file --origin_path="$ARCHIVE_PATH" --dest_path="$ORIGIN_PATH"
done
}
# Return the path in the archive where has been stocked the origin path
#
# [internal]
#
# usage: _get_archive_path ORIGIN_PATH
_get_archive_path () {
# For security reasons we use csv python library to read the CSV
sudo python -c "
import sys
import csv
with open(sys.argv[1], 'r') as backup_file:
backup_csv = csv.DictReader(backup_file, fieldnames=['source', 'dest'])
for row in backup_csv:
if row['source']==sys.argv[2].strip('\"'):
print row['dest']
sys.exit(0)
raise Exception('Original path for %s not found' % sys.argv[2])
" "${YNH_BACKUP_CSV}" "$1"
return $?
}
# Restore a file or a directory
#
# Use the registered path in backup_list by ynh_backup to restore the file at
# the good place.
#
# usage: ynh_restore_file --origin_path=origin_path [--dest_path=dest_path] [--not_mandatory]
# | arg: -o, --origin_path - Path where was located the file or the directory before
# to be backuped or relative path to $YNH_CWD where it is located in the backup archive
# | arg: -d, --dest_path - Path where restore the file or the dir, if unspecified,
# the destination will be ORIGIN_PATH or if the ORIGIN_PATH doesn't exist in
# the archive, the destination will be searched into backup.csv
# | arg: -m, --not_mandatory - Indicate that if the file is missing, the restore process can ignore it.
#
# If DEST_PATH already exists and is lighter than 500 Mo, a backup will be made in
# /home/yunohost.conf/backup/. Otherwise, the existing file is removed.
#
# examples:
# ynh_restore_file "/etc/nginx/conf.d/$domain.d/$app.conf"
# # if apps/wordpress/etc/nginx/conf.d/$domain.d/$app.conf exists, restore it into
# # /etc/nginx/conf.d/$domain.d/$app.conf
# # if no, search a correspondance in the csv (eg: conf/nginx.conf) and restore it into
# # /etc/nginx/conf.d/$domain.d/$app.conf
#
# # DON'T GIVE THE ARCHIVE PATH:
# ynh_restore_file "conf/nginx.conf"
#
ynh_restore_file () {
# Declare an array to define the options of this helper.
local legacy_args=odm
declare -Ar args_array=( [o]=origin_path= [d]=dest_path= [m]=not_mandatory )
local origin_path
local archive_path
local dest_path
local not_mandatory
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
local origin_path="/${origin_path#/}"
local archive_path="$YNH_CWD${origin_path}"
# Default value for dest_path = /$origin_path
local dest_path="${dest_path:-$origin_path}"
local not_mandatory="${not_mandatory:-0}"
# If archive_path doesn't exist, search for a corresponding path in CSV
if [ ! -d "$archive_path" ] && [ ! -f "$archive_path" ] && [ ! -L "$archive_path" ]; then
if [ "$not_mandatory" == "0" ]
then
archive_path="$YNH_BACKUP_DIR/$(_get_archive_path \"$origin_path\")"
else
return 0
fi
fi
# Move the old directory if it already exists
if [[ -e "${dest_path}" ]]
then
# Check if the file/dir size is less than 500 Mo
if [[ $(du -sb ${dest_path} | cut -d"/" -f1) -le "500000000" ]]
then
local backup_file="/home/yunohost.conf/backup/${dest_path}.backup.$(date '+%Y%m%d.%H%M%S')"
mkdir -p "$(dirname "$backup_file")"
mv "${dest_path}" "$backup_file" # Move the current file or directory
else
ynh_secure_remove --file=${dest_path}
fi
fi
# Restore origin_path into dest_path
mkdir -p $(dirname "$dest_path")
# Do a copy if it's just a mounting point
if mountpoint -q $YNH_BACKUP_DIR; then
if [[ -d "${archive_path}" ]]; then
archive_path="${archive_path}/."
mkdir -p "$dest_path"
fi
cp -a "$archive_path" "${dest_path}"
# Do a move if YNH_BACKUP_DIR is already a copy
else
mv "$archive_path" "${dest_path}"
fi
}
# Deprecated helper since it's a dangerous one!
#
# [internal]
#
ynh_bind_or_cp() {
local AS_ROOT=${3:-0}
local NO_ROOT=0
[[ "${AS_ROOT}" = "1" ]] || NO_ROOT=1
echo "This helper is deprecated, you should use ynh_backup instead" >&2
ynh_backup "$1" "$2" 1
}
# Create a directory under /tmp
#
# [internal]
#
# Deprecated helper
#
# usage: ynh_mkdir_tmp
# | ret: the created directory path
ynh_mkdir_tmp() {
echo "The helper ynh_mkdir_tmp is deprecated." >&2
echo "You should use 'mktemp -d' instead and manage permissions \
properly with chmod/chown." >&2
local TMP_DIR=$(mktemp -d)
# Give rights to other users could be a security risk.
# But for retrocompatibility we need it. (This helpers is deprecated)
chmod 755 $TMP_DIR
echo $TMP_DIR
}
# Calculate and store a file checksum into the app settings
#
# $app should be defined when calling this helper
#
# usage: ynh_store_file_checksum --file=file
# | arg: -f, --file - The file on which the checksum will performed, then stored.
ynh_store_file_checksum () {
# Declare an array to define the options of this helper.
local legacy_args=f
declare -Ar args_array=( [f]=file= )
local file
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
local checksum_setting_name=checksum_${file//[\/ ]/_} # Replace all '/' and ' ' by '_'
ynh_app_setting_set --app=$app --key=$checksum_setting_name --value=$(sudo md5sum "$file" | cut -d' ' -f1)
# If backup_file_checksum isn't empty, ynh_backup_if_checksum_is_different has made a backup
if [ -n "${backup_file_checksum-}" ]
then
# Print the diff between the previous file and the new one.
# diff return 1 if the files are different, so the || true
diff --report-identical-files --unified --color=always $backup_file_checksum $file >&2 || true
fi
# Unset the variable, so it wouldn't trig a ynh_store_file_checksum without a ynh_backup_if_checksum_is_different before it.
unset backup_file_checksum
}
# Verify the checksum and backup the file if it's different
# This helper is primarily meant to allow to easily backup personalised/manually
# modified config files.
#
# $app should be defined when calling this helper
#
# usage: ynh_backup_if_checksum_is_different --file=file
# | arg: -f, --file - The file on which the checksum test will be perfomed.
#
# | ret: Return the name a the backup file, or nothing
ynh_backup_if_checksum_is_different () {
# Declare an array to define the options of this helper.
local legacy_args=f
declare -Ar args_array=( [f]=file= )
local file
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
local checksum_setting_name=checksum_${file//[\/ ]/_} # Replace all '/' and ' ' by '_'
local checksum_value=$(ynh_app_setting_get --app=$app --key=$checksum_setting_name)
# backup_file_checksum isn't declare as local, so it can be reuse by ynh_store_file_checksum
backup_file_checksum=""
if [ -n "$checksum_value" ]
then # Proceed only if a value was stored into the app settings
if ! echo "$checksum_value $file" | sudo md5sum -c --status
then # If the checksum is now different
backup_file_checksum="/home/yunohost.conf/backup/$file.backup.$(date '+%Y%m%d.%H%M%S')"
sudo mkdir -p "$(dirname "$backup_file_checksum")"
sudo cp -a "$file" "$backup_file_checksum" # Backup the current file
ynh_print_warn "File $file has been manually modified since the installation or last upgrade. So it has been duplicated in $backup_file_checksum"
echo "$backup_file_checksum" # Return the name of the backup file
fi
fi
}
# Delete a file checksum from the app settings
#
# $app should be defined when calling this helper
#
# usage: ynh_remove_file_checksum file
# | arg: -f, --file= - The file for which the checksum will be deleted
ynh_delete_file_checksum () {
# Declare an array to define the options of this helper.
local legacy_args=f
declare -Ar args_array=( [f]=file= )
local file
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
local checksum_setting_name=checksum_${file//[\/ ]/_} # Replace all '/' and ' ' by '_'
ynh_app_setting_delete --app=$app --key=$checksum_setting_name
}
# Remove a file or a directory securely
#
# usage: ynh_secure_remove --file=path_to_remove
# | arg: -f, --file - File or directory to remove
ynh_secure_remove () {
# Declare an array to define the options of this helper.
local legacy_args=f
declare -Ar args_array=( [f]=file= )
local file
# Manage arguments with getopts
ynh_handle_getopts_args "$@"
local forbidden_path=" \
/var/www \
/home/yunohost.app"
if [ $# -ge 2 ]
then
echo "/!\ Packager ! You provided more than one argument to ynh_secure_remove but it will be ignored... Use this helper with one argument at time." >&2
fi
if [[ "$forbidden_path" =~ "$file" \
# Match all paths or subpaths in $forbidden_path
|| "$file" =~ ^/[[:alnum:]]+$ \
# Match all first level paths from / (Like /var, /root, etc...)
|| "${file:${#file}-1}" = "/" ]]
# Match if the path finishes by /. Because it seems there is an empty variable
then
echo "Avoid deleting $file." >&2
else
if [ -e "$file" ]
then
sudo rm -R "$file"
else
echo "$file wasn't deleted because it doesn't exist." >&2
fi
fi
}