CLONE_NEWUSER = 0x10000000
EPERM = 1
ENOENT = 2
+ENOSYS = 38
F_GETFD = 1
F_SETFD = 2
FD_CLOEXEC = 1
libc.fcntl.argtypes = (ctypes.c_int, ctypes.c_int, ctypes.c_int)
-def oserror(filename: str = "") -> None:
+ENOSYS_MSG = """
+mkosi was unable to invoke the {syscall}() system call.
+
+This probably means either the system call is not implemented by the running kernel version ({kver}) or the
+system call is prohibited via seccomp if mkosi is being executed inside a containerized environment.
+"""
+
+
+def oserror(syscall: str, filename: str = "") -> None:
+ if ctypes.get_errno() == ENOSYS:
+ print(ENOSYS_MSG.format(syscall=syscall, kver=os.uname().version), file=sys.stderr)
+
raise OSError(ctypes.get_errno(), os.strerror(ctypes.get_errno()), filename or None)
def unshare(flags: int) -> None:
if libc.unshare(flags) < 0:
- oserror()
+ oserror("unshare")
def statfs(path: str) -> int:
buffer = (ctypes.c_long * 15)()
if libc.statfs(path.encode(), ctypes.byref(buffer)) < 0:
- oserror(path)
+ oserror("statfs", path)
return int(buffer[0])
typeb = type.encode() if type else None
optionsb = options.encode() if options else None
if libc.mount(srcb, dst.encode(), typeb, flags, optionsb) < 0:
- oserror(dst)
+ oserror("mount", dst)
def umount2(path: str, flags: int = 0) -> None:
if libc.umount2(path.encode(), flags) < 0:
- oserror(path)
+ oserror("umount2", path)
def cap_permitted_to_ambient() -> None:
payload = (cap_user_data_t * LINUX_CAPABILITY_U32S_3)()
if libc.capget(ctypes.addressof(header), ctypes.byref(payload)) < 0:
- oserror()
+ oserror("capget")
payload[0].inheritable = payload[0].permitted
payload[1].inheritable = payload[1].permitted
if libc.capset(ctypes.addressof(header), ctypes.byref(payload)) < 0:
- oserror()
+ oserror("capset")
effective = payload[1].effective << 32 | payload[0].effective
break
if effective & (1 << cap) and libc.prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0) < 0:
- oserror()
+ oserror("prctl")
def have_effective_cap(capability: int) -> bool:
keyring = libkeyutils.keyctl_join_session_keyring(None)
if keyring == -1:
- oserror()
+ oserror("keyctl")
def mount_rbind(src: str, dst: str, attrs: int = 0) -> None:
fd = libc.syscall(NR_open_tree, AT_FDCWD, src.encode(), flags)
if fd < 0:
- oserror(src)
+ oserror("open_tree", src)
try:
attr = mount_attr()
r = libc.syscall(NR_mount_setattr, fd, b"", flags, ctypes.addressof(attr), MOUNT_ATTR_SIZE_VER0)
if r < 0:
- oserror(src)
+ oserror("mount_setattr", src)
try:
libc.move_mount.argtypes = (
r = libc.syscall(NR_move_mount, fd, b"", AT_FDCWD, dst.encode(), MOVE_MOUNT_F_EMPTY_PATH)
if r < 0:
- oserror(dst)
+ oserror("move_mount", dst)
finally:
os.close(fd)
event = libc.eventfd(0, 0)
if event < 0:
- oserror()
+ oserror("eventfd")
pid = os.fork()
if pid == 0:
# We're guaranteed to have / be a mount when we get here, so pivot_root() won't fail anymore,
# even if we're in the initramfs.
if libc.pivot_root(b".", b".") < 0:
- oserror()
+ oserror("pivot_root")
# As documented in the pivot_root() man page, this will unmount the old rootfs.
umount2(".", MNT_DETACH)