-
Notifications
You must be signed in to change notification settings - Fork 1
/
zdisk
executable file
·471 lines (412 loc) · 15.6 KB
/
zdisk
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
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
#!/bin/bash
#
# Manages a persistent compressed ramdisk.
#
# With the `zdisk start` a compressed ramdisk is created and mounted into `/mnt/zdisk`
# (can be changed with ZDISK_MOUNTPOINT environment variable). The ramdisk is sized at
# 25% of total memory (uncompressed size) but tipically it will use 12-13% memory or
# even less, depending of the stored data.
#
# With the command `zdisk stop` the contents are "squashed" in the `/var/lib/zdisk.sqfs`
# file (can be changed with the SAVED_DISK environment variable), and then the disk is
# destroyed and the memory freed.
#
# With the next `zdisk start` the saved contents are made available without loading
# into memory (the squashed file is mounted directly) and then overlayed with the
# compressed ramdisk, to make disk writeable. A next `zdisk save` will merge new contents
# with the saved backup, so the next time all changes will be available.
#
# With the `zdisk save` it is possible to make a backup of the disk contents without
# the need to stop the disk; this maybe usefull to take backups on demand or by
# scheduling it with cron.
#
# Dependencies:
# tools : zramctl, mksquashfs
# modules: zram, overlay
# filesystems: xfs
#
# Michele Tessaro
VERSION="1.1.0"
ZDISK_PATH="${ZDISK_PATH:-$(readlink -f "$0")}" # The path of this script
ROOT=${ROOT:-""} # Set externally to change the installation root
SYSTEMD_SERVICE_DIR="${ROOT}/etc/systemd/system" # Systemd configuration directory
CONF_DIR="${ROOT}/etc/conf.d" # Configuration files directory
BIN_PATH=${BIN_PATH:-"${ROOT}/usr/local/bin"} # Binary installation path
ZDISK_BIN="${BIN_PATH}/zdisk" # Name of the script binary
# These options are configurable throught the /etc/conf.d/zdisk configuration file or with environment variables
RESTORE_ON_START=${RESTORE_ON_START:-"true"}
SAVE_ON_STOP=${SAVE_ON_STOP:-"true"}
ZDISK_PCTSIZE=${ZDISK_PCTSIZE:-25} # Size of ramdisk (default 25% of ram)
memTotal=$(head -n1 /proc/meminfo | tr -s ' ' | cut -d ' ' -f2)
ZDISK_SIZE=${ZDISK_SIZE:-"$(( memTotal*ZDISK_PCTSIZE/100 ))K"} # Size of ramdisk, in kilobytes
ZDISK_ALGORITHM=${ZDISK_ALGORITHM:-"lzo"}
ZDISK_FS=${ZDISK_FS:-"xfs"} # Filesystem used for the ramdisk
SERVICE_NAME=$(basename "$0")
ZDISK_MOUNTPOINT=${ZDISK_MOUNTPOINT:-"${ROOT}/mnt/${SERVICE_NAME}"} # Where the disk is made available
SAVED_DISK="${ROOT}/var/lib/${SERVICE_NAME}" # The squashedfs filename used for storing ramdisk contents
SAVED_QUICK="${SAVED_DISK}.ovr.tbz2" # The file used for quick saves
SAVE_MODE=${SAVE_MODE:-"full"} # full or incremental; incremental is deprecated
SAVE_NICE=${SAVE_NICE:-10} # Priority when saving disk contents
USE_PBZIP2=${USE_PBZIP2:-true} # Use pbzip2, if installed
# These variables does not need to be changed
OVR_WORKDIR=${OVR_WORKDIR:-"${ROOT}/tmp"}
OVR_LOWER="${OVR_WORKDIR}/.${SERVICE_NAME}/squashed"
OVR_ZRAM="${OVR_WORKDIR}/.${SERVICE_NAME}/zram"
# OVER_UPPER and OVR_WORK must be on the same filesystem
OVR_UPPER="${OVR_ZRAM}/upper"
OVR_WORK="${OVR_ZRAM}/work"
function start() {
if grep -q "${ZDISK_MOUNTPOINT}" /etc/mtab; then
echo "Disk already mounted"
exit 1
else
modprobe overlay || die "Cannot load overlay module"
modprobe zram num_devices=4 || die "Cannot load zram module"
sleep 1
local ZRAM_DEVICE=$(zramctl -f)
sleep 1 # Give time to settle down, to avoid errors
# Creation of the compressed ramdisk
zramctl ${ZRAM_DEVICE} -s ${ZDISK_SIZE} -a ${ZDISK_ALGORITHM}
mkfs -t ${ZDISK_FS} ${ZRAM_DEVICE}
mkdir -p "${ZDISK_MOUNTPOINT}" # Make sure the mount point exists
if [[ "${RESTORE_ON_START}" == "true" && -f "${SAVED_DISK}.sqfs" ]]; then
# There is a previous backup, need to make it available
mkdir -p "${OVR_LOWER}" "${OVR_ZRAM}"
mount ${ZRAM_DEVICE} "${OVR_ZRAM}"
# Build of the squashfs+zram overlayed disk
mkdir -p "${OVR_UPPER}" "${OVR_WORK}"
mount -t squashfs "${SAVED_DISK}.sqfs" "${OVR_LOWER}"
mount -t overlay -o lowerdir=${OVR_LOWER},upperdir=${OVR_UPPER},workdir=${OVR_WORK} overlayfs "${ZDISK_MOUNTPOINT}"
else
# No previous backup, simply mount the compressed ramdisk
mount ${ZRAM_DEVICE} "${ZDISK_MOUNTPOINT}"
fi
if [[ "${RESTORE_ON_START}" == "true" && -f "${SAVED_QUICK}" ]]; then
# There is a quick backup, restore it
#mkdir -p "${OVR_ZRAM}"
tar -C "${ZDISK_MOUNTPOINT}" -jxf "${SAVED_QUICK}"
fi
# Make sure users can write to the disk
chgrp users "${ZDISK_MOUNTPOINT}"
chmod g+rwx "${ZDISK_MOUNTPOINT}"
fi
}
function stop() {
local doSave="${SAVE_ON_STOP}"
local quickSave=""
# Argument parsing
while [[ -n "$1" ]]; do
case "$1" in
"--nosave")
doSave="false"
;;
"--quick")
quickSave="--quick"
;;
*)
help
exit 1
;;
esac
shift
done
if grep -q "${ZDISK_MOUNTPOINT}" /etc/mtab; then
[[ "${doSave}" == "true" ]] && save ${quickSave}
# Find the zdisk device BEFORE unmounting it
local ZDISK_DEVICE=$(grep "${ZDISK_MOUNTPOINT}" /etc/mtab | cut -d ' ' -f1)
# We can unmount the zdisk
umount "${ZDISK_MOUNTPOINT}" || die "Unable to unmount ${ZDISK_MOUNTPOINT}, maybe in use"
case ${ZDISK_DEVICE} in
overlayfs)
# The disk was overlayed, we need to unmount all mount points
ZDISK_DEVICE=$(grep "${OVR_ZRAM}" /etc/mtab | cut -d ' ' -f1)
umount "${OVR_ZRAM}"
umount "${OVR_LOWER}"
;;
*)
;;
esac
# Now we can destroy the zram device to free memory
zramctl -r ${ZDISK_DEVICE}
# Finally, put the saved disk in place for the next start
if [[ -f "${SAVED_DISK}.tmp.sqfs" ]]; then
echo "Restoring ${SAVED_DISK}.sqfs from ${SAVED_DISK}.tmp.sqfs"
mv "${SAVED_DISK}.tmp.sqfs" "${SAVED_DISK}.sqfs"
fi
else
echo "Disk not mounted"
exit 1
fi
}
function save() {
local saveMode=""
local saveDisk="${SAVED_DISK}"
local options=""
local overwrite=""
# Argument parsing
while [[ -n "$1" ]]; do
case "$1" in
"--progress")
options="${options} -progress"
;;
"--overwrite")
overwrite="true"
;;
"--quick")
saveMode="quick"
;;
"--dest")
shift
saveDisk="$1"
[[ -z "${saveMode}" ]] && saveMode="full"
;;
*)
saveMode="$1"
;;
esac
shift
done
if grep -q "${ZDISK_MOUNTPOINT}" /etc/mtab; then
# Disck mounted, we can save it
[[ "${saveMode}" == "incremental" || "${saveMode}" == "full" || "${saveMode}" == "quick" ]] || saveMode="full"
if [[ "${saveMode}" == "quick" ]]; then
# Backup only the changed files (from the ramdisk)
mkdir -p $(dirname "${SAVED_QUICK}")
local PBZIP2_PATH="$(which pbzip2)"
local TAR_OPTS="-j"
if [[ "${USE_PBZIP2}" == "true" && -n "${PBZIP2_PATH}" ]]; then
TAR_OPTS="--use-compress-prog=${PBZIP2_PATH}"
echo "Doing ${saveMode} backup with pbzip2"
else
echo "Doing ${saveMode} backup"
fi
if [ -d "${OVR_ZRAM}/upper" ]; then
# Ramdisk + squashed filesystem, save only ramdisk contents
#nice -n ${SAVE_NICE}
tar -C "${OVR_ZRAM}/upper" ${TAR_OPTS} -cf "${SAVED_QUICK}" . || die "Unable to save ramdisk contents"
else
# Ramdisk only, save the contents
#nice -n ${SAVE_NICE}
tar -C "${ZDISK_MOUNTPOINT}" ${TAR_OPTS} -cf "${SAVED_QUICK}" . || die "Unable to save ramdisk contents"
fi
else
if [[ "${saveMode}" == "incremental" ]]; then
# It isn't necessary to backup the .prev_backup directory
options="-root-becomes .prev_backup -wildcards -e .prev_backup ${options}"
else
# The .prev_backup directory must be ignored so to build a much smaller file
options="-noappend -wildcards -e .prev_backup ${options}"
fi
if [[ -n "$(losetup -j ${saveDisk}.sqfs)" ]]; then
# The squashfs file is mounted, overwriting may be risky
saveDisk="${saveDisk}.tmp"
overwrite="true"
echo "Destination file is mounted, saving to ${saveDisk}.sqfs"
fi
if [[ -f "${saveDisk}.sqfs" && -z "${overwrite}" ]]; then
# Keep only one previous backup
[[ -f "${saveDisk}.bak" ]] && rm "${saveDisk}.bak"
[[ "${saveMode}" == "full" ]] && mv "${saveDisk}.sqfs" "${saveDisk}.bak"
fi
# Backup disk contents
echo "Doing ${saveMode} backup"
mkdir -p $(dirname "${saveDisk}")
nice -n ${SAVE_NICE} mksquashfs "${ZDISK_MOUNTPOINT}" "${saveDisk}.sqfs" ${options} || die "Unable to save ramdisk contents"
# Backup done, remove quick backup if present
[[ -f "${SAVED_QUICK}" ]] && rm "${SAVED_QUICK}"
return 0
fi
else
echo "Disk not mounted"
exit 1
fi
}
function install() {
local serviceType=""
local serviceName="zdisk"
local autostart="true"
local forceInstall=""
# Argument parsing
while [[ -n "$1" ]]; do
case "$1" in
"--systemd")
serviceType="systemd"
;;
"--autostart")
shift
autostart=$(boolValue "$1")
;;
"--force")
shift
forceInstall="true"
;;
*)
serviceName="$1"
;;
esac
shift
done
if [[ ! -f "${ZDISK_BIN}" || -n "${forceInstall}" ]]; then
# Copy the script in the bin directory
mkdir -p "${BIN_PATH}"
cp "${ZDISK_PATH}" "${ZDISK_BIN}"
else
echo "Command already installed, use --force to force reinstall"
fi
# Make an alias for the service
[ -f "${BIN_PATH}/${serviceName}" ] || ln -s "${ZDISK_BIN}" "${BIN_PATH}/${serviceName}"
case "$serviceType" in
"systemd")
mkdir -p "${SYSTEMD_SERVICE_DIR}" "${CONF_DIR}"
# Create a configuration file
[ -f "${CONF_DIR}/${serviceName}" ] || cat > "${CONF_DIR}/${serviceName}" <<EOF
# Where to mount the persistent compressed ramdisk
ZDISK_MOUNTPOINT="/mnt/${serviceName}"
# File where to backup ramdisk contents
SAVED_DISK="/var/lib/${serviceName}.sqfs"
# Backup mode. Possible values:
# incremental: fast but the backup may grow as deleted files still be the file (DEPRECATED)
# full : slow, but more compact (deleted files will be remove from the backup)
SAVEMODE="full"
# Use pbzip2, if installed, instead of bzip2 for parallel compression during quick saves.
# USE_PBZIP2="true"
# on stop, save disk contents
# SAVE_ON_STOP="true"
# On start, restore previous saved disk contents
# RESTORE_ON_START="true"
# zdisk size will be max 25% of total memory; used (compressed) memory is (hopefully) less.
# ZDISK_PCTSIZE=25
# Alternatively, the ramdisk size can be explicetly set
# ZDISK_SIZE="1G"
# Ramdisk compression algorithm; can be lzo o lz4
# ZDISK_ALGORITHM="lzo"
# Filesystem to use for the ramdisk. Usually xfs consumes less space.
# ZDISK_FS="xfs"
EOF
# Create the systemd service definition
[ -f "${SYSTEMD_SERVICE_DIR}/${serviceName}.service" ] || cat > "${SYSTEMD_SERVICE_DIR}/${serviceName}.service" <<EOF
[Unit]
Description=Persistent compressed ramdisk manager
Wants=local-fs.target
After=local-fs.target
[Service]
Type=oneshot
RemainAfterExit=true
EnvironmentFile=${CONF_DIR}/${serviceName}
ExecStart=${ZDISK_BIN} start
ExecStop=${ZDISK_BIN} stop --quick
TimeoutStopSec=infinity
[Install]
WantedBy=multi-user.target
EOF
# Inform systemd of the configuration change
systemctl daemon-reload
# Enable start on boot, if requested
[[ "${autostart}" == "true" ]] && systemctl enable "${serviceName}.service"
;;
*)
;;
esac
exit 0 # Everithing is ok
}
function uninstall() {
local serviceType=""
local serviceName=${SERVICE_NAME}
# Argument parsing
while [[ -n "$1" ]]; do
case "$1" in
"--systemd")
shift
serviceType="systemd"
serviceName=${1:-${SERVICE_NAME}}
;;
*)
help
exit 1
;;
esac
shift
done
case "$serviceType" in
"systemd")
# Stop the service, if running
systemctl stop "${serviceName}"
# Disable the service
systemctl disable "${serviceName}"
# Remove the service definition, if exists
[ -f "${SYSTEMD_SERVICE_DIR}/${serviceName}.service" ] && rm "${SYSTEMD_SERVICE_DIR}/${serviceName}.service"
# Remove the configuration file, if exists
[ -f "${CONF_DIR}/${serviceName}" ] && rm "${CONF_DIR}/${serviceName}"
;;
esac
[ -f "${BIN_PATH}/${serviceName}" ] && rm "${BIN_PATH}/${serviceName}"
exit 0 # Everithing is ok
}
function boolValue() {
local default=${2:-"true"}
case "${1:-$default}" in
"1"|"true"|"yes"|"on")
echo "true"
;;
"0"|"false"|"no"|"off")
echo "false"
;;
*)
die "Expected boolean value, got '$1'"
;;
esac
}
function die() {
echo "$1"
exit 1
}
function help() {
cat <<EOF
Persistent compressed ramdisk manager v${VERSION}
Usage: $0 {command} [options]
command description
------- -----------
start mounts the compressed ramdisk
stop unmounts the compressed ramdisk
--nosave no not save ramdisk contents
save save contents of a mounted ramdisk
--progress show compression progress
--dest file save to file instead of ${SAVED_DISK}.sqfs
--overwrite overwrite if exists (do not create .bak file)
install [name] install script command (default name=${SERVICE_NAME})
--systemd install also systemd service
--autostart [on|off] make service start at boot (default: on)
--force force installation even if already installed
uninstall [name] uninstall script command (default name=${SERVICE_NAME})
--systemd remove also systemd service
EOF
}
# Options parsing
case "$1" in
"start")
start
;;
"stop")
shift
stop $@
;;
"save")
shift
save --progress $@
;;
"install")
shift
install $@
;;
"uninstall")
shift
uninstall $@
;;
"help")
help
;;
*)
help
exit 1
;;
esac