Loading buildstream/_sandboxchroot.py +88 −145 Original line number Diff line number Diff line Loading @@ -34,16 +34,78 @@ from . import utils, _signals from . import Sandbox, SandboxFlags class Mount(): def __init__(self, platform): self.platform = platform def _mount(self, src, dest, mount_type=None, options=None): argv = [utils.get_host_tool('mount')] if mount_type: argv.extend([self.platform.switch(sunos='-F', default='-t'), mount_type]) if options: argv.extend(['-o', options]) process = subprocess.Popen( argv + [src, dest], stdout=subprocess.PIPE, stderr=subprocess.PIPE ) _, err = process.communicate() status = process.poll() if status != 0: raise ElementError('Mounting {} failed with exit code {}:\n{}' .format(src, status, err.decode('utf-8'))) return dest def _umount(self, path): args = self.platform.switch(linux='-l', default=[]) process = subprocess.Popen( [utils.get_host_tool('umount'), args, path], stdout=subprocess.PIPE, stderr=subprocess.PIPE ) _, err = process.communicate() status = process.poll() if status != 0: raise ElementError('Unmounting {} failed with exit code {}:\n{}' .format(path, status, err.decode('utf-8'))) @contextmanager def mount(self, src, dest, mount_type=None, **kwargs): options = ','.join([key for key, val in kwargs.items() if val]) yield self._mount(src, dest, mount_type, options) self._umount(dest) @contextmanager def bind_mount(self, src, dest, **kwargs): options = ','.join([key for key, val in kwargs.items() if val]) options += self.platform.switch(linux=',' if options else '' + 'rbind', aix=',' if options else '' + 'rbind', sunos='') mount_type = self.platform.switch(aix='lofs', default=None) yield self._mount(src, dest, mount_type, options) self._umount(dest) class SandboxChroot(Sandbox): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._mount_bind = self._get_context()._platform.switch( sunos=[utils.get_host_tool('mount'), '-F', 'lofs'], aix=[utils.get_host_tool('mount'), '-o', 'bind'], linux=[utils.get_host_tool('mount'), '--rbind'], # openbsd=[utils.get_host_tool('mount_nullfs')] ) self.mount = Mount(self._get_context()._platform) def run(self, command, flags, cwd=None, env=None): Loading Loading @@ -115,7 +177,7 @@ class SandboxChroot(Sandbox): with ExitStack() as stack: stack.enter_context(self.create_devices(flags)) stack.enter_context(self.mount(flags)) stack.enter_context(self.mount_dirs(flags)) stack.enter_context(_signals.suspendable(suspend, resume)) stack.enter_context(_signals.terminator(terminate)) Loading Loading @@ -256,7 +318,7 @@ class SandboxChroot(Sandbox): with ExitStack() as stack: stack.enter_context(self.create_devices(flags)) stack.enter_context(self.mount(flags)) stack.enter_context(self.mount_dirs(flags)) process = subprocess.Popen( [utils.get_host_tool('chroot'), self.get_directory()] + command, Loading Loading @@ -306,148 +368,29 @@ class SandboxChroot(Sandbox): # flags (:class:`.SandboxFlags`): The sandbox flags # @contextmanager def mount(self, flags): mounting = [] def mount_dirs(self, flags): with ExitStack() as stack: if flags & SandboxFlags.INTERACTIVE: mount_point = self.create_mount_point('/dev/', 'dir') mounting.append(self.mount_bind('/dev/', mount_point)) point = os.path.join(self.get_directory(), '/dev/'.lstrip(os.sep)) os.makedirs(point, exist_ok=True) stack.enter_context(self.mount.bind_mount('/dev/', point)) mount_point = self.create_mount_point('/proc/', 'dir') mounting.append(self.mount_proc(mount_point)) point = os.path.join(self.get_directory(), '/proc/'.lstrip(os.sep)) os.makedirs(point, exist_ok=True) stack.enter_context(self.mount.mount('/proc/', point, 'proc', ro=True)) mount_point = self.create_mount_point('/tmp/', 'dir') mounting.append(self.mount_bind('/tmp/', mount_point)) point = os.path.join(self.get_directory(), '/tmp/'.lstrip(os.sep)) os.makedirs(point, exist_ok=True) stack.enter_context(self.mount.bind_mount('/tmp/', point)) # User defined # mount_map = MountMap(self, False) # root_mount_source = mount_map.get_mount_source('/') # mount_root_cmd = self._mount_bind + [root_mount_source, '/'] for point, process in mounting: _, err = process.communicate() status = process.poll() if not status == 0: raise ElementError('Mounting {} failed with exit code {}: {}' .format(point, status, err.decode('utf-8'))) yield unmounting = [] for point, _ in mounting: unmounting.append(self.unmount(point)) for point, process in unmounting: _, err = process.communicate() status = process.poll() if status != 0: raise ElementError('Unmounting {} failed with exit code {}:\n{}' .format(point, status, err.decode('utf-8'))) point = os.path.join(self.get_directory(), point.lstrip(os.sep)) # Try removing a file first, in case we mounted a file. try: os.remove(point) # If the file is a directory, remove the directory. except IsADirectoryError: shutil.rmtree(point) # If no file is found, the file was removed by umount. except FileNotFoundError: pass # mount_bind() # # Create a binding mount from the given source to a target in the sandbox. # # Args: # source (str): The directory or file to mount # target (str): The directory or file inside the sandbox to mount to # stdout (file): The stdout to report to # stderr (file): The stderr to report to # # Returns: # (tuple): (sandbox_target, process) # def mount_bind(self, source, point): process = subprocess.Popen( self._mount_bind + [source, point], stdout=subprocess.PIPE, stderr=subprocess.PIPE ) return (point, process) # mount_proc() # # Mount the proc device - do *not* use mount_bind() for this. # def mount_proc(self, point): cmd = self._get_context()._platform.switch( sunos=[utils.get_host_tool('mount'), '-F', 'proc', '/proc', point], linux=[utils.get_host_tool('mount'), '-t', 'proc', '/proc', point], aix=[utils.get_host_tool('mount'), '-o', 'bind', '/proc', point], # openbsd=[utils.get_host_tool('mount_nullfs')] ) process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) return (point, process) # unmount() # # Unmount a directory in the sandbox. # # Args: # target (str): The directory or file inside the sandbox to unmount # stdout (file): The stdout to report to # stderr (file): The stderr to report to # # Returns: # (tuple): (sandbox_target, process) # def unmount(self, point): process = subprocess.Popen( [utils.get_host_tool('umount'), point], stdout=subprocess.PIPE, stderr=subprocess.PIPE ) return (point, process) # create_mount_point() # # Create a mount point. # # Args: # target (str): The path to create # kind ('file'|'dir'): Whether to create a file or a dir # # Returns: # (str): The location of the mount point # def create_mount_point(self, target, kind): mount_point = os.path.join(self.get_directory(), target.lstrip(os.sep)) if kind == 'dir': os.makedirs(mount_point, exist_ok=True) elif kind == 'file': try: open(mount_point, mode='x') except FileExistsError: pass return mount_point # mknod() # # Create a device node equivalent to the given source node Loading Loading
buildstream/_sandboxchroot.py +88 −145 Original line number Diff line number Diff line Loading @@ -34,16 +34,78 @@ from . import utils, _signals from . import Sandbox, SandboxFlags class Mount(): def __init__(self, platform): self.platform = platform def _mount(self, src, dest, mount_type=None, options=None): argv = [utils.get_host_tool('mount')] if mount_type: argv.extend([self.platform.switch(sunos='-F', default='-t'), mount_type]) if options: argv.extend(['-o', options]) process = subprocess.Popen( argv + [src, dest], stdout=subprocess.PIPE, stderr=subprocess.PIPE ) _, err = process.communicate() status = process.poll() if status != 0: raise ElementError('Mounting {} failed with exit code {}:\n{}' .format(src, status, err.decode('utf-8'))) return dest def _umount(self, path): args = self.platform.switch(linux='-l', default=[]) process = subprocess.Popen( [utils.get_host_tool('umount'), args, path], stdout=subprocess.PIPE, stderr=subprocess.PIPE ) _, err = process.communicate() status = process.poll() if status != 0: raise ElementError('Unmounting {} failed with exit code {}:\n{}' .format(path, status, err.decode('utf-8'))) @contextmanager def mount(self, src, dest, mount_type=None, **kwargs): options = ','.join([key for key, val in kwargs.items() if val]) yield self._mount(src, dest, mount_type, options) self._umount(dest) @contextmanager def bind_mount(self, src, dest, **kwargs): options = ','.join([key for key, val in kwargs.items() if val]) options += self.platform.switch(linux=',' if options else '' + 'rbind', aix=',' if options else '' + 'rbind', sunos='') mount_type = self.platform.switch(aix='lofs', default=None) yield self._mount(src, dest, mount_type, options) self._umount(dest) class SandboxChroot(Sandbox): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._mount_bind = self._get_context()._platform.switch( sunos=[utils.get_host_tool('mount'), '-F', 'lofs'], aix=[utils.get_host_tool('mount'), '-o', 'bind'], linux=[utils.get_host_tool('mount'), '--rbind'], # openbsd=[utils.get_host_tool('mount_nullfs')] ) self.mount = Mount(self._get_context()._platform) def run(self, command, flags, cwd=None, env=None): Loading Loading @@ -115,7 +177,7 @@ class SandboxChroot(Sandbox): with ExitStack() as stack: stack.enter_context(self.create_devices(flags)) stack.enter_context(self.mount(flags)) stack.enter_context(self.mount_dirs(flags)) stack.enter_context(_signals.suspendable(suspend, resume)) stack.enter_context(_signals.terminator(terminate)) Loading Loading @@ -256,7 +318,7 @@ class SandboxChroot(Sandbox): with ExitStack() as stack: stack.enter_context(self.create_devices(flags)) stack.enter_context(self.mount(flags)) stack.enter_context(self.mount_dirs(flags)) process = subprocess.Popen( [utils.get_host_tool('chroot'), self.get_directory()] + command, Loading Loading @@ -306,148 +368,29 @@ class SandboxChroot(Sandbox): # flags (:class:`.SandboxFlags`): The sandbox flags # @contextmanager def mount(self, flags): mounting = [] def mount_dirs(self, flags): with ExitStack() as stack: if flags & SandboxFlags.INTERACTIVE: mount_point = self.create_mount_point('/dev/', 'dir') mounting.append(self.mount_bind('/dev/', mount_point)) point = os.path.join(self.get_directory(), '/dev/'.lstrip(os.sep)) os.makedirs(point, exist_ok=True) stack.enter_context(self.mount.bind_mount('/dev/', point)) mount_point = self.create_mount_point('/proc/', 'dir') mounting.append(self.mount_proc(mount_point)) point = os.path.join(self.get_directory(), '/proc/'.lstrip(os.sep)) os.makedirs(point, exist_ok=True) stack.enter_context(self.mount.mount('/proc/', point, 'proc', ro=True)) mount_point = self.create_mount_point('/tmp/', 'dir') mounting.append(self.mount_bind('/tmp/', mount_point)) point = os.path.join(self.get_directory(), '/tmp/'.lstrip(os.sep)) os.makedirs(point, exist_ok=True) stack.enter_context(self.mount.bind_mount('/tmp/', point)) # User defined # mount_map = MountMap(self, False) # root_mount_source = mount_map.get_mount_source('/') # mount_root_cmd = self._mount_bind + [root_mount_source, '/'] for point, process in mounting: _, err = process.communicate() status = process.poll() if not status == 0: raise ElementError('Mounting {} failed with exit code {}: {}' .format(point, status, err.decode('utf-8'))) yield unmounting = [] for point, _ in mounting: unmounting.append(self.unmount(point)) for point, process in unmounting: _, err = process.communicate() status = process.poll() if status != 0: raise ElementError('Unmounting {} failed with exit code {}:\n{}' .format(point, status, err.decode('utf-8'))) point = os.path.join(self.get_directory(), point.lstrip(os.sep)) # Try removing a file first, in case we mounted a file. try: os.remove(point) # If the file is a directory, remove the directory. except IsADirectoryError: shutil.rmtree(point) # If no file is found, the file was removed by umount. except FileNotFoundError: pass # mount_bind() # # Create a binding mount from the given source to a target in the sandbox. # # Args: # source (str): The directory or file to mount # target (str): The directory or file inside the sandbox to mount to # stdout (file): The stdout to report to # stderr (file): The stderr to report to # # Returns: # (tuple): (sandbox_target, process) # def mount_bind(self, source, point): process = subprocess.Popen( self._mount_bind + [source, point], stdout=subprocess.PIPE, stderr=subprocess.PIPE ) return (point, process) # mount_proc() # # Mount the proc device - do *not* use mount_bind() for this. # def mount_proc(self, point): cmd = self._get_context()._platform.switch( sunos=[utils.get_host_tool('mount'), '-F', 'proc', '/proc', point], linux=[utils.get_host_tool('mount'), '-t', 'proc', '/proc', point], aix=[utils.get_host_tool('mount'), '-o', 'bind', '/proc', point], # openbsd=[utils.get_host_tool('mount_nullfs')] ) process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) return (point, process) # unmount() # # Unmount a directory in the sandbox. # # Args: # target (str): The directory or file inside the sandbox to unmount # stdout (file): The stdout to report to # stderr (file): The stderr to report to # # Returns: # (tuple): (sandbox_target, process) # def unmount(self, point): process = subprocess.Popen( [utils.get_host_tool('umount'), point], stdout=subprocess.PIPE, stderr=subprocess.PIPE ) return (point, process) # create_mount_point() # # Create a mount point. # # Args: # target (str): The path to create # kind ('file'|'dir'): Whether to create a file or a dir # # Returns: # (str): The location of the mount point # def create_mount_point(self, target, kind): mount_point = os.path.join(self.get_directory(), target.lstrip(os.sep)) if kind == 'dir': os.makedirs(mount_point, exist_ok=True) elif kind == 'file': try: open(mount_point, mode='x') except FileExistsError: pass return mount_point # mknod() # # Create a device node equivalent to the given source node Loading