Additionally, we were using the `access` syscall for x86_64 for our
linux PAL. I looked at the linux source and found that using the
`access`
syscall is equivalent to using `faccessat` as we do now: https://elixir.bootlin.com/linux/v6.17.5/source/fs/open.c#L550
Also, some of the `fcntl.h` functions were accidentally implemented and exported from `unistd.h`. They are supposed to be implemented in `fcntl.h` and exported to `unistd.h`. This is now the behavior.
`linkat(2)` doesn't have `AT_EMPTY_PATH` as a valid flag in this
implementation because it isn't POSIX. We have it (and have
support for it), but it is more effort to add it. If we need it at some
point, it can be added in about 3 lines.
`openat(2)` previously wasn't exposed, and William was not aware of
`Sys::openat`'s existence. We use it under the hood for `mkfifoat(2)`
and friends, so expose it as a libc API. This helps to pass more os-test tests.
Lastly, the 3 implemented syscalls here help pass some os-test tests.
`posix_fallocate` ensures that a byte range in a file is allocated so
that subsequent writes don't fail. Unlike ftruncate, posix_fallocate
does not shrink files.
The Linux syscall fallocate is similar to posix_fallocate except with
far more control over how byte ranges are allocated (e.g. it supports
file holes and other features). This MR doesn't implement fallocate as
it requires syscall and redoxfs support.
Finally, I changed the flags for flock from usize to c_int. That matches
what we have in libc and also avoids some silly, needless type casting.
struct flock is posix defined locking mechanism on *nix platform
Example usage (copied from https://gavv.github.io/articles/file-locks/) :
#include <fcntl.h>
struct flock fl;
memset(&fl, 0, sizeof(fl));
// lock in shared mode
fl.l_type = F_RDLCK;
// lock entire file
fl.l_whence = SEEK_SET; // offset base is start of the file
fl.l_start = 0; // starting offset is zero
fl.l_len = 0; // len is zero, which is a special value representing end
// of file (no matter how large the file grows in future)
fl.l_pid = 0; // F_SETLK(W) ignores it; F_OFD_SETLK(W) requires it to be zero
// F_SETLKW specifies blocking mode
if (fcntl(fd, F_SETLKW, &fl) == -1) {
exit(1);
}
// atomically upgrade shared lock to exclusive lock, but only
// for bytes in range [10; 15)
//
// after this call, the process will hold three lock regions:
// [0; 10) - shared lock
// [10; 15) - exclusive lock
// [15; SEEK_END) - shared lock
fl.l_type = F_WRLCK;
fl.l_start = 10;
fl.l_len = 5;
// F_SETLKW specifies non-blocking mode
if (fcntl(fd, F_SETLK, &fl) == -1) {
exit(1);
}
// release lock for bytes in range [10; 15)
fl.l_type = F_UNLCK;
if (fcntl(fd, F_SETLK, &fl) == -1) {
exit(1);
}
// close file and release locks for all regions
// remember that locks are released when process calls close()
// on any descriptor for a lock file
close(fd);