#include "common.hh" #include "buffer.hh" #include "io.hh" #include "unique_fd.hh" #include #include #include #include namespace { bool mkdirs_internal(int root, std::filesystem::path const& path, mode_t mode, unique_fd* out) { if (mkdirat(root, path.c_str(), mode) == 0) { if (out) *out = io::openat(root, path, io::open_flags::path | io::open_flags::directory); return true; } if (errno == EEXIST) { struct stat buf; if (fstatat(root, path.c_str(), &buf, 0)) return false; if (!S_ISDIR(buf.st_mode)) return false; if (out) *out = io::openat(root, path, io::open_flags::path | io::open_flags::directory); return true; } if (errno != ENOENT) return false; auto parent = path.parent_path(); if (parent == path || !parent.has_relative_path()) return false; unique_fd parent_fd; if (!mkdirs_internal(root, parent, mode, &parent_fd)) return false; auto name = path.filename(); if (mkdirat(parent_fd.get(), name.c_str(), mode)) return false; if (out) *out = io::openat(parent_fd.get(), name, io::open_flags::path | io::open_flags::directory); return true; } } // namespace namespace io { bool read_all(int fd, void* data, size_t size) { auto* d = reinterpret_cast(data); size_t offset = 0; while (offset < size) { auto got = read(fd, d + offset, size - offset); if (got < 0) { if (errno == EINTR) continue; return false; } if (got == 0) return false; offset += got; } return true; } bool write_all(int fd, void const* data, size_t size) { auto* d = reinterpret_cast(data); size_t offset = 0; while (offset < size) { auto wrote = write(fd, d + offset, size - offset); if (wrote < 0) { if (errno == EINTR) continue; return false; } if (wrote == 0) return false; offset += wrote; } return true; } Return fill(int fd, Buffer* out, size_t buf_request_size, size_t* bytes) { if (bytes) *bytes = 0; while (true) { size_t avail; auto* ptr = out->wbuf(buf_request_size, avail); if (avail == 0) return Return::OK; auto got = read(fd, ptr, avail); if (got < 0) { if (errno == EINTR) continue; if (errno == EAGAIN || errno == EWOULDBLOCK) return Return::OK; return Return::ERR; } if (got == 0) return Return::CLOSED; if (bytes) *bytes += got; out->wcommit(got); // No point in trying again, will most likely get EAGAIN or EWOULDBLOCK. // Might also be because the connection got closed, but we are in no hurry // to find out so worth the not extra syscall at every read. if (static_cast(got) < avail) return Return::OK; } } bool drain(RoBuffer* in, int fd, size_t* bytes) { if (bytes) *bytes = 0; while (true) { size_t avail; auto* ptr = in->rbuf(0, avail); if (avail == 0) return true; auto wrote = write(fd, ptr, avail); if (wrote < 0) { if (errno == EINTR) continue; if (errno == EAGAIN || errno == EWOULDBLOCK) return true; return false; } if (wrote == 0) return false; if (bytes) *bytes += wrote; in->rcommit(wrote); // No point in trying again, will most likely get EAGAIN or EWOULDBLOCK if (static_cast(wrote) < avail) return true; } } bool mkdirs(std::filesystem::path const& path, mode_t mode) { return mkdirs(AT_FDCWD, path, mode); } bool mkdirs(int fd, std::filesystem::path const& path, mode_t mode) { return mkdirs_internal(fd, path, mode, nullptr); } bool make_nonblocking(int fd) { int status = fcntl(fd, F_GETFL, 0); if (status == -1) return false; if ((status & O_NONBLOCK) == 0) { if (fcntl(fd, F_SETFL, status | O_NONBLOCK)) return false; } return true; } unique_fd open(std::filesystem::path const& path, open_flags flags, std::filesystem::perms perms) { return openat(AT_FDCWD, path, flags, perms); } unique_fd openat(int fd, std::filesystem::path const& path, open_flags flags, std::filesystem::perms perms) { int posix_flags = O_RDONLY; if ((flags & open_flags::wronly) == open_flags::wronly) posix_flags |= O_WRONLY; if ((flags & open_flags::rdwr) == open_flags::rdwr) posix_flags |= O_RDWR; if ((flags & open_flags::create) == open_flags::create) posix_flags |= O_CREAT; if ((flags & open_flags::excl) == open_flags::excl) posix_flags |= O_EXCL; if ((flags & open_flags::trunc) == open_flags::trunc) posix_flags |= O_TRUNC; if ((flags & open_flags::append) == open_flags::append) posix_flags |= O_APPEND; if ((flags & open_flags::directory) == open_flags::directory) posix_flags |= O_DIRECTORY; if ((flags & open_flags::path) == open_flags::path) posix_flags |= O_PATH; return unique_fd(::openat(fd, path.c_str(), posix_flags, static_cast(perms))); } bool close(int fd) { return ::close(fd) == 0; } ssize_t pread(int fd, void *buf, size_t count, off_t offset) { return ::pread(fd, buf, count, offset); } ssize_t read(int fd, void *buf, size_t count) { return ::read(fd, buf, count); } ssize_t write(int fd, const void *buf, size_t count) { return ::write(fd, buf, count); } bool access(std::filesystem::path const& path, access_mode mode) { int posix_mode; if (mode == access_mode::exists) { posix_mode = F_OK; } else { posix_mode = 0; if ((mode & access_mode::read) == access_mode::read) posix_mode |= R_OK; if ((mode & access_mode::write) == access_mode::write) posix_mode |= W_OK; if ((mode & access_mode::exec) == access_mode::exec) posix_mode |= X_OK; } return ::access(path.c_str(), posix_mode) == 0; } bool unlinkat(int fd, std::filesystem::path const& path) { return ::unlinkat(fd, path.c_str(), 0) == 0; } bool fallocate(int fd, off_t offset, off_t len) { return posix_fallocate(fd, offset, len) == 0; } } // namespace io