summaryrefslogtreecommitdiff
path: root/source/fud_file.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/fud_file.cpp')
-rw-r--r--source/fud_file.cpp201
1 files changed, 201 insertions, 0 deletions
diff --git a/source/fud_file.cpp b/source/fud_file.cpp
new file mode 100644
index 0000000..e27a46d
--- /dev/null
+++ b/source/fud_file.cpp
@@ -0,0 +1,201 @@
+/*
+ * libfud
+ * Copyright 2024 Dominick Allen
+ *
+ * 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 "fud_file.hpp"
+
+#include <cerrno>
+#include <fcntl.h>
+#include <linux/openat2.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+namespace fud {
+
+FileResult RegularFile::open(
+ StringView filename,
+ FileAccessMode mode,
+ OpenFlags flags,
+ Option<int> dirFdOption,
+ Allocator* allocator)
+{
+ if (allocator == nullptr) {
+ return FudStatus::NullPointer;
+ }
+
+ if (!filename.nullTerminated()) {
+ return FudStatus::ArgumentInvalid;
+ }
+
+ int dirFd = dirFdOption.valueOr(AT_FDCWD);
+
+ RegularFile file{};
+
+ uint32_t openFlags = 0;
+ switch (mode) {
+ case FileAccessMode::Read:
+ openFlags = O_RDONLY;
+ break;
+ case FileAccessMode::Write:
+ openFlags = O_WRONLY;
+ break;
+ case FileAccessMode::ReadWrite:
+ openFlags = O_RDWR;
+ break;
+ default:
+ return FudStatus::ArgumentInvalid;
+ }
+
+ if (flags.hasFlag(OpenFlagEnum::Append) && flags.hasFlag(OpenFlagEnum::Truncate)) {
+ return FudStatus::OperationInvalid;
+ }
+
+ openFlags |= flags.flags();
+
+ open_how openHow{};
+ zeroObject(openHow);
+ openHow.flags = openFlags;
+ openHow.resolve = RESOLVE_NO_SYMLINKS;
+
+ auto status = syscall(SYS_openat2, dirFd, filename.data(), &openHow, sizeof(openHow));
+ if (status == -1) {
+ if constexpr (EAGAIN != EWOULDBLOCK && status == EWOULDBLOCK) {
+ return FudStatus::Partial;
+ }
+ switch (errno) {
+ case ETXTBSY:
+ case EAGAIN:
+ return FudStatus::Partial;
+ case ENOENT:
+ return FudStatus::NotFound;
+ case EFBIG:
+ case EOVERFLOW:
+ case EINVAL:
+ case EISDIR:
+ case ENAMETOOLONG:
+ return FudStatus::ArgumentInvalid;
+ case EROFS:
+ case EACCES:
+ case EPERM:
+ return FudStatus::PermissionDenied;
+ case ELOOP:
+ case EXDEV:
+ case ENFILE:
+ case E2BIG:
+ default:
+ return FudStatus::Failure;
+ }
+ }
+ fudAssert(status <= std::numeric_limits<decltype(file.m_fd)>::max());
+ file.m_fd = static_cast<decltype(file.m_fd)>(status);
+
+ using Stat = struct stat;
+ Stat sBuffer{};
+ auto fStatus = fstat(file.m_fd, &sBuffer);
+ if (fStatus == -1) {
+ return FudStatus::Failure;
+ }
+
+ if ((sBuffer.st_mode & S_IFMT) != S_IFREG) {
+ return FudStatus::ObjectInvalid;
+ }
+
+ return file;
+}
+
+/*
+ static FileResult RegularFile::create(
+ StringView filename,
+ FileAccessMode mode,
+ OpenFlags flags,
+ bool exclusive,
+ Option<int> dirFdOption,
+ Allocator* allocator = &globalFudAllocator);
+{
+}
+*/
+
+RegularFile::~RegularFile()
+{
+ static_cast<void>(this->close());
+}
+
+RegularFile::RegularFile(RegularFile&& rhs) noexcept : m_fd{rhs.m_fd}, m_modeFlags{rhs.m_modeFlags}
+{
+ rhs.m_fd = -1;
+}
+
+RegularFile& RegularFile::operator=(RegularFile&& rhs) noexcept
+{
+ if (&rhs == this) {
+ return *this;
+ }
+
+ static_cast<void>(this->close());
+
+ m_fd = rhs.m_fd;
+ m_modeFlags = rhs.m_modeFlags;
+
+ rhs.m_fd = -1;
+ return *this;
+}
+
+FudStatus RegularFile::take(RegularFile& rhs)
+{
+ if (&rhs == this) {
+ return FudStatus::Success;
+ }
+
+ auto status = this->close();
+ if (status != FudStatus::Success) {
+ return status;
+ }
+
+ m_fd = rhs.m_fd;
+ m_modeFlags = rhs.m_modeFlags;
+
+ rhs.m_fd = -1;
+ return status;
+}
+
+FudStatus RegularFile::close()
+{
+ FudStatus status = FudStatus::Success;
+ if (m_fd != -1) {
+ auto closeStatus = ::close(m_fd);
+ if (closeStatus == -1) {
+ switch (errno) {
+ case EBADF:
+ status = FudStatus::HandleInvalid;
+ break;
+ case EINTR:
+ case EIO:
+ case ENOSPC:
+ case EDQUOT:
+ status = FudStatus::Partial;
+ break;
+ default:
+ status = FudStatus::Failure;
+ break;
+ }
+ }
+ m_fd = -1;
+ }
+ return status;
+}
+
+} // namespace fud