def unionfs(rw='rw', ro=None, union='union'):
    """
    Decorator for the UnionFS feature.

    This configures a unionfs for projects. The given base_dir and/or image_dir
    are layered as follows:
     image_dir=RW:base_dir=RO
    All writes go to the image_dir, while base_dir delivers the (read-only)
    versions of the rest of the filesystem.

    The unified version will be provided in the project's builddir. Unmouting
    is done as soon as the function completes.

    Args:
        rw: writeable storage area for the unified fuse filesystem.
        ro: read-only storage area for the unified fuse filesystem.
        union: mountpoint of the unified fuse filesystem.
    """
    from functools import wraps

    def wrap_in_union_fs(func):
        """
        Function that wraps a given function inside the file system.

        Args:
            func: The function that needs to be wrapped inside the unions fs.
        Return:
            The file system with the function wrapped inside.
        """

        @wraps(func)
        def wrap_in_union_fs_func(project, *args, **kwargs):
            """
            Wrap the func in the UnionFS mount stack.

            We make sure that the mount points all exist and stack up the
            directories for the unionfs. All directories outside of the default
            build environment are tracked for deletion.
            """
            container = project.container
            if container is None or in_container():
                return func(project, *args, **kwargs)

            build_dir = local.path(project.builddir)
            LOG.debug("UnionFS - Project builddir: %s", project.builddir)
            if __unionfs_is_active(root=build_dir):
                LOG.debug(
                    "UnionFS already active in %s, nesting not supported.",
                    build_dir)
                return func(project, *args, **kwargs)

            ro_dir = local.path(container.local)
            rw_dir = build_dir / rw
            un_dir = build_dir / union
            LOG.debug("UnionFS - RW: %s", rw_dir)

            unionfs_cmd = __unionfs_set_up(ro_dir, rw_dir, un_dir)
            project_builddir_bak = project.builddir
            project.builddir = un_dir

            proc = unionfs_cmd.popen()
            while (not __unionfs_is_active(root=un_dir)) and \
                  (proc.poll() is None):
                pass

            ret = None
            if proc.poll() is None:
                try:
                    with local.cwd(un_dir):
                        ret = func(project, *args, **kwargs)
                finally:
                    project.builddir = project_builddir_bak

                    from signal import SIGINT
                    is_running = proc.poll() is None
                    while __unionfs_is_active(root=un_dir) and is_running:
                        try:
                            proc.send_signal(SIGINT)
                            proc.wait(timeout=3)
                        except subprocess.TimeoutExpired:
                            proc.kill()
                            is_running = False
                    LOG.debug("Unionfs shut down.")

            if __unionfs_is_active(root=un_dir):
                raise UnmountError()

            return ret

        return wrap_in_union_fs_func

    return wrap_in_union_fs
Decorator for the UnionFS feature.

    This configures a unionfs for projects. The given base_dir and/or image_dir
    are layered as follows:
     image_dir=RW:base_dir=RO
    All writes go to the image_dir, while base_dir delivers the (read-only)
    versions of the rest of the filesystem.

    The unified version will be provided in the project's builddir. Unmouting
    is done as soon as the function completes.

    Args:
        rw: writeable storage area for the unified fuse filesystem.
        ro: read-only storage area for the unified fuse filesystem.
        union: mountpoint of the unified fuse filesystem.