Skip to content

Commit d9943ee

Browse files
committed
fix(image-factory): say which device is missing, and check both
Follows tool.sh giving the factory the host's /dev. These are for a factory started by hand without it, and for the case that survives either way: the module not being loaded on the host at all. Three failures instead of one message. No /dev/loop-control means the loop module is not loaded, and loading it inside here would not help since this /dev is not the host's. No node for the loop device the kernel handed out means a copied /dev, which now says so - and names the first-build-only symptom, because "it worked the second time" reads like a ghost otherwise. No /dev/mapper/control is the same cause one step later, and was worth catching before kpartx does. mount_image.sh had no check at all: a failed losetup left LOOPDEV empty and the error surfaced as 'kpartx -av ""'. Signed-off-by: Simon Walz <contact@simonwalz.de>
1 parent 4e1bd4b commit d9943ee

2 files changed

Lines changed: 60 additions & 16 deletions

File tree

‎image-factory/create_image.sh‎

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,16 @@ sync
165165

166166
# Setup loop device
167167
#
168-
# The kernel hands out a free loop number through /dev/loop-control, but the
169-
# matching /dev/loopN node only exists if something creates it - and in a
170-
# container there is no udev to do that. losetup then fails with ENOENT and
171-
# reports the device node as "lost". Creating the node is enough to get past
172-
# that, provided the container may use block devices at all.
168+
# This container's /dev is a snapshot of the host's, taken when the container
169+
# started - 'docker run --privileged' enumerates the host's devices once and
170+
# creates them in here. A device the kernel makes afterwards therefore exists on
171+
# the host and not in this container, and asking for a free loop device is
172+
# exactly that: /dev/loop-control hands out a number, and the node for it never
173+
# appears. losetup then fails with ENOENT and calls the node "lost".
174+
#
175+
# It bites on the first build of a machine, where no loop device existed when
176+
# the container started, and stops biting on the second, where one did.
177+
# Creating the node ourselves is enough wherever we are allowed to.
173178
attach_loop_device() {
174179
local dev
175180
dev="$(losetup --find --show "${IMAGE}" 2>/dev/null)" || dev=""
@@ -197,31 +202,60 @@ for _attempt in 1 2 3; do
197202
done
198203

199204
if [[ -z "${LOOPDEV}" ]]; then
205+
if [[ ! -e /dev/loop-control ]]; then
206+
raise "There is no /dev/loop-control, so no loop device can be asked for.
207+
208+
The loop module is not loaded on the build host, or it was not loaded
209+
when this container started. On the host:
210+
211+
sudo modprobe loop
212+
213+
then build again. Loading it inside the container does not help: this
214+
container's /dev was taken from the host at start and does not follow it."
215+
fi
216+
200217
raise "Could not attach ${IMAGE} to a loop device.
201218
202-
Building a disk image needs loop devices, device-mapper and the right to
203-
mount filesystems. All three belong to the host kernel, and an LXC
204-
container has none of them by default - a 'device node /dev/loopN is
205-
lost' above means the kernel did hand out a loop device, but this
206-
container has no node for it.
219+
The kernel did hand out a loop device - 'device node /dev/loopN is lost'
220+
above says so - but this container has no node for it, and creating one
221+
was not permitted either.
222+
223+
On a normal host or VM this only happens on the very first build, before
224+
any loop device exists. Load the module on the host and build again:
207225
208-
Build on a host or in a VM instead. If it has to be this container, it
209-
must be privileged and unconfined, and its config needs at least:
226+
sudo modprobe loop dm_mod
227+
228+
Inside an LXC container it happens every time, because loop devices,
229+
device-mapper and mounting all belong to the host kernel. Build on a host
230+
or in a VM instead; if it has to be that container, it must be privileged
231+
and unconfined, with at least:
210232
211233
lxc.apparmor.profile: unconfined
212234
lxc.cgroup2.devices.allow: b 7:* rwm
213235
lxc.cgroup2.devices.allow: c 10:237 rwm
214236
lxc.mount.entry: /dev/loop-control dev/loop-control none bind,create=file
215237
lxc.mount.entry: /dev/loop0 dev/loop0 none bind,create=file
216238
217-
plus one bind entry per loop device you want available. Note that
218-
device-mapper is the next thing this script needs, and it has the same
219-
kind of problem.
239+
plus one bind entry per loop device wanted.
220240
221241
Building for '--platform lxc' needs none of this: it exports a container
222242
rather than partitioning a disk."
223243
fi
224244

245+
# Device-mapper is the next thing needed, and it is the same story: kpartx maps
246+
# the image's partitions through it, and /dev/mapper/control has to be here.
247+
if [[ ! -e /dev/mapper/control ]]; then
248+
raise "There is no /dev/mapper/control, so the partitions of ${IMAGE} cannot
249+
be mapped.
250+
251+
Same cause as a missing loop device: this container's /dev was taken from
252+
the host when it started. On the host:
253+
254+
sudo modprobe dm_mod
255+
256+
then build again."
257+
fi
258+
225259
partprobe "${LOOPDEV}"
226260

227261
# Map partitions

‎image-factory/mount_image.sh‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,17 @@ if [[ ! -d "${OUTPUT_DIR}" ]]; then
3131
fi
3232

3333
# Setup loop device
34-
LOOPDEV="$(losetup --find --show $IMAGE)"
34+
if ! LOOPDEV="$(losetup --find --show "${IMAGE}")" || [[ -z "${LOOPDEV}" ]]; then
35+
raise "Could not attach ${IMAGE} to a loop device.
36+
37+
This needs the host's /dev, not the copy of it a privileged container
38+
gets at start: the node for a loop device the kernel hands out now does
39+
not exist in that copy. tool.sh mounts it; a factory started by hand
40+
needs '-v /dev:/dev' too.
41+
42+
If /dev is mounted and this still fails, the loop module is not loaded on
43+
the host: 'sudo modprobe loop dm_mod'."
44+
fi
3545
partprobe "${LOOPDEV}"
3646

3747
# Map partitions

0 commit comments

Comments
 (0)