mounts: accept more mount options

Before we could only ask OSBuild to mount a device as readonly. But
devices can have more mount options than this. Supporting more options
is necessary for the new version of image-info that is using OSBuild's
internals in order to mount the image it wants to work on. Otherwise,
for instance, some umasks aren't applied properly and we can get
differences in rpm-verify results, thus corrupting the DB.

Mount is now accepting:
* readonly
* uid
* gid
* umask
* shortname
This commit is contained in:
Thomas Lavocat 2023-01-25 17:27:04 +01:00 committed by Thomas Lavocat
parent c0fb5cf90c
commit 8f08433804
3 changed files with 122 additions and 46 deletions

View file

@ -46,14 +46,11 @@ def make_dev_tmpfs(tmpdir):
subprocess.run(["umount", "--lazy", dev_path], check=True)
@pytest.mark.skipif(not TestBase.can_bind_mount(), reason="root only")
def test_mount_ro(tmpdir):
index = meta.Index(os.curdir)
def create_image(tmpdir):
# create a file to contain an image
tree = os.path.join(tmpdir, "tree")
os.makedirs(tree)
size = 1024 * 1024
size = 2 * 1024 * 1024
file = os.path.join(tree, "image.img")
with open(file, "wb") as f:
f.truncate(size)
@ -67,56 +64,106 @@ def test_mount_ro(tmpdir):
},
"options": {
"label": "TEST",
"uuid": "FEEDFACE-CAFE-4004-FEED-C0DEC0FFEE11"
"volid": "7B7795E7",
"fat-size": 32
}
}
with make_arguments(mkfsopts):
subprocess.run(
[os.path.join(os.curdir, "stages", "org.osbuild.mkfs.ext4")],
[os.path.join(os.curdir, "stages", "org.osbuild.mkfs.fat")],
check=True,
stdout=sys.stdout,
stderr=sys.stderr)
return tree, size
with tempfile.TemporaryDirectory() as mountpoint:
def mount(mgr, devpath, tree, size, mountpoint, options):
index = meta.Index(os.curdir)
# Device manager to open the loopback device
devmgr = devices.DeviceManager(mgr, devpath, tree)
# get a Device for the loopback
dev = devices.Device(
"loop",
index.get_module_info(
"Device",
"org.osbuild.loopback"
),
None,
{
"filename": "image.img",
"start": 0,
"size": size // 512 # size is in sectors / blocks
}
)
# open the device and get its loopback path
lpath = os.path.join(
devpath,
devmgr.open(dev)["path"]
)
# mount the loopback
mounts.MountManager(
devmgr,
mountpoint
).mount(
mounts.Mount(
lpath,
index.get_module_info("Mount", "org.osbuild.fat"),
dev,
"/",
options
)
)
@pytest.mark.skipif(not TestBase.can_bind_mount(), reason="root only")
def test_without_options(tmpdir):
tree, size = create_image(tmpdir)
options = {}
with tempfile.TemporaryDirectory(dir=tmpdir) as mountpoint:
with host.ServiceManager() as mgr:
with make_dev_tmpfs(tmpdir) as devpath:
# Device manager to open the loopback device
devmgr = devices.DeviceManager(mgr, devpath, tree)
# get a Device for the loopback
dev = devices.Device(
"loop",
index.get_module_info(
"Device",
"org.osbuild.loopback"
),
None,
{
"filename": "image.img",
"start": 0,
"size": size // 512 # size is in sectors / blocks
}
)
# open the device and get its loopback path
lpath = os.path.join(
devpath,
devmgr.open(dev)["path"]
)
# mount the loopback
mounts.MountManager(
devmgr,
mountpoint
).mount(
mounts.Mount(
lpath,
index.get_module_info("Mount", "org.osbuild.ext4"),
dev,
"/",
{"readonly": True}
)
)
mount(mgr, devpath, tree, size, mountpoint, options)
with open(os.path.join(mountpoint, "test"), "w", encoding="utf-8") as f:
f.write("should work")
os.remove(os.path.join(mountpoint, "test"))
# try to write in it, and failure to do so is a success
@pytest.mark.skipif(not TestBase.can_bind_mount(), reason="root only")
def test_all_options(tmpdir):
tree, size = create_image(tmpdir)
options = {
"readonly": True,
"uid": 0,
"gid": 0,
"umask": "077",
"shortname": "winnt"
}
print(options)
with tempfile.TemporaryDirectory(dir=tmpdir) as mountpoint:
with host.ServiceManager() as mgr:
with make_dev_tmpfs(tmpdir) as devpath:
mount(mgr, devpath, tree, size, mountpoint, options)
# Check FS is read only
with pytest.raises(OSError) as err:
with open(os.path.join(mountpoint, "test"), "w", encoding="utf-8") as f:
f.write("should not work")
assert err.value.errno == errno.EROFS # Check that FS is read only
assert err.value.errno == errno.EROFS
# Check the other options
st = os.lstat(mountpoint)
assert st.st_mode == 0o040700
assert st.st_uid == 0
assert st.st_gid == 0
shortname_tested = False
proc = subprocess.run("mount", capture_output=True, check=True)
for line in proc.stdout.splitlines():
strline = line.decode("utf-8")
if mountpoint in strline:
assert "winnt" in strline
shortname_tested = True
assert shortname_tested