Chroot: clean up and centralise unmounting

This commit is contained in:
InsanePrawn 2021-10-25 20:50:46 +02:00
parent 2209447af0
commit 0884cb2efd

View file

@ -64,6 +64,11 @@ Chroot = None
chroots: dict[str, Chroot] = {} chroots: dict[str, Chroot] = {}
def make_abs_path(path: str) -> str:
"""Simply ensures the path string starts with a '/'. Does no disk modifications!"""
return '/' + path.lstrip('/')
def get_chroot_path(chroot_name, override_basepath: str = None) -> str: def get_chroot_path(chroot_name, override_basepath: str = None) -> str:
base_path = config.get_path('chroots') if not override_basepath else override_basepath base_path = config.get_path('chroots') if not override_basepath else override_basepath
return os.path.join(base_path, chroot_name) return os.path.join(base_path, chroot_name)
@ -176,7 +181,8 @@ class Chroot:
raise Exception(f"Chroot {self.name} is already initialized, this seems like a bug") raise Exception(f"Chroot {self.name} is already initialized, this seems like a bug")
return return
self.deactivate() active_previously = self.active
self.deactivate_core()
if self.copy_base: if self.copy_base:
if reset or not os.path.exists(self.get_path('usr/bin')): if reset or not os.path.exists(self.get_path('usr/bin')):
@ -245,6 +251,8 @@ class Chroot:
raise Exception(f'Failed to initialize chroot "{self.name}"') raise Exception(f'Failed to initialize chroot "{self.name}"')
self.initialized = True self.initialized = True
if active_previously:
self.activate()
def mount( def mount(
self, self,
@ -267,9 +275,9 @@ class Chroot:
os.makedirs(absolute_destination, exist_ok=True) os.makedirs(absolute_destination, exist_ok=True)
result = mount(absolute_source, absolute_destination, options=options, fs_type=fs_type, register_unmount=False) result = mount(absolute_source, absolute_destination, options=options, fs_type=fs_type, register_unmount=False)
if result.returncode != 0: if result.returncode != 0:
raise Exception(f'{self.name}: failed to mount {absolute_source} to {relative_destination}') raise Exception(f'{self.name}: failed to mount {absolute_source} to {absolute_destination}')
logging.debug(f'{self.name}: {absolute_source} successfully mounted to {absolute_destination}.') logging.debug(f'{self.name}: {absolute_source} successfully mounted to {absolute_destination}.')
self.active_mounts += [relative_destination] self.active_mounts += [make_abs_path(relative_destination)]
atexit.register(self.deactivate) atexit.register(self.deactivate)
return absolute_destination return absolute_destination
@ -278,10 +286,21 @@ class Chroot:
return return
path = self.get_path(relative_path) path = self.get_path(relative_path)
result = umount(path) result = umount(path)
if result.returncode == 0 and relative_path in self.active_mounts: if result.returncode == 0 and make_abs_path(relative_path) in self.active_mounts:
self.active_mounts.remove(relative_path) self.active_mounts.remove(relative_path)
return result return result
def umount_many(self, relative_paths: list[str]):
# make sure paths start with '/'. Important: also copies the collection and casts to list, which will be sorted!
mounts = [make_abs_path(path) for path in relative_paths]
mounts.sort(reverse=True)
for mount in mounts:
if mount == '/proc':
continue
self.umount(mount)
if '/proc' in mounts:
self.umount('/proc')
def activate(self, fail_if_active: bool = False): def activate(self, fail_if_active: bool = False):
"""mount /dev, /sys and /proc""" """mount /dev, /sys and /proc"""
if self.active and fail_if_active: if self.active and fail_if_active:
@ -293,8 +312,7 @@ class Chroot:
self.active = True self.active = True
def deactivate_core(self): def deactivate_core(self):
for dst in BASIC_MOUNTS.keys(): self.umount_many(BASIC_MOUNTS.keys())
self.umount(dst)
# TODO: so this is a weird one. while the basic bind-mounts get unmounted # TODO: so this is a weird one. while the basic bind-mounts get unmounted
# additional mounts like crossdirect are intentionally left intact. Is such a chroot still `active` afterwards? # additional mounts like crossdirect are intentionally left intact. Is such a chroot still `active` afterwards?
self.active = False self.active = False
@ -303,11 +321,7 @@ class Chroot:
if not self.active: if not self.active:
if fail_if_inactive: if fail_if_inactive:
raise Exception(f"Chroot {self.name} not activated, can't deactivate!") raise Exception(f"Chroot {self.name} not activated, can't deactivate!")
for mount in self.active_mounts[::-1]: self.umount_many(self.active_mounts)
if mount == 'proc':
continue
self.umount(mount)
self.umount('proc')
self.active = False self.active = False
def run_cmd(self, def run_cmd(self,