Kernel+LibC: Implement posix_spawn file actions in kernel#26562
Kernel+LibC: Implement posix_spawn file actions in kernel#26562firatkizilboga wants to merge 8 commits intoSerenityOS:masterfrom
Conversation
Hendiadyoin1
left a comment
There was a problem hiding this comment.
Welcome to the Project, and thank you for looking into this
Here are a few nits on my side
There was a problem hiding this comment.
In general, nice to have more test cases,
but I dont quite get the point of having self cleaning and non self cleaning variants of the tests
There was a problem hiding this comment.
Could you clarify this comment? Are you referring to the two paths we are testing? If so, I wanted to test the 'old' path as well, since these changes affect it too.
There was a problem hiding this comment.
Ah my bad, misread the branch and logic a bit
read it as a thing only getting destroyed in the slow path,
but its also only created in the slow path, so you can disregard the comment
There was a problem hiding this comment.
It took me a while to realize this, but this is for testing the non-syscall version. The names use_slow_path and get_attr_for_path confused me a bit. Maybe inverting that condition and calling it use_syscall could be clearer? I'm also not sure why that function is called get_attr_for_path. There is no path parameter.
Also, we usually prefer to use enum classes over bools for parameters that are likely passed as constants (https://github.com/SerenityOS/serenity/blob/master/Documentation/CodingStyle.md#right-8). So I think having an enum
enum class UseSyscall {
No,
Yes,
};would be better here.
Additionally, I think there should be a FIXME comment somewhere noting that this non-syscall version should stopped being tested once we implement spawnattrs in the kernel (as setting spawnattrs would then not change anything).
|
Thanks for the warm welcome! This is my first significant contribution to a project of this size, so I really appreciate the detailed guidance. I hope I'm getting the workflow right! I've just updated the PR with the changes you requested. |
22b6a15 to
a8e5617
Compare
|
Please use To combine those two commits retroactively, you can do |
|
Also, it would be nice if you could split the PR up into smaller atomic commits that only do one change. So one or more for adding the basic infrastructure and then individual commits for each of the spawn actions. Each commit should fully work though, so you likely need to make LibC continue to use the fork+exec impl when a specific file action isn't implemented yet. |
spholz
left a comment
There was a problem hiding this comment.
Still not a full review, it would really be helpful if you could split the changes into smaller steps.
Add comprehensive tests for posix_spawn file actions including: - addopen, addclose, adddup2, addchdir, addfchdir - Both fast path (kernel) and slow path (fork) variants - Error propagation tests for various failure cases - Action ordering verification
Add serialization structures for posix_spawn file actions: - Kernel/API/spawn.h: SpawnFileAction* structs and alignment - LibC spawn.cpp: convert from lambdas to serialize/deserialize - Kernel returns ENOTSUP, triggering LibC fallback to fork() Runtime behavior unchanged - all actions still use fork() path.
First kernel-side file action implementation. The dup2 action duplicates a file descriptor, with explicit closure of any existing fd at the target slot. Other action types still return ENOTSUP, triggering LibC fallback.
Implement the close file action for posix_spawn. Validates the fd exists and closes it properly.
Opens a file and places it at the specified fd. Handles path parsing, flags, mode, umask, and explicit closure of any existing fd.
Changes the current working directory for the spawned process.
Changes current directory via fd. Validates the fd is a directory and checks for execute (search) permission.
All posix_spawn file actions are now implemented in the kernel: - dup2, close, open, chdir, fchdir The ENOTSUP fallback path in LibC is no longer needed. Errors from file actions are now returned directly from the syscall. The fork() path is still used when spawnattr is present (FIXME).
a8e5617 to
6d63f2a
Compare
|
Sorry for the delay, I've almost finished my full review. I hope I can submit it by tomorrow. |
spholz
left a comment
There was a problem hiding this comment.
Thank you for working on this!
(I hope I'm not overwhelming you with my following review comments.)
There was a problem hiding this comment.
nit: I'd rename this file to "Spawn.h" (uppercase "S").
| i32 fd; | ||
| }; | ||
|
|
||
| inline constexpr size_t SPAWN_FILE_ACTION_ALIGNMENT = max( |
There was a problem hiding this comment.
Instead of doing this, I'd either put all spawn file action structs in a (tagged) union, or at least somehow get rid of this max() cascade.
Alternatively, you could maybe use Array::max() by doing something like to_array<size_t>({ alignof(...), ... }).max().
Also, constexpr variables are always implicitly inline, so this inline is unnecessary here.
|
|
||
| struct SpawnFileActionDup2 { | ||
| SpawnFileActionHeader header; | ||
| i32 old_fd; |
There was a problem hiding this comment.
File descriptors use int as their type. Any reason why you don't want to use that here? (Same for all other spawn file action struct file descriptor members)
| if (action() < 0) { | ||
| perror("posix_spawn file action"); | ||
| if (file_actions && !file_actions->state->buffer.is_empty()) { | ||
| using namespace Kernel; |
There was a problem hiding this comment.
I'm not really happy about the using namespace Kernels in this file. I'd prefer to qualify the complete name instead.
| return IterationDecision::Continue; | ||
| return IterationDecision::Break; | ||
| }); | ||
| // Use posix_spawn which handles the syscall path |
There was a problem hiding this comment.
I'm confused by this comment here. I assume you mean that posix_spawn will use the posix_spawn syscall?
| spawn_and_wait(&actions, attr_ptr, "/bin/pwd"sv, argv, 0); | ||
|
|
||
| auto content = read_file_content(out_path); | ||
| EXPECT(content.trim_whitespace() == "/tmp" || content.trim_whitespace() == "/private/tmp"); |
There was a problem hiding this comment.
Why do you allow /private/tmp here? Same for fchdir.
There was a problem hiding this comment.
glibc and freebsd (https://man.freebsd.org/cgi/man.cgi?query=posix_spawn_file_actions_addchdir_np) only seem to provide _np (non portable) versions of addchdir and addfchdir, not sure why. Maybe there is some reason they didn't like some POSIX behavior?
Maybe something worth investigating, but that's not really related to your PR.
|
|
||
| switch (header->type) { | ||
| case SpawnFileActionType::Dup2: { | ||
| if (header->record_length < sizeof(SpawnFileActionDup2)) |
There was a problem hiding this comment.
For non-variable length structs I would be extra cautious and check that they have the exact same size as expected.
| return EINVAL; | ||
|
|
||
| auto const* action = reinterpret_cast<SpawnFileActionDup2 const*>(header); | ||
| TRY(m_fds.with_exclusive([&](auto& fds) -> ErrorOr<void> { |
There was a problem hiding this comment.
Rather than duplicating syscall implementations here, please reuse the existing syscall implementations (I think that should be possible). In some cases that won't be directly possible, so you probably need to add new _impl functions for some of the syscalls.
(note to self: I haven't reviewed at any file action implementation other than dup2.)
|
|
||
| auto description = TRY(fds.open_file_description(action->old_fd)); | ||
| if (action->old_fd != action->new_fd) { | ||
|
|
There was a problem hiding this comment.
It seems like you accidentally added two empty lines in the last commit in this file. Or was that on purpose and you applied the fix to the wrong commit? In that case, please also add empty lines between the other cases.
|
Thank you for the review @spholz. I did some initial work to address your points. I am a bit busy these days, but I have not abandoned this and I will definitely come back to this over the next weeks. About AK::Stream and the discussion around that, I thought we can remove the path from the structs and just put it directly after the related struct in the buffer. I think this could allow use of a stream, please let me know what you think. |
No pressure, take your time!
Yes, I think that should work. Note that you need to do something like this for every struct type to make template<>
class AK::Traits<YourStructNameHere> : public DefaultTraits<YourStructNameHere> {
public:
static constexpr bool is_trivially_serializable() { return true; }
}; |
This commit implements the FIXME left by Tomás Simões to handle file actions directly in the kernel during posix_spawn, avoiding the expensive fork() path.
Changes include:
Supported actions: addopen, addclose, adddup2, addchdir, addfchdir