Commit graph

28 commits

Author SHA1 Message Date
Dusty Mabe
f4b899873b osbuild/remoteloop: add more loop device options
This adds lock, partscan, read_only, sector_size to _create_device()
similar to make_loop() from devices/org.osbuild.loopback.
2024-12-04 16:30:55 +01:00
Michael Vogt
c990c07f79 osbuild: reuse loop_for_fd() in LoopServer
The LoopServer._create_device() duplicates the code from
`Loop.loop_for_fd()` right now. Given that this is relatively
subtle code it's better to not duplicate it and instead just
reuse the loop_for_fd() implementation in LoopServer.
2024-03-11 10:56:49 +01:00
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
Simon de Vlieger
ea6085fae6 osbuild: run isort on all files 2022-09-12 13:32:51 +02:00
Christian Kellner
099cfbcea1 remoteloop: close loop device controller
When cleaning up the `RemoteLoop` API instance, close the loop
controller in addition to closing all the open loop devices.
2021-08-11 20:59:51 +02:00
Christian Kellner
8388a64ffa api: remove 'addr' param from message dispatcher
Now that jsoncomm is using a connection oriented protocol, the
`addr` parameter is not needed[*] and can thus be removed from
the `BaseAPI._message` message dispatcher. Adapt all usages
of it, including the tests.

[*] sendmsg ignores the destination parameter for connection
oriented sockets.
2020-07-29 02:16:20 +01:00
Christian Kellner
630f73ba01 api: close file descriptor set in _dispatch
Make sure file descriptors are never leaked by closing them after
the `_message` method invocation. Clients that want to hold on to
fds past the scope of the method should use `FdSet.steal` to
extract those.
Adapt the `LoopServer`'s `_message` implementation accordingly.
2020-07-27 12:50:38 +01:00
Christian Kellner
93c1be4c5f remoteloop: use high level message dispatcher
Use the new `BaseAPI._message` high level message dispatcher that
is more convenient to use.
2020-07-27 12:50:38 +01:00
Christian Kellner
0c7284572e osbuild: auto-generate socket addresses for APIs
Rely on the ability of `BaseAPI` to auto-generate socket addresses
when no one was provided. The `BuildRoot` does not rely on the
sockets being created in the `BuildRoot.api` directory anymore and
will instead bind-mount each individual socket address to the well
known location via the `BaseAPI.endpoint` identifier.
Convert all API providers to take the `socket_address` as an
optional keyword argument.
2020-07-27 12:50:38 +01:00
Christian Kellner
bc81e68727 api: each API defines its 'endpoint' name
Add a new abstract class property to `BaseAPI` called `endpoint`,
meant to be implemented by deriving classes in order to identify
the end point name for the API provider.
Implement the new property in all existing API providers.
2020-07-27 12:50:38 +01:00
Christian Kellner
9edc2b0362 remoteloop: port LoopServer to use BaseAPI
Now that `api.BaseAPI` provides the basic scaffolding for API
servers, use that base class and remove the code duplication.
2020-07-27 12:50:38 +01:00
Christian Kellner
e9c4075bb3 remoteloop: properly close the event loop
Close the event loop when the context is exited, which will clear
the internal queues and shut down the executor of the event loop.
Not doing this will create a warning when the object is garbage
collected.
2020-07-21 13:25:04 +02:00
David Rheinsberg
4ad4da4658 osbuild: convert to jsoncomm
Convert the hard-coded DGRAM communication to util.jsoncomm. This
avoids hard-coding any IPC-details and simplifies the callers quite a
bit.
2020-04-21 13:47:38 +02:00
Tom Gundersen
16dfd7eec1 remoteloop: drop O_DIRECT
Appart from giving us a hard time on s390x, this feature did not seem
to have a measurable effect. Moreover, O_DIRECT is not supported by
tmpfs so without this patch we could not use tmpfs as backing store,
which does speed up image generation considerably.

Drop the flag and and rather put the store on tmpfs in order to speed
things up.
2020-02-06 19:01:12 +01:00
Tom Gundersen
794ec97bf3 api: add barriers
Ensure that the api sockets are created before entering the with clause.

