debian-forge/test/run/test_devices.py
Thomas Lavocat da11ef4eb0 loop: use LOOP_CONFIGURE instead of LOOP_SET_FD
LOOP_CONFIGURE allows to atomically configure the decive when opening
it. This avoid the possibility of a race condition where between set_fd
and set_status some operations are already accepted by the loopback
device. See https://lwn.net/Articles/820408/

This feature was included in the linux kernel 5.8 however it is safe to
not include any kind of fallback to the previous method as @obudai
points out that:

LOOP_CONFIGURE was backported into RHEL 8 kernel in RHEL 8.4 as a part
of https://bugzilla.redhat.com/show_bug.cgi?id=1881760 (block layer:
update to upstream v5.8).

Since RHEL 8.4 is currently the oldest supported release that we support
running osbuild on, it might be just fine implementing this without the
fallback.

From a centos stream 8 container:
kernel-4.18.0-448.el8.x86_64
- loop: Fix missing discard support when using LOOP_CONFIGURE (Ming Lei) [1997338]
- [block] loop: Set correct device size when using LOOP_CONFIGURE (Ming Lei) [1881760]
- [block] loop: unset GENHD_FL_NO_PART_SCAN on LOOP_CONFIGURE (Ming Lei) [1881760]
- [block] loop: Add LOOP_CONFIGURE ioctl (Ming Lei) [1881760]
2023-05-05 15:42:47 +02:00

81 lines
1.9 KiB
Python
Executable file

#!/usr/bin/python3
#
# Runtime Tests for Device Host Services
#
import os
import tempfile
import pytest
from osbuild import devices, host, loop, meta
from ..test import TestBase
@pytest.fixture(name="tmpdir")
def tmpdir_fixture():
with tempfile.TemporaryDirectory(prefix="test-devices-") as tmp:
yield tmp
@pytest.mark.skipif(not TestBase.can_bind_mount(), reason="root only")
def test_loopback_basic(tmpdir):
index = meta.Index(os.curdir)
info = index.get_module_info("Device", "org.osbuild.loopback")
tree = os.path.join(tmpdir, "tree")
os.makedirs(tree)
devpath = os.path.join(tmpdir, "dev")
os.makedirs(devpath)
size = 1024 * 1024
filename = os.path.join(tree, "image.img")
with open(filename, "wb") as f:
f.truncate(size)
sb = os.fstat(f.fileno())
testfile = os.path.join(tmpdir, "test.img")
options = {
"filename": "image.img",
"start": 0,
"size": size // 512 # size is in sectors / blocks
}
dev = devices.Device("loop", info, None, options)
with host.ServiceManager() as mgr:
devmgr = devices.DeviceManager(mgr, devpath, tree)
reply = devmgr.open(dev)
assert reply
assert reply["path"]
node = reply["path"]
assert os.path.exists(os.path.join(devpath, node))
minor = reply["node"]["minor"]
lo = loop.Loop(minor)
li = lo.get_status()
assert li.lo_offset == 0
assert li.lo_sizelimit == size
assert li.lo_inode == sb.st_ino
with pytest.raises(OSError):
with open(testfile, "wb") as f:
f.truncate(1)
lo.configure(f.fileno())
lo.close()
uid = f"device/{dev.name}"
client = mgr.services[uid]
client.call("close", None)
lo = loop.Loop(minor)
with open(filename, "r", encoding="utf8") as f:
assert not lo.is_bound_to(f.fileno())