Repository: SerenityOS/serenity
File: Kernel/Syscalls/posix_spawn.cpp (C++)

Code before:
            return EOVERFLOW;
        Vector<Syscall::StringArgument, 32> strings;
        TRY(strings.try_resize(list.length));
        TRY(copy_from_user(strings.data(), list.strings, size.value()));
        for (size_t i = 0; i < list.length; ++i) {
            auto string = TRY(try_copy_kstring_from_user(strings[i]));
            TRY(output.try_append(move(string)));
        }
        return {};
    };

    Vector<NonnullOwnPtr<KString>> arguments;
    TRY(copy_user_strings(params.arguments, arguments));

    Vector<NonnullOwnPtr<KString>> environment;
    TRY(copy_user_strings(params.environment, environment));

    auto credentials = this->credentials();
    Process::Name name {};
    Process::current().name().with([&name](auto& process_name) {
        name.store_characters(process_name.representable_view());
    });
    auto child_and_first_thread = TRY(Process::create(name.representable_view(), credentials->uid(), credentials->gid(), pid(), m_is_kernel_process, vfs_root_context(), hostname_context(), current_directory(), nullptr, tty(), nullptr));

    auto& child = child_and_first_thread.process;
    auto& child_first_thread = child_and_first_thread.first_thread;

    ArmedScopeGuard thread_finalizer_guard = [&child_first_thread]() {
        SpinlockLocker lock(g_scheduler_lock);
        child_first_thread->detach();
        child_first_thread->set_state(Thread::State::Dying);
    };

    // Since we aren't copying parent's file descriptor table, set it up like create init process does
    TRY(child->m_fds.with_exclusive([&](auto& fds) -> ErrorOr<void> {
        TRY(fds.try_resize(Process::OpenFileDescriptions::max_open()));

        // NOTE: If Device::base_devices() is returning nullptr, it means the null device is not attached which is a bug.
        VERIFY(Device::base_devices() != nullptr);
        auto& device_to_use_as_tty = tty() ? (CharacterDevice&)*tty() : Device::base_devices()->null_device;
        auto description = TRY(device_to_use_as_tty.open(O_RDWR));
        auto setup_description = [&](int fd) {
            fds.m_fds_metadatas[fd].allocate();
            fds[fd].set(*description);
        };
        setup_description(0);
        setup_description(1);
        setup_description(2);

        return {};
    }));

Review comment (suggestion):
`create_user_process` uses a structured binding for this, which might be nice-er

Code after:

    auto copy_user_strings = [](auto const& list, auto& output) -> ErrorOr<void> {
        if (!list.length)
            return {};
        Checked<size_t> size = sizeof(*list.strings);
        size *= list.length;
        if (size.has_overflow())
            return EOVERFLOW;
        Vector<Syscall::StringArgument, 32> strings;
        TRY(strings.try_resize(list.length));
        TRY(copy_from_user(strings.data(), list.strings, size.value()));
        for (size_t i = 0; i < list.length; ++i) {
            auto string = TRY(try_copy_kstring_from_user(strings[i]));
            TRY(output.try_append(move(string)));
        }
        return {};
    };

    Vector<NonnullOwnPtr<KString>> arguments;
    TRY(copy_user_strings(params.arguments, arguments));

    Vector<NonnullOwnPtr<KString>> environment;
    TRY(copy_user_strings(params.environment, environment));

    auto credentials = this->credentials();
    auto [child, child_first_thread] = TRY(Process::create({}, credentials->uid(), credentials->gid(), pid(), m_is_kernel_process, vfs_root_context(), hostname_context(), current_directory(), nullptr, tty(), nullptr));

    ArmedScopeGuard thread_finalizer_guard = [&child_first_thread]() {
        SpinlockLocker lock(g_scheduler_lock);
        child_first_thread->detach();
        child_first_thread->set_state(Thread::State::Dying);
    };

    // "It is implementation-defined whether the fork handlers are run when posix_spawn() or posix_spawnp() is called."
    // We don't run them, as they are currently implemented in LibC.

    TRY(child->m_fds.with_exclusive([&](auto& child_fds) {
        return m_fds.with_exclusive([&](auto const& parent_fds) {
            return child_fds.try_clone(parent_fds);
        });

        // FD_CLOEXEC is handled by do_exec().
        // FIXME: Support FD_CLOFORK.
    }));

    // FIXME: "If file descriptor 0, 1, or 2 would otherwise be closed in the new process image created by posix_spawn() or posix_spawnp(),
    //         implementations may open an unspecified file for the file descriptor in the new process image."

    // Copy protected data which isn't set by do_exec().
    child->with_mutable_protected_data([&](auto& child_protected_data) {
        with_protected_data([&](auto const& parent_protected_data) {