/* * 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) { if (!filename.nullTerminated()) { return FileResult::error(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 FileResult::error(FudStatus::ArgumentInvalid); } if (flags.hasFlag(OpenFlagEnum::Append) && flags.hasFlag(OpenFlagEnum::Truncate)) { return FileResult::error(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 FileResult::error(FudStatus::Partial); } switch (errno) { case ETXTBSY: case EAGAIN: return FileResult::error(FudStatus::Partial); case ENOENT: return FileResult::error(FudStatus::NotFound); case EBADF: case EFBIG: case EOVERFLOW: case EINVAL: case EISDIR: case ENAMETOOLONG: return FileResult::error(FudStatus::ArgumentInvalid); case EROFS: case EACCES: case EPERM: return FileResult::error(FudStatus::PermissionDenied); case ELOOP: case EXDEV: case ENFILE: case E2BIG: default: return FileResult::error(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 FileResult::error(FudStatus::Failure); } if ((sBuffer.st_mode & S_IFMT) != S_IFREG) { return FileResult::error(FudStatus::ObjectInvalid); } file.m_openFlags = flags; return FileResult::okay(std::move(file)); } FileResult RegularFile::create( StringView filename, FileAccessMode mode, OpenFlags flags, Permissions permissions, bool exclusive, Option dirFdOption) { if (!filename.nullTerminated()) { return FileResult::error(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 FileResult::error(FudStatus::ArgumentInvalid); } if (flags.hasFlag(OpenFlagEnum::Append) && flags.hasFlag(OpenFlagEnum::Truncate)) { return FileResult::error(FudStatus::OperationInvalid); } openFlags |= flags.flags() | O_CREAT | (O_EXCL * static_cast(exclusive)); open_how openHow{}; zeroObject(openHow); openHow.flags = openFlags; openHow.resolve = RESOLVE_NO_SYMLINKS; openHow.mode = permissions.mode(); auto status = syscall(SYS_openat2, dirFd, filename.data(), &openHow, sizeof(openHow)); if (status == -1) { if constexpr (EAGAIN != EWOULDBLOCK && status == EWOULDBLOCK) { return FileResult::error(FudStatus::Partial); } switch (errno) { case ETXTBSY: case EAGAIN: return FileResult::error(FudStatus::Partial); case EBADF: case EFBIG: case EOVERFLOW: case EINVAL: case EISDIR: case ENAMETOOLONG: return FileResult::error(FudStatus::ArgumentInvalid); case EROFS: case EACCES: case EPERM: return FileResult::error(FudStatus::PermissionDenied); case EDQUOT: case ENOENT: case ELOOP: case EXDEV: case ENFILE: case E2BIG: default: return FileResult::error(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 FileResult::error(FudStatus::Failure); } if ((sBuffer.st_mode & S_IFMT) != S_IFREG) { return FileResult::error(FudStatus::ObjectInvalid); } return FileResult::okay(std::move(file)); } RegularFile::~RegularFile() { static_cast(this->close()); } RegularFile::RegularFile(RegularFile&& rhs) noexcept : m_fd{rhs.m_fd}, m_openFlags{rhs.m_openFlags}, 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_openFlags = rhs.m_openFlags; 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_openFlags = rhs.m_openFlags; 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; } Result RegularFile::size() const { using RetType = Result; auto fileSize = lseek(m_fd, 0, SEEK_END); if (fileSize == -1) { switch (errno) { case EBADF: case ESPIPE: return RetType::error(FudStatus::ObjectInvalid); default: return RetType::error(FudStatus::Failure); } } auto seekBegin = lseek(m_fd, 0, SEEK_SET); if (seekBegin == -1) { return RetType::error(FudStatus::Failure); } return RetType::okay(static_cast(fileSize)); } } // namespace fud