summaryrefslogtreecommitdiff
path: root/src/io.cc
diff options
context:
space:
mode:
authorJoel Klinghed <the_jk@spawned.biz>2021-11-17 22:34:57 +0100
committerJoel Klinghed <the_jk@spawned.biz>2021-11-17 22:34:57 +0100
commit6232d13f5321b87ddf12a1aa36b4545da45f173d (patch)
tree23f3316470a14136debd9d02f9e920ca2b06f4cc /src/io.cc
Travel3: Simple image and video display site
Reads the images and videos from filesystem and builds a site in memroy.
Diffstat (limited to 'src/io.cc')
-rw-r--r--src/io.cc234
1 files changed, 234 insertions, 0 deletions
diff --git a/src/io.cc b/src/io.cc
new file mode 100644
index 0000000..04e6dfa
--- /dev/null
+++ b/src/io.cc
@@ -0,0 +1,234 @@
+#include "common.hh"
+
+#include "buffer.hh"
+#include "io.hh"
+#include "unique_fd.hh"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+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<char*>(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<char const*>(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<size_t>(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<size_t>(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<mode_t>(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