debian-forge/osbuild/util/linux.py
Christian Kellner 0f670829a3 util/linux: fix BLK_IOC_FLSBUF on ppc64le
ioctl contants are platform dependent. It should be the same on
x86, aarch64 and s390x but it is indeed different on ppc64le.
This lead to the call to `ioctl_blockdev_flushbuf` actually
raising an exception of `OSError: [Errno 22] Invalid argument`.

The constant was calculated with a little python snippet that
in theory could also go directly into the code, but for now
the simpler condition in this patch is enough.

The snippet is a port of the defines from the Linux kernel,
specifically /usr/include/asm-generic/ioctl.h.

    class IOConstants:
        """IO Commands for Linux"""
        if platform.machine() == "ppc64le":
            NRBITS = 8
            TYPEBITS = 8
            SIZEBITS = 13

            DIR_NONE = 1
        else:
            NRBITS = 8
            TYPEBITS = 8
            SIZEBITS = 14

            DIR_NONE = 0

        NRSHIFT = 0
        TYPESHIFT = NRSHIFT+NRBITS
        SIZESHIFT = TYPESHIFT+TYPEBITS
        DIRSHIFT = SIZESHIFT+SIZEBITS

        @classmethod
        def make(cls, directory, iotype, nr, size):
            return ((directory << cls.DIRSHIFT) |
                    (iotype << cls.TYPESHIFT) |
                    (nr << cls.NRSHIFT) |
                    (size << cls.SIZESHIFT))

        @classmethod
        def make_dir_none(cls, iotype, nr):
            return cls.make(cls.DIR_NONE, iotype, nr, 0)

This is used to get the value for `BLKFLSBUF` taken from the
include `/usr/include/linux/fs.h`:

    #define BLKFLSBUF  _IO(0x12,97)	/* flush buffer cache */

The value is then obtained via:

    print("0x%x" % IOConstants.make_dir_none(0x12,97))
    0x20001261
2022-02-01 16:28:56 +01:00

123 lines
3 KiB
Python

"""Linux API Access
This module provides access to linux system-calls and other APIs, in particular
those not provided by the python standard library. The idea is to provide
universal wrappers with broad access to linux APIs. Convenience helpers and
higher-level abstractions are beyond the scope of this module.
In some cases it is overly complex to provide universal access to a specific
API. Hence, the API might be restricted to a reduced subset of its
functionality, just to make sure we can actually implement the wrappers in a
reasonable manner.
"""
import array
import fcntl
import platform
__all__ = [
"ioctl_get_immutable",
"ioctl_toggle_immutable",
]
# NOTE: These are wrong on at least ALPHA and SPARC. They use different
# ioctl number setups. We should fix this, but this is really awkward
# in standard python.
# Our tests will catch this, so we will not accidentally run into this
# on those architectures.
FS_IOC_GETFLAGS = 0x80086601
FS_IOC_SETFLAGS = 0x40086602
FS_IMMUTABLE_FL = 0x00000010
if platform.machine() == "ppc64le":
BLK_IOC_FLSBUF = 0x20001261
else:
BLK_IOC_FLSBUF = 0x00001261
def ioctl_get_immutable(fd: int):
"""Query FS_IMMUTABLE_FL
This queries the `FS_IMMUTABLE_FL` flag on a specified file.
Arguments
---------
fd
File-descriptor to operate on.
Returns
-------
bool
Whether the `FS_IMMUTABLE_FL` flag is set or not.
Raises
------
OSError
If the underlying ioctl fails, a matching `OSError` will be raised.
"""
if not isinstance(fd, int) or fd < 0:
raise ValueError()
flags = array.array('L', [0])
fcntl.ioctl(fd, FS_IOC_GETFLAGS, flags, True)
return bool(flags[0] & FS_IMMUTABLE_FL)
def ioctl_toggle_immutable(fd: int, set_to: bool):
"""Toggle FS_IMMUTABLE_FL
This toggles the `FS_IMMUTABLE_FL` flag on a specified file. It can both set
and clear the flag.
Arguments
---------
fd
File-descriptor to operate on.
set_to
Whether to set the `FS_IMMUTABLE_FL` flag or not.
Raises
------
OSError
If the underlying ioctl fails, a matching `OSError` will be raised.
"""
if not isinstance(fd, int) or fd < 0:
raise ValueError()
flags = array.array('L', [0])
fcntl.ioctl(fd, FS_IOC_GETFLAGS, flags, True)
if set_to:
flags[0] |= FS_IMMUTABLE_FL
else:
flags[0] &= ~FS_IMMUTABLE_FL
fcntl.ioctl(fd, FS_IOC_SETFLAGS, flags, False)
def ioctl_blockdev_flushbuf(fd: int):
"""Flush the block device buffer cache
NB: This function needs the `CAP_SYS_ADMIN` capability.
Arguments
---------
fd
File-descriptor of a block device to operate on.
Raises
------
OSError
If the underlying ioctl fails, a matching `OSError`
will be raised.
"""
if not isinstance(fd, int) or fd < 0:
raise ValueError(f"Invalid file descriptor: '{fd}'")
fcntl.ioctl(fd, BLK_IOC_FLSBUF, 0)