Skip to content

Commit a299a23

Browse files
committed
Adding the ChrootManager class
Chroot manager class can be used to setup a chroot enviroment including an arbitrary list of bind mounts. The provided paths to bind are mounted in the chroot to the same actual root path they are bound from. This class is useful to setup a chroot envirment based on the root-tree created on prepare step. Signed-off-by: David Cassany <[email protected]>
1 parent 8949fce commit a299a23

File tree

1 file changed

+119
-0
lines changed

1 file changed

+119
-0
lines changed

kiwi/chroot_manager.py

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# Copyright (c) 2025 SUSE Software Solutions Germany GmbH. All rights reserved.
2+
#
3+
# This file is part of kiwi.
4+
#
5+
# kiwi is free software: you can redistribute it and/or modify
6+
# it under the terms of the GNU General Public License as published by
7+
# the Free Software Foundation, either version 3 of the License, or
8+
# (at your option) any later version.
9+
#
10+
# kiwi is distributed in the hope that it will be useful,
11+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
# GNU General Public License for more details.
14+
#
15+
# You should have received a copy of the GNU General Public License
16+
# along with kiwi. If not, see <http://www.gnu.org/licenses/>
17+
#
18+
import os
19+
import logging
20+
from typing import (
21+
List, Optional
22+
)
23+
24+
# project
25+
from kiwi.mount_manager import MountManager
26+
from kiwi.command import (
27+
Command, CommandT, MutableMapping
28+
)
29+
from kiwi.exceptions import (
30+
KiwiUmountBusyError
31+
)
32+
33+
log = logging.getLogger('kiwi')
34+
35+
36+
class ChrootManager:
37+
"""
38+
**Implements methods for setting and unsetting a chroot environment**
39+
40+
The caller is responsible for cleaning up bind mounts if the ChrootManager
41+
is used as is.
42+
43+
The class also supports to be used as a context manager, where any bind or kernel
44+
filesystem mount is unmounted once the context manager's with block is left
45+
46+
* :param string root_dir: path to change the root to
47+
* :param list binds: current root paths to bind to the chrooted path
48+
"""
49+
def __init__(self, root_dir: str, binds: List[str] = []):
50+
self.root_dir = root_dir
51+
self.mounts: List[MountManager] = []
52+
for bind in binds:
53+
self.mounts.append(MountManager(
54+
device=bind, mountpoint=os.path.normpath(root_dir + bind)
55+
))
56+
57+
def __enter__(self) -> "ChrootManager":
58+
try:
59+
self.mount()
60+
except Exception as e:
61+
self.umount()
62+
raise KiwiChrootEnterError(e)
63+
return self
64+
65+
def __exit__(self, exc_type, exc_value, traceback) -> None:
66+
self.umount()
67+
68+
def mount(self) -> None:
69+
"""
70+
Mounts binds to the chroot path
71+
"""
72+
for mnt in self.mounts:
73+
mnt.bind_mount()
74+
75+
def umount(self) -> None:
76+
"""
77+
Unmounts all binds from the chroot path
78+
79+
If any unmount raises a KiwiUmountBusyError this is trapped
80+
and kept until the iteration over all bind mounts is over.
81+
"""
82+
errors = []
83+
for mnt in reversed(self.mounts):
84+
try:
85+
mnt.umount()
86+
except KiwiUmountBusyError as e:
87+
errors.append(e)
88+
89+
if errors:
90+
raise KiwiUmountBusyError(errors)
91+
92+
def run(
93+
self, command: List[str],
94+
custom_env: Optional[MutableMapping[str, str]] = None,
95+
raise_on_error: bool = True, stderr_to_stdout: bool = False,
96+
raise_on_command_not_found: bool = True
97+
) -> Optional[CommandT]:
98+
"""
99+
This is a wrapper for Command.run method but pre-appending the
100+
chroot call at the command list
101+
102+
:param list command: command and arguments
103+
:param dict custom_env: custom os.environ
104+
:param bool raise_on_error: control error behaviour
105+
:param bool stderr_to_stdout: redirects stderr to stdout
106+
107+
:return:
108+
Contains call results in command type
109+
110+
.. code:: python
111+
112+
CommandT(output='string', error='string', returncode=int)
113+
114+
:rtype: CommandT
115+
"""
116+
chroot_cmd = ['chroot', self.root_dir]
117+
chroot_cmd = chroot_cmd + command
118+
return Command.run(chroot_cmd)
119+

0 commit comments

Comments
 (0)