Project Report: Add support for chdir(2) support in posix_spawn(3)

November 22, 2021 posted by Martin Husemann

This post was written by Piyush Sachdeva:


The primary goal of the project was to extend posix_spawn(3) to include chdir(2) for the newly created child process. Two functions were supposed to be implemented, namely posix_spawn_file_actions_addchdir() and posix_spawn_file_actions_addfchdir(), to support both chdir(2) and fchdir(2) respectively. posix_spawn() is a POSIX standard method responsible for creating and executing new child processes.


The original code can be found at my github tree.

The implementation plan was discussed and made with the guidance of both my mentors Martin Husemann and Joerg Sonnenberger. The plan was divided into three phases each corresponding to the specific part of The NetBSD code-base which is supposed to be touched:


The following actions were performed in the user-land to set things up for the kernel-space.

  • Add another member to the posix_spawn_file_actions_t struct i.e a union, which would hold the path to chdir to.
  • Implement the two functions posix_spawn_file_actions_addchdir() and posix_spawn_file_actions_addfchdir(). These functions would:
    1. allocate memory for another posix_spawn_file_actions_t object in the posix_spawn_file_actions_t array.
    2. take the path/file descriptor from the user as an argument and make the relative field of the newly allocated file actions object, point to it.
  • The final step was to add the prototypes for the two new functions to the `src/include/spawn.h' header file

Once the aforementioned changes were made, the only thing left to do was to make the kernel support these two new functions.


The following actions were performed inside the kernel space.

  • The three functions in the `src/sys/kern_exec.c' file which correspond to the posix_spawn_file_actions were edited:
    • posix_spawn_fa_alloc() was adjusted to make sure that the path passed to posix_spawn_file_actions_addchdir() gets copied from the user-land to the kernel-space.
    • Similarly posix_spawn_fa_free() was adjusted to make sure that the memory allocated in case of FAE_CHDIR gets freed as well.
    • Finally, two new cases FAE_CHDIR & FAE_FCHDIR were added in the handle_posix_spawn_file_actions(). In each of the cases, a call to one of the two newly created functions (discussed in the next point) do_sys_chdir() and do_sys_fchdir() was made respectively.

    Note: At the time of code integration, a helper function was written by Christos Zoulas. This function aimed to reduce the amount of repeated code in both posix_spawn_fa__free() and posix_spawn_fa_alloc()

  • Two new functions, similar to the already present sys_chdir() and sys_fchdir() in `src/sys/vfs_syscalls.c' were created. Namely do_sys_chdir() and do_sys_chdir were written with two specific thoughts in mind:
    • By default sys_chdir() and sys_fchdir() took syscallargs as a parameter. The purpose of the new functions was to replace this with const char * and an int type parameter respectively.
    • The do_sys_chdir() also replaced UIO_USERSPACE with UIO_SYSSPACE. This was done because the chdir path passed to this function already resided in the Kernel-space due to the change made in posix_spawn_fa_alloc().
  • Finally, the prototypes for the newly written functions were added to the `src/sys/sys/vfs_syscalls.h' file and this file was also included in the 'sys/kern/kern_exec.c'.

Note: Similar to the above changes of user-land and kernel-space, a few tweaks were also made to `src/sys/compat/netbsd/netbsd32.h' and `netbsd32_execve.c'. This was required to help COMPAT_NETBSD32 deal with the new file actions member. However, these changes were made at the time of integration by Martin Husemann.

With most of addition of new features being done, all that remained was testing and documentation.

Testing & Documentation

  • A total of ten new test cases have been added to the `src/tests/lib/libc/gen/posix_spawn/t_spawn.c' file.
  • Three utility functions were also used to aid in testing. Out of the three, one new function was written and two existing functions (filesize() and empty_outfile()) from `t_fileactions.c' were used. To make sure that the 2 existing functions were shared between both the files i.e `t_spawn.c' and `t_fileactions.c' a new header and C file was created, namely `fa_spawn_utils.h' and `fa_spawn_utils.c'. Following this, the bodies of both the functions were moved from `t_fileactions.c' to `fa_spawn_utils.c' and their prototypes were added to the corresponding header file.
  • The general approach that was taken to all test cases was to make posix_spawn() execute ``/bin/pwd'' and write the output to a file. Then read the file and do string comparison. The third function i.e. check_succes() was written for just this purpose.
  • The ten test cases cover the following scenarios:
    • Absolute path test - for both chdir and fchdir.
    • Relative path test - for both chdir and fchdir.
    • Trying to open a file instead of directory - for both chdir and fchdir.
    • Invalid path/file descriptor (fd=-1) - for both chdir and fchdir.
    • Trying to open a directory without access permissions for chdir.
    • Opening a closed file descriptor for fchdir.
  • The first 8 test cases had a lot of repetitive code. Therefore, at the time of integration, another function was created i.e spawn_chdir(). This function included a huge chunk of the common code and it did all the heavy lifting for those first 8 test cases.


In this matter, a complete man page is written which explains both posix_spawn_file_actions_addchdir() and posix_spawn_file_actions_addfchdir() in great detail. The content of the manual page is taken from the POSIX documentation provided to us by Robert Elz.


Since the project was well planned from the beginning, it resulted in few issues.

  • The user-land was the most straight forward part of the project and I had no trouble sailing through it.
  • Kernel space was where things got a bit complicated, as I had to add functionality to pre-existing functions.
  • I was completely new to using atf(7) and groff(1). Therefore, it took me some time to understand the respective man pages and become comfortable with testing and documentation part.

Most of the issues faced were generally logistical. As it was my first time doing a kernel project, I was new to building from source, Virtual Machines and other things like SSH. But luckily, I had great help from my mentors and the entire NetBSD community.


I would like to express my heartfelt gratitude to The NetBSD Foundation for giving me this opportunity and sponsoring the Project. This project would not have been possible without the constant support and encouragement of both my mentors Martin Husemann and Joerg Sonnenberger. My gratitude to Christos Zoulas who worked on the crucial part of integrating the code. A special mention to all of the other esteemed NetBSD developers, who have helped me navigate through the thick and thin of this project and have answered even my most trivial questions.



Post a Comment:
Comments are closed for this entry.