diff --git a/install_base.sh b/install_base.sh index c875fb14b2d9e71473e95fd1df96a88e89e014b2..735054d7ab2451f5ef6cbe95f39aaab762d3a754 100755 --- a/install_base.sh +++ b/install_base.sh @@ -158,6 +158,7 @@ apt_packages=( openssh-client sshpass wget + rsync unzip qemu-user-static kernelshark @@ -179,6 +180,7 @@ apt_packages=( pacman_packages=( coreutils git + rsync openssh sshpass base-devel diff --git a/lisa/_kmod.py b/lisa/_kmod.py index 0ca0e7d8352fa92fcdeac0f2455bc440d4fa3dcb..16a3dd3fd31e946da222e1b6900e85fd842c1dd9 100644 --- a/lisa/_kmod.py +++ b/lisa/_kmod.py @@ -338,7 +338,8 @@ def _overlay_folders(lowers, upper=None, backend=None, copy_filter=None): Overlay folders on top of each other. :param lowers: List of read-only lower layers. The end of the list takes - precedence. + precedence. Apart from the first item in the list, all items must have + been populated as an "upper" of its preceding lowers. :type lowers: list(str) :param upper: Read-write upper layer taking all the changes made to the @@ -349,11 +350,12 @@ def _overlay_folders(lowers, upper=None, backend=None, copy_filter=None): * ``overlayfs``: Uses Linux overlayfs mounts. This is the fastest and most space efficient method. - * ``copy``: This uses plain copies to simulate overlayfs. Note that the - simulation is not entirely transparent, as a higher layer is not able - to hide files in lower layers like it can do with overlayfs and - whiteout files. + * ``copy``: This uses plain copies to simulate overlayfs. * ``None``: defaults to ``overlayfs``. + + Note that mixing lowers created with different backends is not + supported. Stick with the same backend when creating all the lowers in + the stack. :type backend: str or None """ logger = logging.getLogger(f'{__name__}.overlay') @@ -414,7 +416,7 @@ def _overlay_folders(lowers, upper=None, backend=None, copy_filter=None): @contextlib.contextmanager def do_copy(dirs): - def _copytree(src, dst): + def _python_copytree(src, dst): base_src = Path(src) base_dst = Path(dst) def copy_file(src, dst): @@ -434,8 +436,8 @@ def _overlay_folders(lowers, upper=None, backend=None, copy_filter=None): else: try: dst_mtime = os.path.getmtime(dst) - except OSError: - if os.path.lexists(dst): + except OSError as e: + if not isinstance(e, FileNotFoundError): os.remove(dst) return shutil.copy2(src=src, dst=dst) else: @@ -446,7 +448,6 @@ def _overlay_folders(lowers, upper=None, backend=None, copy_filter=None): return shutil.copy2(src=src, dst=dst) else: return dst - shutil.copytree( src=str(src), dst=str(dst), @@ -455,9 +456,28 @@ def _overlay_folders(lowers, upper=None, backend=None, copy_filter=None): dirs_exist_ok=True, copy_function=copy_file, ) + + def _rsync_copytree(src, dst): + subprocess.check_call(['rsync', '-au', '--', f'{src}/', dst]) + + def _copytree(src, dst): + try: + _rsync_copytree(src, dst) + # rsync not installed + except FileNotFoundError: + logger.debug('rsync not installed, falling back on python copy') + _python_copytree(src, dst) + logger.debug(f'Copying trees instead of overlayfs for {mount_point}') - for src in lowers: - _copytree(src=src, dst=mount_point) + + # Apart from the first one in the list, all lowers are expected to + # have been populated as an "upper" of the preceding lowers. + # Therefore, we can simply make a copy of the top-most lower. + # + # This is the only way we can let the user delete a file in an + # upper and subsequently restore that state. + src = lowers[-1] + _copytree(src=src, dst=mount_point) try: yield mount_point @@ -1312,6 +1332,7 @@ class KernelTree(Loggable, SerializeViaConstructor): ) + [ str(tree_key), str(build_env), + str(overlay_backend), ] + [ # We need to take checksum the make variables # as well, as it can influence the kernel tree