The comparison to file descriptors is highly misleading: the current and root directory of a process are not file descriptors or any kind of pointers to an "open file description" (a
struct file), but just pointers to directory entries (
The kernel does not keep an open file description referring to the directory inode pointed by either the current or the root directory, which could be inherited by child processes via any kind of handle.
In order for they to be used in any way, the current and root directory have to be opened by path, just like any other file, and all the standard checks apply.
Opening a file with
O_PATH will return just an opaque handle, and it will succeed with any file that couldn't be normally opened for read or write, provided that the path to it is accessible:
$ perl -e 'sysopen my $fh, "/root", 0, 0 or die "$!"'
Permission denied at -e line 1.
$ perl -e 'sysopen my $fh, "/root", 010000000, 0 or die "$!"' # 010000000 is O_PATH
Such an opaque fd cannot be used as a normal fd even by privileged processes, and fortunately there's no way to do an
openat(fd, "", AT_EMPTY_PATH|O_RDWR) in order to
dup() it into a regular file descriptor ;-)
BTW, the musl library defines
O_PATH since 2012.