Merge pull request #175 from doraskayo/bwrap-bootstrap
Add a rootless bootstrap mode using bubblewrap
This commit is contained in:
commit
9bc2ca1726
|
@ -23,13 +23,15 @@ Get me started!
|
|||
installed.
|
||||
|
||||
a. Alternatively, run ``./rootfs.py --chroot`` to run it in a chroot.
|
||||
b. Alternatively, run ``./rootfs.py`` but don’t run the actual
|
||||
b. Alternatively, run ``./rootfs.py --bwrap`` to run it in a bubblewrap
|
||||
sandbox. When user namespaces are supported, this mode is rootless.
|
||||
c. Alternatively, run ``./rootfs.py`` but don’t run the actual
|
||||
virtualization and instead copy sysa/tmp/initramfs to a USB or
|
||||
some other device and boot from bare metal. NOTE: we now require
|
||||
a hard drive. This is currently hardcoded as sda. You also need
|
||||
to put ``sysc/tmp/disk.img`` onto your sda on the bootstrapping
|
||||
machine.
|
||||
c. Alternatively, do not use python at all, see "Python-less build"
|
||||
d. Alternatively, do not use python at all, see "Python-less build"
|
||||
below.
|
||||
|
||||
5. Wait.
|
||||
|
|
|
@ -3,11 +3,13 @@
|
|||
This file contains a few functions to be shared by all Sys* classes
|
||||
"""
|
||||
|
||||
# SPDX-FileCopyrightText: 2022 Dor Askayo <dor.askayo@gmail.com>
|
||||
# SPDX-FileCopyrightText: 2021-22 fosslinux <fosslinux@aussies.space>
|
||||
# SPDX-FileCopyrightText: 2021 Andrius Štikonas <andrius@stikonas.eu>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import hashlib
|
||||
import glob
|
||||
import subprocess
|
||||
|
@ -33,10 +35,20 @@ class SysGeneral:
|
|||
mounted_tmpfs = False
|
||||
|
||||
def __del__(self):
|
||||
if self.mounted_tmpfs and not self.preserve_tmp:
|
||||
if not self.preserve_tmp:
|
||||
self.remove_tmp()
|
||||
|
||||
def remove_tmp(self):
|
||||
"""Remove the tmp directory"""
|
||||
if self.tmp_dir is None:
|
||||
return
|
||||
|
||||
if self.mounted_tmpfs:
|
||||
print(f"Unmounting tmpfs from {self.tmp_dir}")
|
||||
umount(self.tmp_dir)
|
||||
os.rmdir(self.tmp_dir)
|
||||
|
||||
print(f"Removing {self.tmp_dir}")
|
||||
shutil.rmtree(self.tmp_dir, ignore_errors=True)
|
||||
|
||||
def mount_tmpfs(self):
|
||||
"""Mount the tmpfs for this sysx"""
|
||||
|
|
72
rootfs.py
72
rootfs.py
|
@ -7,6 +7,7 @@ you can run bootstap inside chroot.
|
|||
"""
|
||||
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
# SPDX-FileCopyrightText: 2022 Dor Askayo <dor.askayo@gmail.com>
|
||||
# SPDX-FileCopyrightText: 2021 Andrius Štikonas <andrius@stikonas.eu>
|
||||
# SPDX-FileCopyrightText: 2021 Bastian Bittorf <bb@npl.de>
|
||||
# SPDX-FileCopyrightText: 2021 Melg Eight <public.melg8@gmail.com>
|
||||
|
@ -30,7 +31,7 @@ def create_configuration_file(args):
|
|||
config_path = os.path.join('sysa', 'bootstrap.cfg')
|
||||
with open(config_path, "w", encoding="utf_8") as config:
|
||||
config.write("FORCE_TIMESTAMPS=" + str(args.force_timestamps) + "\n")
|
||||
config.write("CHROOT=" + str(args.chroot) + "\n")
|
||||
config.write("CHROOT=" + str(args.chroot or args.bwrap) + "\n")
|
||||
config.write("UPDATE_CHECKSUMS=" + str(args.update_checksums) + "\n")
|
||||
config.write("DISK=sda1\n")
|
||||
|
||||
|
@ -45,7 +46,9 @@ def main():
|
|||
default="x86")
|
||||
parser.add_argument("-c", "--chroot", help="Run inside chroot",
|
||||
action="store_true")
|
||||
parser.add_argument("-p", "--preserve", help="Do not unmount temporary dir",
|
||||
parser.add_argument("-bw", "--bwrap", help="Run inside a bwrap sandbox",
|
||||
action="store_true")
|
||||
parser.add_argument("-p", "--preserve", help="Do not remove temporary dir",
|
||||
action="store_true")
|
||||
parser.add_argument("-t", "--tmpdir", help="Temporary directory")
|
||||
parser.add_argument("--force-timestamps",
|
||||
|
@ -81,6 +84,8 @@ def main():
|
|||
count += 1
|
||||
if args.chroot:
|
||||
count += 1
|
||||
if args.bwrap:
|
||||
count += 1
|
||||
if args.minikernel:
|
||||
count += 1
|
||||
if args.bare_metal:
|
||||
|
@ -109,11 +114,10 @@ def main():
|
|||
pass
|
||||
|
||||
system_c = SysC(arch=args.arch, preserve_tmp=args.preserve,
|
||||
tmpdir=args.tmpdir, chroot=args.chroot)
|
||||
system_b = SysB(arch=args.arch, preserve_tmp=args.preserve,
|
||||
chroot=args.chroot)
|
||||
tmpdir=args.tmpdir)
|
||||
system_b = SysB(arch=args.arch, preserve_tmp=args.preserve)
|
||||
system_a = SysA(arch=args.arch, preserve_tmp=args.preserve,
|
||||
tmpdir=args.tmpdir, chroot=args.chroot,
|
||||
tmpdir=args.tmpdir,
|
||||
sysb_dir=system_b.sys_dir, sysc_tmp=system_c.tmp_dir)
|
||||
|
||||
bootstrap(args, system_a, system_b, system_c)
|
||||
|
@ -129,15 +133,59 @@ print(shutil.which('chroot'))
|
|||
chroot_binary = run('sudo', 'python3', '-c', find_chroot,
|
||||
capture_output=True).stdout.decode().strip()
|
||||
|
||||
system_c.prepare(mount_tmpfs=True,
|
||||
create_disk_image=False)
|
||||
system_a.prepare(mount_tmpfs=True,
|
||||
copy_sysc=True,
|
||||
create_initramfs=False)
|
||||
|
||||
# sysa
|
||||
arch = stage0_arch_map.get(args.arch, args.arch)
|
||||
init = os.path.join(os.sep, 'bootstrap-seeds', 'POSIX', arch, 'kaem-optional-seed')
|
||||
run('sudo', 'env', '-i', 'PATH=/bin', chroot_binary, system_a.tmp_dir, init)
|
||||
|
||||
elif args.bwrap:
|
||||
system_c.prepare(mount_tmpfs=False,
|
||||
create_disk_image=False)
|
||||
system_a.prepare(mount_tmpfs=False,
|
||||
copy_sysc=True,
|
||||
create_initramfs=False)
|
||||
|
||||
# sysa
|
||||
arch = stage0_arch_map.get(args.arch, args.arch)
|
||||
init = os.path.join(os.sep, 'bootstrap-seeds', 'POSIX', arch, 'kaem-optional-seed')
|
||||
run('bwrap', '--unshare-user',
|
||||
'--uid', '0',
|
||||
'--gid', '0',
|
||||
'--cap-add', 'CAP_SYS_CHROOT', # Required for chroot from sysa to sysc
|
||||
'--clearenv',
|
||||
'--setenv', 'PATH', '/usr/bin',
|
||||
'--bind', system_a.tmp_dir, '/',
|
||||
'--dir', '/dev',
|
||||
'--dev-bind', '/dev/null', '/dev/null',
|
||||
'--dev-bind', '/dev/zero', '/dev/zero',
|
||||
'--dev-bind', '/dev/random', '/dev/random',
|
||||
'--dev-bind', '/dev/urandom', '/dev/urandom',
|
||||
'--dir', '/sysc/dev',
|
||||
'--dev-bind', '/dev/null', '/sysc/dev/null',
|
||||
'--dev-bind', '/dev/zero', '/sysc/dev/zero',
|
||||
'--dev-bind', '/dev/random', '/sysc/dev/random',
|
||||
'--dev-bind', '/dev/urandom', '/sysc/dev/urandom',
|
||||
'--proc', '/sysc/proc',
|
||||
'--bind', '/sys', '/sysc/sys',
|
||||
'--tmpfs', '/sysc/tmp',
|
||||
init)
|
||||
|
||||
elif args.minikernel:
|
||||
if os.path.isdir('kritis-linux'):
|
||||
shutil.rmtree('kritis-linux')
|
||||
|
||||
system_c.prepare(mount_tmpfs=True,
|
||||
create_disk_image=True)
|
||||
system_a.prepare(mount_tmpfs=True,
|
||||
copy_sysc=False,
|
||||
create_initramfs=True)
|
||||
|
||||
run('git', 'clone',
|
||||
'--depth', '1', '--branch', 'v0.7',
|
||||
'https://github.com/bittorf/kritis-linux.git')
|
||||
|
@ -155,11 +203,23 @@ print(shutil.which('chroot'))
|
|||
'--log', '/tmp/bootstrap.log')
|
||||
|
||||
elif args.bare_metal:
|
||||
system_c.prepare(mount_tmpfs=True,
|
||||
create_disk_image=True)
|
||||
system_a.prepare(mount_tmpfs=True,
|
||||
copy_sysc=False,
|
||||
create_initramfs=True)
|
||||
|
||||
print("Please:")
|
||||
print(" 1. Take sysa/tmp/initramfs and your kernel, boot using this.")
|
||||
print(" 2. Take sysc/tmp/disk.img and put this on a writable storage medium.")
|
||||
|
||||
else:
|
||||
system_c.prepare(mount_tmpfs=True,
|
||||
create_disk_image=True)
|
||||
system_a.prepare(mount_tmpfs=True,
|
||||
copy_sysc=False,
|
||||
create_initramfs=True)
|
||||
|
||||
run(args.qemu_cmd,
|
||||
'-enable-kvm',
|
||||
'-m', str(args.qemu_ram) + 'M',
|
||||
|
|
24
sysa.py
24
sysa.py
|
@ -1,6 +1,7 @@
|
|||
#!/usr/bin/env python3
|
||||
"""System A"""
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
# SPDX-FileCopyrightText: 2022 Dor Askayo <dor.askayo@gmail.com>
|
||||
# SPDX-FileCopyrightText: 2021 Andrius Štikonas <andrius@stikonas.eu>
|
||||
# SPDX-FileCopyrightText: 2021 Melg Eight <public.melg8@gmail.com>
|
||||
# SPDX-FileCopyrightText: 2021-22 fosslinux <fosslinux@aussies.space>
|
||||
|
@ -17,7 +18,7 @@ class SysA(SysGeneral):
|
|||
Class responsible for preparing sources for System A.
|
||||
"""
|
||||
# pylint: disable=too-many-instance-attributes,too-many-arguments
|
||||
def __init__(self, arch, preserve_tmp, tmpdir, chroot, sysb_dir, sysc_tmp):
|
||||
def __init__(self, arch, preserve_tmp, tmpdir, sysb_dir, sysc_tmp):
|
||||
self.git_dir = os.path.dirname(os.path.join(__file__))
|
||||
self.arch = arch
|
||||
self.preserve_tmp = preserve_tmp
|
||||
|
@ -27,26 +28,22 @@ class SysA(SysGeneral):
|
|||
self.tmp_dir = os.path.join(self.git_dir, 'tmp')
|
||||
else:
|
||||
self.tmp_dir = os.path.join(tmpdir, 'sysa')
|
||||
os.mkdir(self.tmp_dir)
|
||||
self.sysa_dir = os.path.join(self.tmp_dir, 'sysa')
|
||||
self.base_dir = self.sysa_dir
|
||||
self.cache_dir = os.path.join(self.sys_dir, 'distfiles')
|
||||
self.sysb_dir = sysb_dir
|
||||
self.sysc_tmp = sysc_tmp
|
||||
self.chroot = chroot
|
||||
|
||||
self.prepare()
|
||||
|
||||
if not chroot:
|
||||
self.make_initramfs()
|
||||
|
||||
def prepare(self):
|
||||
def prepare(self, mount_tmpfs, copy_sysc, create_initramfs):
|
||||
"""
|
||||
Prepare directory structure for System A.
|
||||
We create an empty tmpfs, unpack stage0-posix.
|
||||
We create an empty tmp directory, unpack stage0-posix.
|
||||
Rest of the files are unpacked into more structured directory /sysa
|
||||
"""
|
||||
self.mount_tmpfs()
|
||||
if mount_tmpfs:
|
||||
self.mount_tmpfs()
|
||||
else:
|
||||
os.mkdir(self.tmp_dir)
|
||||
|
||||
self.stage0_posix()
|
||||
self.sysa()
|
||||
|
@ -54,9 +51,12 @@ class SysA(SysGeneral):
|
|||
# sysb must be added to sysa as it is another initramfs stage
|
||||
self.sysb()
|
||||
|
||||
if self.chroot:
|
||||
if copy_sysc:
|
||||
self.sysc()
|
||||
|
||||
if create_initramfs:
|
||||
self.make_initramfs()
|
||||
|
||||
def sysa(self):
|
||||
"""Copy in sysa files for sysa."""
|
||||
self.get_packages()
|
||||
|
|
3
sysb.py
3
sysb.py
|
@ -12,10 +12,9 @@ class SysB(SysGeneral):
|
|||
"""
|
||||
Class responsible for preparing sources for System B.
|
||||
"""
|
||||
def __init__(self, arch, preserve_tmp, chroot):
|
||||
def __init__(self, arch, preserve_tmp):
|
||||
self.git_dir = os.path.dirname(os.path.join(__file__))
|
||||
self.arch = arch
|
||||
self.preserve_tmp = preserve_tmp
|
||||
self.chroot = chroot
|
||||
|
||||
self.sys_dir = os.path.join(self.git_dir, 'sysb')
|
||||
|
|
49
sysc.py
49
sysc.py
|
@ -1,6 +1,7 @@
|
|||
#!/usr/bin/env python3
|
||||
"""System C"""
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
# SPDX-FileCopyrightText: 2022 Dor Askayo <dor.askayo@gmail.com>
|
||||
# SPDX-FileCopyrightText: 2021-22 fosslinux <fosslinux@aussies.space>
|
||||
# SPDX-FileCopyrightText: 2021 Andrius Štikonas <andrius@stikonas.eu>
|
||||
|
||||
|
@ -16,12 +17,14 @@ class SysC(SysGeneral):
|
|||
"""
|
||||
Class responsible for preparing sources for System C.
|
||||
"""
|
||||
|
||||
dev_name = None
|
||||
|
||||
# pylint: disable=too-many-instance-attributes
|
||||
def __init__(self, arch, preserve_tmp, tmpdir, chroot):
|
||||
def __init__(self, arch, preserve_tmp, tmpdir):
|
||||
self.git_dir = os.path.dirname(os.path.join(__file__))
|
||||
self.arch = arch
|
||||
self.preserve_tmp = preserve_tmp
|
||||
self.chroot = chroot
|
||||
|
||||
self.sys_dir = os.path.join(self.git_dir, 'sysc')
|
||||
self.cache_dir = os.path.join(self.sys_dir, 'distfiles')
|
||||
|
@ -29,44 +32,46 @@ class SysC(SysGeneral):
|
|||
self.tmp_dir = os.path.join(self.sys_dir, 'tmp')
|
||||
else:
|
||||
self.tmp_dir = os.path.join(tmpdir, 'sysc')
|
||||
os.mkdir(self.tmp_dir)
|
||||
|
||||
self.prepare()
|
||||
|
||||
def __del__(self):
|
||||
if not self.preserve_tmp:
|
||||
if not self.chroot:
|
||||
print(f"Deleting {self.dev_name}")
|
||||
if self.dev_name is not None:
|
||||
print(f"Detaching {self.dev_name}")
|
||||
run('sudo', 'losetup', '-d', self.dev_name)
|
||||
print(f"Unmounting tmpfs from {self.tmp_dir}")
|
||||
umount(self.tmp_dir)
|
||||
os.rmdir(self.tmp_dir)
|
||||
|
||||
def prepare(self):
|
||||
super().__del__()
|
||||
|
||||
def prepare(self, mount_tmpfs, create_disk_image):
|
||||
"""
|
||||
Prepare directory structure for System C.
|
||||
"""
|
||||
self.mount_tmpfs()
|
||||
if not self.chroot:
|
||||
if mount_tmpfs:
|
||||
self.mount_tmpfs()
|
||||
else:
|
||||
os.mkdir(self.tmp_dir)
|
||||
|
||||
rootfs_dir = None
|
||||
|
||||
if create_disk_image:
|
||||
# Create + mount a disk for QEMU to use
|
||||
disk_path = os.path.join(self.tmp_dir, 'disk.img')
|
||||
self.dev_name = create_disk(disk_path, "msdos", "ext4", '8G')
|
||||
self.rootfs_dir = os.path.join(self.tmp_dir, 'mnt')
|
||||
os.mkdir(self.rootfs_dir)
|
||||
mount(self.dev_name + "p1", self.rootfs_dir, 'ext4')
|
||||
rootfs_dir = os.path.join(self.tmp_dir, 'mnt')
|
||||
os.mkdir(rootfs_dir)
|
||||
mount(self.dev_name + "p1", rootfs_dir, 'ext4')
|
||||
# Use chown to allow executing user to access it
|
||||
run('sudo', 'chown', getpass.getuser(), self.dev_name)
|
||||
run('sudo', 'chown', getpass.getuser(), self.rootfs_dir)
|
||||
run('sudo', 'chown', getpass.getuser(), rootfs_dir)
|
||||
else:
|
||||
self.rootfs_dir = self.tmp_dir
|
||||
rootfs_dir = self.tmp_dir
|
||||
|
||||
self.get_packages()
|
||||
|
||||
copytree(self.sys_dir, self.rootfs_dir, ignore=shutil.ignore_patterns("tmp"))
|
||||
copytree(self.sys_dir, rootfs_dir, ignore=shutil.ignore_patterns("tmp"))
|
||||
|
||||
# Unmount tmp/mnt if it exists
|
||||
if not self.chroot:
|
||||
umount(self.rootfs_dir)
|
||||
# Unmount tmp/mnt if it was mounted
|
||||
if create_disk_image:
|
||||
umount(rootfs_dir)
|
||||
|
||||
# pylint: disable=line-too-long,too-many-statements
|
||||
def get_packages(self):
|
||||
|
|
Loading…
Reference in New Issue