Signed-off-by: Tom Gundersen <teg@jklm.no>
2020-02-06 19:01:12 +01:00
Lars Karlitski
7bb06d2334 loop: handle set_status returning EBUSY
This happens rarely when the same loop device is used in rapid
succession. The kernel flushes the page cache asynchronously, which
means that it might not be cleared yet when a new file is bound.
`set_status` checks if the cache is clear (`set_fd` doesn't).

Handle this by trying a different device when `set_status` returns
`EBUSY`.

Fixes #177
2020-01-19 22:19:25 +01:00
Lars Karlitski
b487126bb8 loop: explicitly close fds to loop devices
Don't wait until python's garbage collector closes the file descriptors
to loop devices. Close them when the `LoopServer` context manager exits,
after an assembler has finished running.
2020-01-19 22:19:25 +01:00
Christian Kellner
bf41326ac6 remoteloop: don't use O_DIRECT on s390x
Using O_DIRECT to open the image partition and then using that fd
for the backing of the loopback device will break the mounting of
the formatted partition, i.e mount will fail with:

  mount: /tmp/looptest-6qrtkp5e/mountpoint-root: wrong fs type,
  bad option, bad superblock on /dev/loop0, missing codepage or
  helper program, or other error.

Reproducible with the follow small-ish python script, executed via
'env PYTHONPATH=$(pwd) python3 looptest.py':

---- 8< ---- 8< ---- [ looptest.py ] ---- 8< ---- 8< ----

import contextlib
import json
import os
import subprocess
import stat
import tempfile

from osbuild import loop

@contextlib.contextmanager
def mount(source, dest):
    subprocess.run(["mount", source, dest], check=True)
    try:
        yield dest
    finally:
        subprocess.run(["umount", "-R", dest], check=True)

@contextlib.contextmanager
def os_open(path, flags):
    fd = os.open(path, flags)
    try:
        yield fd
    finally:
        os.close(fd)

def main():
    size = 512 * 1024 * 1024
    ptuuid = "0x14fc63d2"

    with contextlib.ExitStack() as cm:
        tmpdir = cm.enter_context(tempfile.TemporaryDirectory(prefix="looptest-"))
        print(f"Temporary directory at {tmpdir}")

        devdir = os.path.join(tmpdir, "dev")
        os.makedirs(devdir, exist_ok=True)
        dir_fd = cm.enter_context(os_open(devdir, os.O_DIRECTORY))

        image = os.path.join(tmpdir, "image")
        subprocess.run(["truncate", "--size", str(size), image], check=True)
        table = f"label: mbr\nlabel-id: {ptuuid}\nbootable, type=83"
        subprocess.run(["sfdisk", image], input=table, encoding='utf-8',
                       check=True)
        # read it back
        r = subprocess.run(["sfdisk", "--json", image],
                           stdout=subprocess.PIPE,
                           encoding='utf-8', check=True)
        table = json.loads(r.stdout)["partitiontable"]
        partitions = table["partitions"]
        start = partitions[0]["start"] * 512
        size = partitions[0]["size"] * 512

        # fails here with os.O_DIRECT
        image_fd = cm.enter_context(os_open(image, os.O_RDWR | os.O_DIRECT))

        control = loop.LoopControl()
        minor = control.get_unbound()
        lo = loop.Loop(minor)
        lo.set_fd(image_fd)
        lo.set_status(offset=start, sizelimit=size, autoclear=True)
        lo.mknod(dir_fd)
        loopdev = f"/dev/loop{minor}"

        # loopdev = os.path.join(devdir, lo.devname)
        # os.chmod(loopdev, os.stat(loopdev).st_mode | stat.S_IRGRP)

        subprocess.run(["ls", "-la", f"{devdir}"], check=True)
        subprocess.run(["mkfs.ext4", loopdev],
                       input="y", encoding='utf-8', check=True)

        subprocess.run(["blkid", loopdev], check=True)

        mountpoint = os.path.join(tmpdir, "mountpoint-root")
        os.makedirs(mountpoint, exist_ok=True)
        cm.enter_context(mount(loopdev, mountpoint))
        subprocess.run(["ls", "-la", tmpdir], check=True)
        subprocess.run(["ls", "-la", mountpoint], check=True)
        subprocess.run(["mount"], check=True)

if __name__ == '__main__':
    main()
2020-01-13 20:05:10 +01:00
Lars Karlitski
b9b2f99123 osbuild: create API sockets in the thread they're used in
This might (hopefully) fix a race in destructing the asyncio.EventLoop
that's used in all API classes, which leads to warnings about unhandled
exceptions on CI.

This also puts their creation closer to where the client-side sockets
are created.
2019-12-25 17:48:26 +01:00
Lars Karlitski
e0bb65dd71 api and remoteloop: don't close the passed-in socket
The socket that the osbuild and loop apis should talk on are passed into
their `__init__` function. The caller should be responsible for closing
those sockets.

This already happens in all current callers.

This fixes a non-fatal error on RHEL's python 3.6, because it was
calling `socket.close` on an already-closed socket:

    Traceback (most recent call last):
      File "/usr/lib64/python3.6/asyncio/base_events.py", line 529, in __del__
        self.close()
      File "/usr/lib64/python3.6/asyncio/unix_events.py", line 63, in close
        super().close()
      File "/usr/lib64/python3.6/asyncio/selector_events.py", line 99, in close
        self._close_self_pipe()
      File "/usr/lib64/python3.6/asyncio/selector_events.py", line 109, in _close_self_pipe
        self._remove_reader(self._ssock.fileno())
      File "/usr/lib64/python3.6/asyncio/selector_events.py", line 268, in _remove_reader
        key = self._selector.get_key(fd)
      File "/usr/lib64/python3.6/selectors.py", line 189, in get_key
        return mapping[fileobj]
      File "/usr/lib64/python3.6/selectors.py", line 70, in __getitem__
        fd = self._selector._fileobj_lookup(fileobj)
      File "/usr/lib64/python3.6/selectors.py", line 224, in _fileobj_lookup
        return _fileobj_to_fd(fileobj)
      File "/usr/lib64/python3.6/selectors.py", line 41, in _fileobj_to_fd
        raise ValueError("Invalid file descriptor: {}".format(fd))
    ValueError: Invalid file descriptor: -1
2019-12-15 12:05:14 +01:00
Christian Kellner
76518db26b dump_fds: add flags and address parameter
Expose the flags, address parameter of the underlying sock.sendmsg
method, in order to be able to explicitly specify the recipient of
the message; as needed in connection-less mode.
2019-10-30 18:44:55 +01:00
Christian Kellner
1c5b97afbc load_fds: use frombytes instead of fromstring
Python 3.2 renamed array.fromstring to array.frombytes, but kept
the former as an, now deprecated, alias. Use the canonical form
which indeed better describes what is going on.
2019-10-30 18:44:55 +01:00
Lars Karlitski
cb2f383601 remoteloop: make LoopClient.device a context manager 2019-10-07 10:10:51 +02:00
Lars Karlitski
0dd60b3abf remoteloop: pass filename to create_device
This makes LoopClient simpler to use in the common case.
2019-10-07 10:10:51 +02:00
Lars Karlitski
356f62058f remoteloop: remove dir_fd argument in create_device
If dir_fd wasn't passed, create_device() openend it to `/dev` and forgot
about closing it. To fix this, it would have to gain logic to only close
the fd if it wasn't passed in.

Side-step the problem by removing dir_fd, since nothing is using it
right now. We can add it back if something needs it.
2019-10-07 10:10:51 +02:00
Lars Karlitski
3d3ffda5d8 remoteloop: don't close a socket it didn't open
Closing the socket is the responsibility of whoever opened it.

Fix this in the only user (qemu assembler) by using socket() in a `with`
block, which closes the socket on exit.
2019-10-07 10:10:51 +02:00
Tom Gundersen
679b79c5e5 osbuild: split package into separate files
Import modules between files using the syntax `from . import foobar`,
renaming what used to be `FooBar` to `foobar.FooBar` when moved to a
separate file.

In __init__.py only import what is meant to be public API.

Signed-off-by: Tom Gundersen <teg@jklm.no>
2019-08-21 09:56:50 +04:00
Tom Gundersen
c124ab264b loop: add helpers to use IPC to create loop devices
loop.py is a simple wrapper around the kernel loop API. remoteloop.py
uses this to create a server/clinet pair that communicates over an
AF_UNIX/SOCK_DGRAM socket to allow the server to create loop devices
for the client.

The client passes a fd that should be bound to the resulting loop
device, and a dir-fd where the loop device node should be created.
The server returns the name of the device node to the client.

The idea is that the client is run from whithin a container without
access to devtmpfs (and hence /dev/loop-control), and the server
runs on the host. The client would typically pass its (fake) /dev
as the output directory.

For the client this will be similar to `losetup -f foo.img --show`.

[@larskarlitski: pylint: ignore the new LoopInfo class, because it
only has dynamic attributes. Also disable attribute-defined-outside-init,
which (among other problems) is not ignored for that class.]

Signed-off-by: Tom Gundersen <teg@jklm.no>
2019-07-19 00:51:12 +02:00