Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
lima-vm
GitHub Repository: lima-vm/lima
Path: blob/master/pkg/cidata/cidata.TEMPLATE.d/boot/04-persistent-data-volume.sh
2655 views
1
#!/bin/bash
2
3
# SPDX-FileCopyrightText: Copyright The Lima Authors
4
# SPDX-License-Identifier: Apache-2.0
5
6
# bash is used for enabling `set -o pipefail`.
7
# NOTE: On Alpine, /bin/bash is ash with ASH_BASH_COMPAT, not GNU bash
8
set -eux -o pipefail
9
10
# Restrict the rest of this script to Alpine until it has been tested with other distros
11
test -f /etc/alpine-release || exit 0
12
13
# Nothing to do unless we are running from a ramdisk
14
[ "$(awk '$2 == "/" {print $3}' /proc/mounts)" != "tmpfs" ] && exit 0
15
16
# Data directories that should be persisted across reboots
17
DATADIRS="/etc /home /root /tmp /usr/local /var/lib"
18
19
# Prepare mnt.sh (used for restoring mounts later)
20
echo "#!/bin/sh" >/mnt.sh
21
echo "set -eux" >>/mnt.sh
22
for DIR in ${DATADIRS}; do
23
while IFS= read -r LINE; do
24
[ -z "$LINE" ] && continue
25
MNTDEV="$(echo "${LINE}" | awk '{print $1}')"
26
# unmangle " \t\n\\#"
27
# https://github.com/torvalds/linux/blob/v6.6/fs/proc_namespace.c#L89
28
MNTPNT="$(echo "${LINE}" | awk '{print $2}' | sed -e 's/\\040/ /g; s/\\011/\t/g; s/\\012/\n/g; s/\\134/\\/g; s/\\043/#/g')"
29
# Ignore if MNTPNT is neither DIR nor a parent directory of DIR.
30
# It is not a parent if MNTPNT doesn't start with DIR, or the first
31
# character after DIR isn't a slash.
32
WITHOUT_DIR="${MNTPNT#"$DIR"}"
33
# shellcheck disable=SC2166
34
[ "$MNTPNT" != "$DIR" ] && [ "$MNTPNT" == "$WITHOUT_DIR" -o "${WITHOUT_DIR::1}" != "/" ] && continue
35
MNTTYPE="$(echo "${LINE}" | awk '{print $3}')"
36
[ "${MNTTYPE}" = "ext4" ] && continue
37
[ "${MNTTYPE}" = "tmpfs" ] && continue
38
MNTOPTS="$(echo "${LINE}" | awk '{print $4}')"
39
if [ "${MNTTYPE}" = "9p" ]; then
40
# https://github.com/torvalds/linux/blob/v6.6/fs/9p/v9fs.h#L61
41
MNTOPTS="$(echo "${MNTOPTS}" | sed -e 's/cache=8f,/cache=fscache,/; s/cache=f,/cache=loose,/; s/cache=5,/cache=mmap,/; s/cache=1,/cache=readahead,/; s/cache=0,/cache=none,/')"
42
fi
43
# Before mv, unmount filesystems (virtiofs, 9p, etc.) below "${DIR}", otherwise host mounts will be wiped out
44
# https://github.com/rancher-sandbox/rancher-desktop/issues/6582
45
umount "${MNTPNT}" || exit 1
46
MNTPNT=${MNTPNT//\\/\\\\}
47
MNTPNT=${MNTPNT//\"/\\\"}
48
echo "mount -t \"${MNTTYPE}\" -o \"${MNTOPTS}\" \"${MNTDEV}\" \"${MNTPNT}\"" >>/mnt.sh
49
done </proc/mounts
50
done
51
chmod +x /mnt.sh
52
53
mkdir -p /mnt/data
54
if [ -e /dev/disk/by-label/data-volume ]; then
55
# Find which disk is data volume on
56
DATA_DISK=$(blkid | grep "data-volume" | awk '{split($0,s,":"); sub(/\d$/, "", s[1]); print s[1]};')
57
# growpart command may be missing in older VMs
58
if command -v growpart >/dev/null 2>&1 && command -v resize2fs >/dev/null 2>&1; then
59
# Automatically expand the data volume filesystem
60
growpart "$DATA_DISK" 1 || true
61
# Only resize when filesystem is in a healthy state
62
if e2fsck -f -p /dev/disk/by-label/data-volume; then
63
resize2fs /dev/disk/by-label/data-volume || true
64
fi
65
fi
66
# Mount data volume
67
mount -t ext4 /dev/disk/by-label/data-volume /mnt/data
68
# Update /etc files that might have changed during this boot
69
cp /etc/network/interfaces /mnt/data/etc/network/
70
cp /etc/resolv.conf /mnt/data/etc/
71
if [ -f /etc/localtime ]; then
72
# Preserve symlink
73
cp -d /etc/localtime /mnt/data/etc/
74
# setup-timezone copies the single zoneinfo file into /etc/zoneinfo and targets the symlink there
75
if [ -d /etc/zoneinfo ]; then
76
rm -rf /mnt/data/etc/zoneinfo
77
cp -r /etc/zoneinfo /mnt/data/etc
78
fi
79
fi
80
if [ -f /etc/timezone ]; then
81
cp /etc/timezone /mnt/data/etc/
82
fi
83
# TODO there are probably others that should be updated as well
84
else
85
# Find an unpartitioned disk and create data-volume
86
DISKS=$(lsblk --list --noheadings --output name,type | awk '$2 == "disk" {print $1}')
87
for DISK in ${DISKS}; do
88
IN_USE=false
89
# Looking for a disk that is not mounted or partitioned
90
# shellcheck disable=SC2013
91
for PART in $(awk '/^\/dev\// {gsub("/dev/", ""); print $1}' /proc/mounts); do
92
if [ "${DISK}" == "${PART}" ] || [ -e /sys/block/"${DISK}"/"${PART}" ]; then
93
IN_USE=true
94
break
95
fi
96
done
97
if [ "${IN_USE}" == "false" ]; then
98
echo 'type=83' | sfdisk --label dos /dev/"${DISK}"
99
PART=$(lsblk --list /dev/"${DISK}" --noheadings --output name,type | awk '$2 == "part" {print $1}')
100
mkfs.ext4 -L data-volume /dev/"${PART}"
101
mount -t ext4 /dev/disk/by-label/data-volume /mnt/data
102
# setup apk package cache
103
mkdir -p /mnt/data/apk/cache
104
mkdir -p /etc/apk
105
ln -s /mnt/data/apk/cache /etc/apk/cache
106
# Move all persisted directories to the data volume
107
for DIR in ${DATADIRS}; do
108
DEST="/mnt/data$(dirname "${DIR}")"
109
mkdir -p "${DIR}" "${DEST}"
110
mv "${DIR}" "${DEST}"
111
done
112
# Make sure all data moved to the persistent volume has been committed to disk
113
sync
114
break
115
fi
116
done
117
fi
118
for DIR in ${DATADIRS}; do
119
if [ -d /mnt/data"${DIR}" ]; then
120
mkdir -p "${DIR}"
121
mount --bind /mnt/data"${DIR}" "${DIR}"
122
fi
123
done
124
# Remount submounts on top of the new ${DIR}
125
/mnt.sh
126
# Reinstall packages from /mnt/data/apk/cache into the RAM disk
127
apk fix --no-network
128
129