diff options
author | Benjamin Barenblat <bbarenblat@gmail.com> | 2020-10-11 12:04:39 -0400 |
---|---|---|
committer | Benjamin Barenblat <bbarenblat@gmail.com> | 2020-10-11 12:04:39 -0400 |
commit | e662f34ef65a3ae94b4216097acc9f0a25611fac (patch) | |
tree | 37f840729de08066fb4fcac739188a00200781bc /posix_extras.cc | |
parent | 4cd5b689a20b88258242e6c40314c16bd3e2194e (diff) |
Eliminate CMake; flatten directory structure
CMake is probably more trouble than it’s worth for this project.
Replace it with a hand-rolled Ninja file.
Diffstat (limited to 'posix_extras.cc')
-rw-r--r-- | posix_extras.cc | 226 |
1 files changed, 226 insertions, 0 deletions
diff --git a/posix_extras.cc b/posix_extras.cc new file mode 100644 index 0000000..3e916ad --- /dev/null +++ b/posix_extras.cc @@ -0,0 +1,226 @@ +// Copyright 2016 Benjamin Barenblat +// +// Licensed under the Apache License, Version 2.0 (the "License"); you may not +// use this file except in compliance with the License. You may obtain a copy +// of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations under +// the License. + +#include "posix_extras.h" + +#include <array> +#include <cerrno> +#include <cstdint> +#include <experimental/optional> +#include <stdexcept> +#include <system_error> +#include <vector> + +#include <dirent.h> +#include <fcntl.h> +#include <glog/logging.h> +#include <stdio.h> +#include <sys/stat.h> +#include <sys/statvfs.h> +#include <sys/types.h> +#include <unistd.h> + +namespace scoville { + +namespace { + +std::system_error SystemError() { + return std::system_error(errno, std::system_category()); +} + +void ValidatePath(const char* const path) { + if (path[0] == '/') { + throw std::invalid_argument("absolute path"); + } +} + +template <typename T> +T CheckSyscall(const T result) { + if (result == -1) { + throw SystemError(); + } + return result; +} + +} // namespace + +File::File(const char* const path, const int flags, const mode_t mode) + : path_(path) { + fd_ = CheckSyscall(open(path, flags, mode)); + VLOG(1) << "opening file descriptor " << fd_; +} + +File::File(const File& other) : path_(other.path_), fd_(other.Duplicate()) { + VLOG(1) << "opening file descriptor " << fd_; +} + +File::~File() noexcept { + VLOG(1) << "closing file descriptor " << fd_; + try { + CheckSyscall(close(fd_)); + } catch (...) { + LOG(ERROR) << "failed to close file descriptor " << fd_; + } +} + +struct stat File::Stat() const { + struct stat result; + CheckSyscall(fstat(fd_, &result)); + return result; +} + +void File::ChModAt(const char* const path, const mode_t mode) const { + ValidatePath(path); + CheckSyscall(fchmodat(fd_, path, mode, 0)); +} + +struct stat File::LinkStatAt(const char* const path) const { + ValidatePath(path); + struct stat result; + CheckSyscall(fstatat(fd_, path, &result, AT_SYMLINK_NOFOLLOW)); + return result; +} + +void File::MkDir(const char* const path, const mode_t mode) const { + ValidatePath(path); + CheckSyscall(mkdirat(fd_, path, mode | S_IFDIR)); +} + +void File::MkNod(const char* const path, const mode_t mode, + const dev_t dev) const { + ValidatePath(path); + CheckSyscall(mknodat(fd_, path, mode, dev)); +} + +File File::OpenAt(const char* const path, const int flags, + const mode_t mode) const { + ValidatePath(path); + File result; + result.fd_ = CheckSyscall(openat(fd_, path, flags, mode)); + result.path_ = path_ + "/" + path; + return result; +} + +std::vector<std::uint8_t> File::Read(off_t offset, size_t bytes) const { + std::vector<std::uint8_t> result(bytes, 0); + size_t cursor = 0; + ssize_t bytes_read; + while (0 < (bytes_read = CheckSyscall( + pread(fd_, result.data() + cursor, bytes, offset)))) { + cursor += static_cast<size_t>(bytes_read); + offset += bytes_read; + bytes -= static_cast<size_t>(bytes_read); + } + result.resize(cursor); + return result; +} + +std::string File::ReadLinkAt(const char* const path) const { + ValidatePath(path); + std::vector<char> result(64, '\0'); + size_t bytes_read; + + while ((bytes_read = static_cast<size_t>(CheckSyscall(readlinkat( + fd_, path, result.data(), result.size())))) == result.size()) { + // We filled the entire buffer. There may be more data we missed. + result.resize(result.size() * 2); + } + return std::string(result.data(), result.data() + bytes_read); +} + +void File::RenameAt(const char* old_path, const char* new_path) const { + ValidatePath(old_path); + ValidatePath(new_path); + CheckSyscall(renameat(fd_, old_path, fd_, new_path)); +} + +void File::RmDirAt(const char* const path) const { + ValidatePath(path); + CheckSyscall(unlinkat(fd_, path, AT_REMOVEDIR)); +} + +struct statvfs File::StatVFs() const { + struct statvfs result; + CheckSyscall(fstatvfs(fd_, &result)); + return result; +} + +void File::SymLinkAt(const char* const target, const char* const source) const { + ValidatePath(source); + CheckSyscall(symlinkat(target, fd_, source)); +} + +void File::Truncate(const off_t size) { CheckSyscall(ftruncate(fd_, size)); } + +void File::UnlinkAt(const char* const path) const { + ValidatePath(path); + CheckSyscall(unlinkat(fd_, path, 0)); +} + +void File::UTimeNs(const char* const path, const timespec& access, + const timespec& modification) const { + ValidatePath(path); + std::array<const timespec, 2> times{{access, modification}}; + CheckSyscall(utimensat(fd_, path, times.data(), AT_SYMLINK_NOFOLLOW)); +} + +size_t File::Write(const off_t offset, + const std::vector<std::uint8_t>& to_write) { + size_t bytes_written = 0; + while (bytes_written < to_write.size()) { + bytes_written += static_cast<size_t>(CheckSyscall(pwrite( + fd_, to_write.data() + bytes_written, to_write.size() - bytes_written, + offset + static_cast<off_t>(bytes_written)))); + } + return bytes_written; +} + +int File::Duplicate() const { return CheckSyscall(dup(fd_)); } + +Directory::Directory(const File& file) { + // "After a successful call to fdopendir(), fd is used internally by the + // implementation, and should not otherwise be used by the application." We + // therefore need to grab an unmanaged copy of the file descriptor from file. + if (!(stream_ = fdopendir(file.Duplicate()))) { + throw SystemError(); + } + rewinddir(stream_); +} + +Directory::~Directory() noexcept { + try { + CheckSyscall(closedir(stream_)); + } catch (...) { + LOG(ERROR) << "failed to close directory stream"; + } +} + +long Directory::offset() const { return CheckSyscall(telldir(stream_)); } + +void Directory::Seek(const long offset) noexcept { seekdir(stream_, offset); } + +std::experimental::optional<dirent> Directory::ReadOne() { + dirent* result; + errno = 0; + if (!(result = readdir(stream_))) { + if (errno == 0) { + return std::experimental::nullopt; + } else { + throw SystemError(); + } + } + return *result; +} + +} // scoville |