/* * 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 #include #include #include #include #include namespace fud { FileResult RegularFile::open( StringView filename, FileAccessMode mode, OpenFlags flags, Option 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::max()); file.m_fd = static_cast(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 dirFdOption, Allocator* allocator = &globalFudAllocator); { } */ RegularFile::~RegularFile() { static_cast(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(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