Example #1
0
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.set_fd(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") as f:
            assert not lo.is_bound_to(f.fileno())
Example #2
0
 def _create_device(self, fd, dir_fd, offset=None, sizelimit=None):
     while True:
         # Getting an unbound loopback device and attaching a backing
         # file descriptor to it is racy, so we must use a retry loop
         lo = loop.Loop(self.ctl.get_unbound())
         try:
             lo.set_fd(fd)
         except OSError as e:
             if e.errno == errno.EBUSY:
                 continue
             raise e
         break
     lo.set_status(offset=offset, sizelimit=sizelimit, autoclear=True)
     lo.mknod(dir_fd)
     # Pin the Loop objects so they are only released when the LoopServer
     # is destroyed.
     self.devs.append(lo)
     return lo.devname
Example #3
0
def loop_create_device(ctl, fd, offset=None, sizelimit=None):
    while True:
        lo = loop.Loop(ctl.get_unbound())
        try:
            lo.set_fd(fd)
        except OSError as e:
            lo.close()
            if e.errno == errno.EBUSY:
                continue
            raise e
        try:
            lo.set_status(offset=offset, sizelimit=sizelimit, autoclear=True)
        except BlockingIOError:
            lo.clear_fd()
            lo.close()
            continue
        break
    try:
        yield lo
    finally:
        lo.close()
Example #4
0
def test_lock(tempdir):

    path = os.path.join(tempdir, "test.img")
    ctl = loop.LoopControl()

    assert ctl

    lo, lo2, f = None, None, None
    try:
        f = open(path, "wb+")
        f.truncate(1024)
        f.flush()
        lo = ctl.loop_for_fd(f.fileno(), autoclear=True, lock=True)
        assert lo

        lo2 = loop.Loop(lo.minor)
        assert lo2

        with pytest.raises(BlockingIOError):
            lo2.flock(fcntl.LOCK_EX | fcntl.LOCK_NB)

        lo.close()
        lo = None

        # after lo is closed, the lock should be release and
        # we should be able to obtain the lock
        lo2.flock(fcntl.LOCK_EX | fcntl.LOCK_NB)
        lo2.clear_fd()

    finally:
        if lo2:
            lo2.close()
        if lo:
            lo.close()
        if f:
            f.close()

        ctl.close()
Example #5
0
def test_clear_fd_wait(tempdir):

    path = os.path.join(tempdir, "test.img")
    ctl = loop.LoopControl()

    assert ctl

    delay_time = 0.25

    def close_loop(lo, barrier):
        barrier.wait()
        time.sleep(delay_time)
        print("closing loop")
        lo.close()

    lo, lo2, f = None, None, None
    try:
        f = open(path, "wb+")
        f.truncate(1024)
        f.flush()
        lo = ctl.loop_for_fd(f.fileno(), autoclear=False)
        assert lo

        # Increase reference count of the loop to > 1 thus
        # preventing the kernel from immediately closing the
        # device. Instead the kernel will set the autoclear
        # attribute and return
        lo2 = loop.Loop(lo.minor)
        assert lo2

        # as long as the second loop is alive, the kernel can
        # not clear the fd and thus we will get a timeout
        with pytest.raises(TimeoutError):
            lo.clear_fd_wait(f.fileno(), 0.1, 0.01)

        # start a thread and sync with a barrier, then close
        # the loop device in the background thread while the
        # main thread is waiting in `clear_fd_wait`. We wait
        # four times the delay time of the thread to ensure
        # we don't get a timeout.
        barrier = threading.Barrier(2)
        thread = threading.Thread(target=close_loop, args=(lo2, barrier))
        barrier.reset()
        thread.start()
        barrier.wait()

        lo.clear_fd_wait(f.fileno(), 4 * delay_time, delay_time / 10)

        # no timeout exception has occurred and thus the device
        # must not be be bound to the original file anymore
        assert not lo.is_bound_to(f.fileno())

    finally:
        if lo2:
            lo2.close()
        if lo:
            with contextlib.suppress(OSError):
                lo.clear_fd()
            lo.close()
        if f:
            f.close()

        ctl.close()