beegfs/meta/source/pmq/pmq_posix_io.hpp
2025-08-10 01:34:16 +02:00

213 lines
5.4 KiB
C++

#pragma once
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "pmq_logging.hpp"
/* Wrapper around open() to open directories to put this awkward code in a
* central place. It is counter-intuitive but this is apparently how you're
* supposed to open directories on Unix, both the reading and "writing" (i.e.
* create, unlink, rename).
* The O_DIRECTORY flag is optional but the O_RDONLY is not; opening with
* O_RDWR | O_DIRECTORY fails with "is a directory" (weird!).
*
* Returns: fd to open directory or -1, in which case the errno variable must
* be handled as usual.
*/
static inline int pmq_open_dir(const char *path)
{
return open(path, O_RDONLY | O_DIRECTORY);
}
static inline int pmq_check_regular_file(int fd, const char *what_file)
{
struct stat st;
if (fstat(fd, &st) == -1)
{
pmq_perr_ef(errno, "Failed to fstat() the fd we opened");
return false;
}
if (! S_ISREG(st.st_mode))
{
pmq_perr_f("We opened the file '%s' expecting a regular file but it's not",
what_file);
return false;
}
return true;
}
// Note: returns an fd >= 0 if successful.
// On failure, -1 is returned and
// - if the file failed to open, errno indicates why the file failed to open.
// - if the file was opened successfuly but then closed again because it was not
// a regular file, errno is set to 0.
static inline int pmq_openat_regular_existing(
int basedir_fd, const char *relpath, int flags)
{
// only access mode may be specified -- no other flags
// In particular, O_CREAT would break the logic.
assert(flags == O_RDWR || flags == O_RDONLY || flags == O_WRONLY);
int fd = openat(basedir_fd, relpath, flags, 0);
if (fd == -1)
{
int e = errno;
pmq_perr_ef(errno, "Failed to openat() existing file='%s', flags=%x",
relpath, flags);
errno = e;
return fd;
}
/* The case where fd refers to something other than a regular file _may_
* have been caught by the kernel already above. For example, opening a
* directory using O_RDWR will fail. On the other hand, opening a directory
* using O_RDONLY will succeed.
* In any case, doing an explicit check here.
*/
if (! pmq_check_regular_file(fd, relpath))
{
close(fd);
errno = 0;
return -1;
}
return fd;
}
static inline int pmq_openat_regular_create(
int basedir_fd, const char *relpath, int flags, mode_t mode)
{
// only access mode may be specified -- no other flags
assert(flags == O_RDWR || flags == O_RDONLY || flags == O_WRONLY);
// But this func makes sure that the creation-flags are specified
flags |= O_CREAT | O_EXCL;
int fd = openat(basedir_fd, relpath, flags, mode);
if (fd == -1)
{
int e = errno;
pmq_perr_ef(errno, "Failed to openat() file='%s', flags=%x, mode=%o",
relpath, flags, (unsigned) mode);
errno = e;
return fd;
}
// all necessarily error handling should be done by the OS. (note O_EXCL)
return fd;
}
__pmq_artificial_func
void assert_sane_size(size_t size)
{
// check that size is representable as a ssize_t too.
// It is implementation defined how syscalls like write() handle write I/O sizes larger than SSIZE_T.
// So better don't even try to.
assert((size_t) (ssize_t) size == size);
}
static inline bool pmq_write_all(int fd, Untyped_Slice slice, const char *what)
{
assert_sane_size(slice.size());
while (slice.size())
{
ssize_t nw = write(fd, slice.data(), slice.size());
if (nw == -1)
{
int e = errno;
pmq_perr_ef(errno, "Failed to write %zu bytes to %s",
slice.size(), what);
errno = e;
return false;
}
slice = slice.offset_bytes((size_t) nw);
}
return true;
}
static inline bool pmq_pwrite_all(int fd, Untyped_Slice slice, off_t offset, const char *what)
{
assert_sane_size(slice.size());
while (slice.size())
{
ssize_t nw = pwrite(fd, slice.data(), slice.size(), offset);
if (nw == -1)
{
int e = errno;
pmq_perr_ef(errno, "Failed to pwrite() %zu bytes at offset %jd to %s",
slice.size(), (intmax_t) offset, what);
errno = e;
return false;
}
slice = slice.offset_bytes((size_t) nw);
offset += (size_t) nw;
}
return true;
}
static inline bool pmq_read_all(int fd, Untyped_Slice slice, const char *what)
{
assert_sane_size(slice.size());
while (slice.size())
{
ssize_t nw = read(fd, slice.data(), slice.size());
if (nw == -1)
{
int e = errno;
pmq_perr_ef(errno, "Failed to read %zu bytes from %s",
slice.size(), what);
errno = e;
return false;
}
slice = slice.offset_bytes((size_t) nw);
}
return true;
}
static inline bool pmq_pread_all(int fd, Untyped_Slice slice, off_t offset, const char *what)
{
assert_sane_size(slice.size());
while (slice.size())
{
ssize_t nw = pread(fd, slice.data(), slice.size(), offset);
if (nw == -1)
{
int e = errno;
pmq_perr_ef(errno, "Failed to pread() %zu bytes at offset %jd to %s",
slice.size(), (intmax_t) offset, what);
errno = e;
return false;
}
slice = slice.offset_bytes((size_t) nw);
offset += (size_t) nw;
}
return true;
}