Compare commits

...

4 Commits

Author SHA1 Message Date
0d8ef8a67c builder: build: filesystem.py: allow add custom kernel and firmware
Signed-off-by: BigfootACA <bigfoot@classfun.cn>
2024-07-21 17:32:19 +08:00
8ed06d8f8b builder: build: filesystem.py: add source for copy local files to rootfs
Signed-off-by: BigfootACA <bigfoot@classfun.cn>
2024-07-21 17:25:00 +08:00
38447f1dd7 builder: lib: context.py: add mount and umount
Signed-off-by: BigfootACA <bigfoot@classfun.cn>
2024-07-21 17:19:38 +08:00
f6a1c73125 builder: lib: context.py: add multiple cgroup supports
Signed-off-by: BigfootACA <bigfoot@classfun.cn>
2024-07-21 17:17:24 +08:00
3 changed files with 74 additions and 37 deletions

View File

@ -5,6 +5,7 @@ from builder.lib import utils
from builder.component import user
from builder.lib.config import ArchBuilderConfigError
from builder.lib.context import ArchBuilderContext
from builder.lib.cgroup import CGroup
log = getLogger(__name__)
@ -14,6 +15,7 @@ def chroot_run(
cwd: str = None,
env: dict = None,
stdin: str | bytes = None,
cgroup: CGroup = None,
) -> int:
"""
Chroot into rootfs and run programs
@ -24,7 +26,7 @@ def chroot_run(
path = ctx.get_rootfs()
args = ["chroot", path]
args.extend(utils.parse_cmd_args(cmd))
return ctx.run_external(args, cwd, env, stdin)
return ctx.run_external(args, cwd, env, stdin, cgroup)
def proc_mkdir(ctx: ArchBuilderContext, file: dict, path: str):
@ -76,8 +78,17 @@ def check_allowed(path: str, action: str):
1. /etc/ is used for administrator configs
2. /boot/ is used for system boot up, you can put bootloaders configs into this folder
3. /var/ is used for daemons runtime states
3. /usr/lib/modules/ is used for kernel modules (custom kernel)
3. /usr/lib/firmware/ is used for system firmware (custom firmware)
"""
if not path.startswith(("/etc/", "/boot/", "/var/")):
allow_list = (
"/etc/",
"/boot/",
"/var/",
"/usr/lib/modules",
"/usr/lib/firmware",
)
if not path.startswith(allow_list):
raise ArchBuilderConfigError(f"{action} {path} is not allowed")
@ -85,8 +96,8 @@ def add_file(ctx: ArchBuilderContext, file: dict):
# at least path content
if "path" not in file:
raise ArchBuilderConfigError("no path set in file")
if "content" not in file:
raise ArchBuilderConfigError("no content set in file")
if "content" not in file and "source" not in file:
raise ArchBuilderConfigError("no content or source set in file")
root = ctx.get_rootfs()
path: str = file["path"]
if path.startswith("/"): path = path[1:]
@ -98,6 +109,9 @@ def add_file(ctx: ArchBuilderContext, file: dict):
# follow symbolic links
follow = file["follow"] if "follow" in file else True
# source is a folder
folder = file["folder"] if "folder" in file else False
# files mode
mode = int(file["mode"]) if "mode" in file else 0o0644
@ -109,6 +123,7 @@ def add_file(ctx: ArchBuilderContext, file: dict):
# resolve to rootfs
real = os.path.join(root, path)
if "content" in file:
if not follow and os.path.exists(real): os.remove(real)
log.debug(f"create file {real}")
with open(real, "wb") as f:
@ -118,6 +133,17 @@ def add_file(ctx: ArchBuilderContext, file: dict):
real, content.strip()
)
f.write(content.encode(encode))
elif "source" in file:
src: str = file["source"]
if not src.startswith("/"):
src = os.path.join(ctx.dir, src)
log.debug(f"copy {src} to {real}")
if folder:
shutil.copytree(src, real, symlinks=follow)
else:
shutil.copyfile(src, real, follow_symlinks=follow)
else:
assert False
log.debug(f"chmod file {real} to {mode:04o}")
os.chmod(real, mode=mode)
log.debug(f"chown file {real} to {uid}:{gid}")

View File

@ -1,7 +1,7 @@
import os
from logging import getLogger
from builder.lib.context import ArchBuilderContext
from builder.lib.mount import MountTab, MountPoint
from builder.lib.mount import MountTab
log = getLogger(__name__)
@ -49,25 +49,6 @@ def undo_mounts(ctx: ArchBuilderContext):
raise RuntimeError("mount points not cleanup")
def do_mount(
ctx: ArchBuilderContext,
source: str,
target: str,
fstype: str,
options: str
):
"""
Add a mount point
"""
mnt = MountPoint()
mnt.source = source
mnt.target = target
mnt.fstype = fstype
mnt.options = options
mnt.mount()
ctx.mounted.insert(0, mnt)
def init_mount(ctx: ArchBuilderContext):
"""
Setup mount points for rootfs
@ -80,7 +61,7 @@ def init_mount(ctx: ArchBuilderContext):
os.symlink(target, real)
def root_mount(source, target, fstype, options):
real = os.path.realpath(os.path.join(root, target))
do_mount(ctx, source, real, fstype, options)
ctx.mount(source, real, fstype, options)
try:
# ensure mount point is clean
mnts = MountTab.parse_mounts()

View File

@ -7,7 +7,7 @@ from builder.lib.cpu import cpu_arch_get
from builder.lib.utils import parse_cmd_args
from builder.lib.subscript import dict_get
from builder.lib.loop import loop_detach
from builder.lib.mount import MountTab
from builder.lib.mount import MountTab, MountPoint
from builder.lib.cgroup import CGroup
from builder.lib.subscript import SubScript
from builder.lib.shadow import PasswdFile, GroupFile
@ -142,7 +142,8 @@ class ArchBuilderContext:
/,
cwd: str = None,
env: dict = None,
stdin: str | bytes = None
stdin: str | bytes = None,
cgroup: CGroup = None,
) -> int:
"""
Run external command
@ -153,7 +154,8 @@ class ArchBuilderContext:
log.debug(f"running external command {argv}")
fstdin = None if stdin is None else PIPE
proc = Popen(args, cwd=cwd, env=env, stdin=fstdin)
self.cgroup.add_pid(proc.pid)
if cgroup is None: cgroup = self.cgroup
cgroup.add_pid(proc.pid)
if stdin:
if type(stdin) is str: stdin = stdin.encode()
proc.stdin.write(stdin)
@ -187,3 +189,31 @@ class ArchBuilderContext:
ss = SubScript()
self.config = deepcopy(self.config_orig)
ss.parse(self.config)
def mount(
self,
source: str,
target: str,
fstype: str,
options: str,
) -> MountPoint:
"""
Add a mount point
"""
mnt = MountPoint()
mnt.source = source
mnt.target = os.path.realpath(target)
mnt.fstype = fstype
mnt.options = options
mnt.mount()
self.mounted.insert(0, mnt)
return mnt
def umount(self, path: str):
"""
Remove a mount point
"""
real = os.path.realpath(path)
if not os.path.ismount(real): return
for mnt in self.mounted.find_target(real):
self.mounted.remove(mnt)