Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .ls-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ ignore:
- hack/common.inc.sh
- pkg/cidata/cidata.TEMPLATE.d
- pkg/cidata/cidata.TEMPLATE.d/util/compare_version.sh
- pkg/cidata/cidata.TEMPLATE.d/util/escape_fstab.sh
- pkg/cidata/cidata.TEMPLATE.d/util/unescape_fstab.sh
- pkg/cidata/wincidata.TEMPLATE.d
- website
# "ubuntu-24.04" does not follow the kebab-case
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ for DIR in ${DATADIRS}; do
MNTDEV="$(echo "${LINE}" | awk '{print $1}')"
# unmangle " \t\n\\#"
# https://github.com/torvalds/linux/blob/v6.6/fs/proc_namespace.c#L89
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')"
MNTPNT="$(echo "${LINE}" | awk '{print $2}' | unescape_fstab.sh)"
# Ignore if MNTPNT is neither DIR nor a parent directory of DIR.
# It is not a parent if MNTPNT doesn't start with DIR, or the first
# character after DIR isn't a slash.
Expand Down
19 changes: 18 additions & 1 deletion pkg/cidata/cidata.TEMPLATE.d/boot.Linux/05-lima-mounts.sh
Comment thread
ekalinin marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,21 @@ if ! [[ ${LIMA_CIDATA_VMTYPE} == "vz" && ${LIMA_CIDATA_MOUNTTYPE} == "virtiofs"
exit 0
fi

# cloud-init's cc_mounts writes the mount point into /etc/fstab without octal-escaping it, so a
# space/tab in the path makes an unparsable line that mount(8) silently skips via nofail.
# cc_mounts already created the directory from the unescaped value, so just repair the fstab
# syntax (see util/escape_fstab.sh) and (re)mount.
# https://github.com/lima-vm/lima/issues/5136
# https://github.com/abiosoft/colima/issues/1471
if grep -q virtiofs /etc/fstab; then
escape_fstab.sh </etc/fstab >/etc/fstab.lima.tmp &&
cat /etc/fstab.lima.tmp >/etc/fstab && rm -f /etc/fstab.lima.tmp
# Mount entries cc_mounts skipped due to the previously broken line (already-mounted ones
# are a no-op). On Oracle Linux the virtiofs module is installed later in
# 30-install-packages.sh (REMOUNT_VIRTIOFS=1 remounts then), so tolerate failure here.
mount -t virtiofs -a || true
fi

# Update fstab entries and unmount/remount the volumes with secontext options
# when selinux is enabled in kernel
if [ -d /sys/fs/selinux ]; then
Expand Down Expand Up @@ -42,7 +57,9 @@ if [ -d /sys/fs/selinux ]; then
fi
sed -i -e "$line""s/comment=cloudconfig/comment=cloudconfig,context=\"$label\"/g" /etc/fstab
if [[ ${MOUNT_OPTIONS} != *"$label"* ]]; then
MOUNT_POINT=$(awk -v line="$line" 'NR==line {print $2}' /etc/fstab)
# fstab stores the mount point octal-escaped (e.g. space = "\040"); decode
# it before passing the path to mount(8).
MOUNT_POINT=$(awk -v line="$line" 'NR==line {print $2}' /etc/fstab | unescape_fstab.sh)
OPTIONS=$(awk -v line="$line" 'NR==line {print $4}' /etc/fstab)

#########################################################
Expand Down
68 changes: 68 additions & 0 deletions pkg/cidata/cidata.TEMPLATE.d/util/escape_fstab.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/bin/bash

# SPDX-FileCopyrightText: Copyright The Lima Authors
# SPDX-License-Identifier: Apache-2.0

# Read an /etc/fstab on stdin and write it to stdout with the mount-point field
# octal-escaped (per fstab(5)) for cloud-config virtiofs entries whose path
# contains a space or tab. cloud-init's cc_mounts writes the mount point verbatim,
# so a space/tab produces an unparsable line that mount(8) silently skips via the
# nofail option. Fields are tab-separated, so -F'\t' isolates the mount point;
# already-escaped paths have no literal space/tab, so the transformation is
# idempotent (and stays correct once cloud-init escapes the field itself).
#
# See:
# https://github.com/lima-vm/lima/issues/5136
# https://github.com/abiosoft/colima/issues/1471
# https://github.com/canonical/cloud-init/issues/3603 (cc_mounts does not escape)
# https://github.com/canonical/cloud-init/issues/6911 (the upstream cloud-init fix)

set -eu

: "${SELFTEST:=}"
if [ -n "${SELFTEST}" ]; then
unset SELFTEST
tab=$(printf '\t')
check() {
local desc=$1 input=$2 want=$3 got
got=$(printf '%s\n' "${input}" | "$0")
if [ "${got}" = "${want}" ]; then
echo "ok: ${desc}"
else
echo "FAIL: ${desc}" >&2
printf ' want: %q\n got: %q\n' "${want}" "${got}" >&2
return 1
fi
}
echo >&2 "=== Running tests ==="
check "space in the mount point is escaped" \
"tag${tab}/tmp/dir with spaces${tab}virtiofs${tab}rw,nofail,comment=cloudconfig${tab}0${tab}0" \
"tag${tab}/tmp/dir\\040with\\040spaces${tab}virtiofs${tab}rw,nofail,comment=cloudconfig${tab}0${tab}0"
check "already-escaped path is unchanged (idempotent)" \
"tag${tab}/tmp/dir\\040with\\040spaces${tab}virtiofs${tab}rw,nofail,comment=cloudconfig${tab}0${tab}0" \
"tag${tab}/tmp/dir\\040with\\040spaces${tab}virtiofs${tab}rw,nofail,comment=cloudconfig${tab}0${tab}0"
check "path without whitespace is unchanged" \
"tag${tab}/mnt/nospace${tab}virtiofs${tab}rw,nofail,comment=cloudconfig${tab}0${tab}0" \
"tag${tab}/mnt/nospace${tab}virtiofs${tab}rw,nofail,comment=cloudconfig${tab}0${tab}0"
check "backslash is escaped before the space" \
"tag${tab}/a b\\c${tab}virtiofs${tab}rw,comment=cloudconfig${tab}0${tab}0" \
"tag${tab}/a\\040b\\134c${tab}virtiofs${tab}rw,comment=cloudconfig${tab}0${tab}0"
check "entry without comment=cloudconfig is unchanged" \
"tag${tab}/tmp/dir with spaces${tab}virtiofs${tab}rw${tab}0${tab}0" \
"tag${tab}/tmp/dir with spaces${tab}virtiofs${tab}rw${tab}0${tab}0"
check "non-virtiofs entry is unchanged" \
"/dev/sda1${tab}/data dir${tab}ext4${tab}defaults,comment=cloudconfig${tab}0${tab}0" \
"/dev/sda1${tab}/data dir${tab}ext4${tab}defaults,comment=cloudconfig${tab}0${tab}0"
echo >&2 "=== All tests passed ==="
exit 0
fi

awk -F'\t' 'BEGIN { OFS = "\t" }
$3 == "virtiofs" && $4 ~ /comment=cloudconfig/ && $2 ~ /[ \t]/ {
p = $2
gsub(/\\/, "\\134", p) # backslash first so introduced escapes are not re-escaped
gsub(/ /, "\\040", p)
gsub(/\t/, "\\011", p)
$2 = p
}
{ print }'
41 changes: 41 additions & 0 deletions pkg/cidata/cidata.TEMPLATE.d/util/unescape_fstab.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/bin/bash

# SPDX-FileCopyrightText: Copyright The Lima Authors
# SPDX-License-Identifier: Apache-2.0

# Read a string on stdin and write it to stdout with the octal escapes used in
# /etc/fstab and /proc/mounts decoded to their literal characters:
# "\040" -> space, "\011" -> tab, "\012" -> newline, "\134" -> backslash,
# "\043" -> "#". The inverse of util/escape_fstab.sh.
# https://github.com/torvalds/linux/blob/v6.6/fs/proc_namespace.c#L89

set -eu

: "${SELFTEST:=}"
if [ -n "${SELFTEST}" ]; then
unset SELFTEST
tab=$(printf '\t')
check() {
local desc=$1 input=$2 want=$3 got
got=$(printf '%s\n' "${input}" | "$0")
if [ "${got}" = "${want}" ]; then
echo "ok: ${desc}"
else
echo "FAIL: ${desc}" >&2
printf ' want: %q\n got: %q\n' "${want}" "${got}" >&2
return 1
fi
}
echo >&2 "=== Running tests ==="
check "spaces are decoded" '/tmp/dir\040with\040spaces' "/tmp/dir with spaces"
check "tab is decoded" '/a\011b' "/a${tab}b"
check "newline is decoded" 'a\012b' "$(printf 'a\nb')"
check "backslash is decoded" '/a\134b' '/a\b'
check "hash is decoded" '/a\043b' "/a#b"
check "a string without escapes is unchanged" "/mnt/nospace" "/mnt/nospace"
check "mixed escapes are decoded" '/a\040b\134c' '/a b\c'
echo >&2 "=== All tests passed ==="
exit 0
fi

sed -e 's/\\040/ /g; s/\\011/\t/g; s/\\012/\n/g; s/\\134/\\/g; s/\\043/#/g'
Loading