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.component import user
from builder.lib.config import ArchBuilderConfigError from builder.lib.config import ArchBuilderConfigError
from builder.lib.context import ArchBuilderContext from builder.lib.context import ArchBuilderContext
from builder.lib.cgroup import CGroup
log = getLogger(__name__) log = getLogger(__name__)
@ -14,6 +15,7 @@ def chroot_run(
cwd: str = None, cwd: str = None,
env: dict = None, env: dict = None,
stdin: str | bytes = None, stdin: str | bytes = None,
cgroup: CGroup = None,
) -> int: ) -> int:
""" """
Chroot into rootfs and run programs Chroot into rootfs and run programs
@ -24,7 +26,7 @@ def chroot_run(
path = ctx.get_rootfs() path = ctx.get_rootfs()
args = ["chroot", path] args = ["chroot", path]
args.extend(utils.parse_cmd_args(cmd)) 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): 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 1. /etc/ is used for administrator configs
2. /boot/ is used for system boot up, you can put bootloaders configs into this folder 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. /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") raise ArchBuilderConfigError(f"{action} {path} is not allowed")
@ -85,8 +96,8 @@ def add_file(ctx: ArchBuilderContext, file: dict):
# at least path content # at least path content
if "path" not in file: if "path" not in file:
raise ArchBuilderConfigError("no path set in file") raise ArchBuilderConfigError("no path set in file")
if "content" not in file: if "content" not in file and "source" not in file:
raise ArchBuilderConfigError("no content set in file") raise ArchBuilderConfigError("no content or source set in file")
root = ctx.get_rootfs() root = ctx.get_rootfs()
path: str = file["path"] path: str = file["path"]
if path.startswith("/"): path = path[1:] if path.startswith("/"): path = path[1:]
@ -98,6 +109,9 @@ def add_file(ctx: ArchBuilderContext, file: dict):
# follow symbolic links # follow symbolic links
follow = file["follow"] if "follow" in file else True follow = file["follow"] if "follow" in file else True
# source is a folder
folder = file["folder"] if "folder" in file else False
# files mode # files mode
mode = int(file["mode"]) if "mode" in file else 0o0644 mode = int(file["mode"]) if "mode" in file else 0o0644
@ -109,15 +123,27 @@ def add_file(ctx: ArchBuilderContext, file: dict):
# resolve to rootfs # resolve to rootfs
real = os.path.join(root, path) real = os.path.join(root, path)
if not follow and os.path.exists(real): os.remove(real) if "content" in file:
log.debug(f"create file {real}") if not follow and os.path.exists(real): os.remove(real)
with open(real, "wb") as f: log.debug(f"create file {real}")
content: str = file["content"] with open(real, "wb") as f:
log.debug( content: str = file["content"]
"write to %s with %s", log.debug(
real, content.strip() "write to %s with %s",
) real, content.strip()
f.write(content.encode(encode)) )
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}") log.debug(f"chmod file {real} to {mode:04o}")
os.chmod(real, mode=mode) os.chmod(real, mode=mode)
log.debug(f"chown file {real} to {uid}:{gid}") log.debug(f"chown file {real} to {uid}:{gid}")

View File

@ -1,7 +1,7 @@
import os import os
from logging import getLogger from logging import getLogger
from builder.lib.context import ArchBuilderContext from builder.lib.context import ArchBuilderContext
from builder.lib.mount import MountTab, MountPoint from builder.lib.mount import MountTab
log = getLogger(__name__) log = getLogger(__name__)
@ -49,25 +49,6 @@ def undo_mounts(ctx: ArchBuilderContext):
raise RuntimeError("mount points not cleanup") 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): def init_mount(ctx: ArchBuilderContext):
""" """
Setup mount points for rootfs Setup mount points for rootfs
@ -80,7 +61,7 @@ def init_mount(ctx: ArchBuilderContext):
os.symlink(target, real) os.symlink(target, real)
def root_mount(source, target, fstype, options): def root_mount(source, target, fstype, options):
real = os.path.realpath(os.path.join(root, target)) real = os.path.realpath(os.path.join(root, target))
do_mount(ctx, source, real, fstype, options) ctx.mount(source, real, fstype, options)
try: try:
# ensure mount point is clean # ensure mount point is clean
mnts = MountTab.parse_mounts() 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.utils import parse_cmd_args
from builder.lib.subscript import dict_get from builder.lib.subscript import dict_get
from builder.lib.loop import loop_detach 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.cgroup import CGroup
from builder.lib.subscript import SubScript from builder.lib.subscript import SubScript
from builder.lib.shadow import PasswdFile, GroupFile from builder.lib.shadow import PasswdFile, GroupFile
@ -142,7 +142,8 @@ class ArchBuilderContext:
/, /,
cwd: str = None, cwd: str = None,
env: dict = None, env: dict = None,
stdin: str | bytes = None stdin: str | bytes = None,
cgroup: CGroup = None,
) -> int: ) -> int:
""" """
Run external command Run external command
@ -153,7 +154,8 @@ class ArchBuilderContext:
log.debug(f"running external command {argv}") log.debug(f"running external command {argv}")
fstdin = None if stdin is None else PIPE fstdin = None if stdin is None else PIPE
proc = Popen(args, cwd=cwd, env=env, stdin=fstdin) 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 stdin:
if type(stdin) is str: stdin = stdin.encode() if type(stdin) is str: stdin = stdin.encode()
proc.stdin.write(stdin) proc.stdin.write(stdin)
@ -187,3 +189,31 @@ class ArchBuilderContext:
ss = SubScript() ss = SubScript()
self.config = deepcopy(self.config_orig) self.config = deepcopy(self.config_orig)
ss.parse(self.config) 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)