static int setup_exec_directory(
                const ExecContext *context,
                const ExecParameters *params,
                uid_t uid,
                gid_t gid,
                ExecDirectoryType type,
                int *exit_status) {

        static const int exit_status_table[_EXEC_DIRECTORY_TYPE_MAX] = {
                [EXEC_DIRECTORY_RUNTIME] = EXIT_RUNTIME_DIRECTORY,
                [EXEC_DIRECTORY_STATE] = EXIT_STATE_DIRECTORY,
                [EXEC_DIRECTORY_CACHE] = EXIT_CACHE_DIRECTORY,
                [EXEC_DIRECTORY_LOGS] = EXIT_LOGS_DIRECTORY,
                [EXEC_DIRECTORY_CONFIGURATION] = EXIT_CONFIGURATION_DIRECTORY,
        };
        char **rt;
        int r;

        assert(context);
        assert(params);
        assert(type >= 0 && type < _EXEC_DIRECTORY_TYPE_MAX);
        assert(exit_status);

        if (!params->prefix[type])
                return 0;

        if (params->flags & EXEC_CHOWN_DIRECTORIES) {
                if (!uid_is_valid(uid))
                        uid = 0;
                if (!gid_is_valid(gid))
                        gid = 0;
        }

        STRV_FOREACH(rt, context->directories[type].paths) {
                _cleanup_free_ char *p = NULL, *pp = NULL;

                p = path_join(params->prefix[type], *rt);
                if (!p) {
                        r = -ENOMEM;
                        goto fail;
                }

                r = mkdir_parents_label(p, 0755);
                if (r < 0)
                        goto fail;

                if (context->dynamic_user &&
                    (!IN_SET(type, EXEC_DIRECTORY_RUNTIME, EXEC_DIRECTORY_CONFIGURATION) ||
                     (type == EXEC_DIRECTORY_RUNTIME && context->runtime_directory_preserve_mode != EXEC_PRESERVE_NO))) {
                        _cleanup_free_ char *private_root = NULL;

                        /* So, here's one extra complication when dealing with DynamicUser=1 units. In that case we
                         * want to avoid leaving a directory around fully accessible that is owned by a dynamic user
                         * whose UID is later on reused. To lock this down we use the same trick used by container
                         * managers to prohibit host users to get access to files of the same UID in containers: we
                         * place everything inside a directory that has an access mode of 0700 and is owned root:root,
                         * so that it acts as security boundary for unprivileged host code. We then use fs namespacing
                         * to make this directory permeable for the service itself.
                         *
                         * Specifically: for a service which wants a special directory "foo/" we first create a
                         * directory "private/" with access mode 0700 owned by root:root. Then we place "foo" inside of
                         * that directory (i.e. "private/foo/"), and make "foo" a symlink to "private/foo". This way,
                         * privileged host users can access "foo/" as usual, but unprivileged host users can't look
                         * into it. Inside of the namespaceof the container "private/" is replaced by a more liberally
                         * accessible tmpfs, into which the host's "private/foo/" is mounted under the same name, thus
                         * disabling the access boundary for the service and making sure it only gets access to the
                         * dirs it needs but no others. Tricky? Yes, absolutely, but it works!
                         *
                         * Note that we don't do this for EXEC_DIRECTORY_CONFIGURATION as that's assumed not to be
                         * owned by the service itself.
                         * Also, note that we don't do this for EXEC_DIRECTORY_RUNTIME as that's often used for sharing
                         * files or sockets with other services. */

                        private_root = path_join(params->prefix[type], "private");
                        if (!private_root) {
                                r = -ENOMEM;
                                goto fail;
                        }

                        /* First set up private root if it doesn't exist yet, with access mode 0700 and owned by root:root */
                        r = mkdir_safe_label(private_root, 0700, 0, 0, MKDIR_WARN_MODE);
                        if (r < 0)
                                goto fail;

                        pp = path_join(private_root, *rt);
                        if (!pp) {
                                r = -ENOMEM;
                                goto fail;
                        }

                        /* Create all directories between the configured directory and this private root, and mark them 0755 */
                        r = mkdir_parents_label(pp, 0755);
                        if (r < 0)
                                goto fail;

                        if (is_dir(p, false) > 0 &&
                            (laccess(pp, F_OK) < 0 && errno == ENOENT)) {

                                /* Hmm, the private directory doesn't exist yet, but the normal one exists? If so, move
                                 * it over. Most likely the service has been upgraded from one that didn't use
                                 * DynamicUser=1, to one that does. */

                                if (rename(p, pp) < 0) {
                                        r = -errno;
                                        goto fail;
                                }
                        } else {
                                /* Otherwise, create the actual directory for the service */

                                r = mkdir_label(pp, context->directories[type].mode);
                                if (r < 0 && r != -EEXIST)
                                        goto fail;
                        }

                        /* And link it up from the original place */
                        r = symlink_idempotent(pp, p, true);
                        if (r < 0)
                                goto fail;

                } else {
                        r = mkdir_label(p, context->directories[type].mode);
                        if (r < 0) {
                                if (r != -EEXIST)
                                        goto fail;

                                if (type == EXEC_DIRECTORY_CONFIGURATION) {
                                        struct stat st;

                                        /* Don't change the owner/access mode of the configuration directory,
                                         * as in the common case it is not written to by a service, and shall
                                         * not be writable. */

                                        if (stat(p, &st) < 0) {
                                                r = -errno;
                                                goto fail;
                                        }

                                        /* Still complain if the access mode doesn't match */
                                        if (((st.st_mode ^ context->directories[type].mode) & 07777) != 0)
                                                log_warning("%s \'%s\' already exists but the mode is different. "
                                                            "(File system: %o %sMode: %o)",
                                                            exec_directory_type_to_string(type), *rt,
                                                            st.st_mode & 07777, exec_directory_type_to_string(type), context->directories[type].mode & 07777);

                                        continue;
                                }
                        }
                }

                /* Lock down the access mode (we use chmod_and_chown() to make this idempotent. We don't
                 * specifiy UID/GID here, so that path_chown_recursive() can optimize things depending on the
                 * current UID/GID ownership.) */
                r = chmod_and_chown(pp ?: p, context->directories[type].mode, UID_INVALID, GID_INVALID);
                if (r < 0)
                        goto fail;

                /* Then, change the ownership of the whole tree, if necessary. When dynamic users are used we
                 * drop the suid/sgid bits, since we really don't want SUID/SGID files for dynamic UID/GID
                 * assignments to exist.*/
                r = path_chown_recursive(pp ?: p, uid, gid, context->dynamic_user ? 01777 : 07777);
                if (r < 0)
                        goto fail;
        }

        return 0;

fail:
        *exit_status = exit_status_table[type];
        return r;
}