`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.
AT_SYMLINK_NOFOLLOW acts on the symlink itself rather than following it.
AT_EMPTY_PATH acts on the directory if path is empty.
Currently, this is implemented in fstatat itself but the code is general
enough to be refactored once we get the other *at functions. It could
likely just live in openat itself.
Linux's variant uses the syscall as intended. Redox's variant uses fpath
to build a path to pass to fstat from the file descriptor plus the file
name. Unlike the syscall, this isn't atomic so the liminal space between
fpath/getcwd and fstat is subject to TOCTOU.
Beyond fstatat, I moved the stat test to its correct location and added
an assert since the output of the test is unchecked.
I also added AT_FDCWD which seems to be -100 across Unixes. The other
AT_* constants are unimplemented for now.
%m is a format specifier that prints an error string for errno. The
specifier is technically only for syslog, but musl and glibc implement
it for printf itself. Parsing for a single specifier in a single
function is error prone, especially when syslog itself is variadic.
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);