/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2016-2019 Danny Robson */ #include "fd.hpp" #include "except.hpp" #include "cast.hpp" #include #include #include using cruft::posix::fd; /////////////////////////////////////////////////////////////////////////////// fd::fd (const std::filesystem::path &path, int flags): fd (path, flags, 0666) { ; } //----------------------------------------------------------------------------- fd::fd (const std::filesystem::path &path, int flags, mode_t mode): m_fd (error::try_value (::open (path.string ().c_str (), flags, mode))) { // You always want binary mode. Always. // // But we want the user to have considered this platform issue regardless // so we won't forcibly set the flag, but we will abort the program when // debugging if they've failed to do so. CHECK_EQ (flags & O_BINARY, O_BINARY); } /////////////////////////////////////////////////////////////////////////////// fd::fd (fd &&rhs) noexcept: m_fd (-1) { std::swap (m_fd, rhs.m_fd); } //----------------------------------------------------------------------------- fd& fd::operator= (fd &&rhs) noexcept { close (); std::swap (m_fd, rhs.m_fd); return *this; } //----------------------------------------------------------------------------- fd& fd::operator= (int rhs) { reset (rhs); return *this; } /////////////////////////////////////////////////////////////////////////////// fd::fd (int _fd): m_fd (_fd) { ; } /////////////////////////////////////////////////////////////////////////////// fd fd::dup (void) const { return dup (m_fd); } //----------------------------------------------------------------------------- fd fd::dup (int _fd) { return fd { error::try_value (::dup (_fd)) }; } /////////////////////////////////////////////////////////////////////////////// fd::~fd () { if (m_fd < 0) return; close (); } /////////////////////////////////////////////////////////////////////////////// struct ::stat fd::stat (void) const { struct stat buf; error::try_value (fstat (m_fd, &buf)); return buf; } /////////////////////////////////////////////////////////////////////////////// #if defined(PLATFORM_LINUX) void fd::allocate (int mode, off_t offset, off_t len) { error::try_call (fallocate, m_fd, mode, offset, len); } #endif /////////////////////////////////////////////////////////////////////////////// void fd::close (void) { error::try_value (::close (m_fd)); m_fd = -1; } //----------------------------------------------------------------------------- void fd::reset (void) { if (m_fd >= 0) { close (); m_fd = -1; } } //----------------------------------------------------------------------------- void fd::reset (int rhs) { if (m_fd >= 0) close (); m_fd = rhs; } //----------------------------------------------------------------------------- int fd::release (void) { int tmp = m_fd; m_fd = -1; return tmp; } /////////////////////////////////////////////////////////////////////////////// ssize_t fd::read (void *buffer, std::size_t count) { return error::try_value ( ::read (m_fd, buffer, cruft::cast::narrow (count)) ); } //----------------------------------------------------------------------------- ssize_t fd::write (const void *buffer, size_t count) { return error::try_value ( ::write (m_fd, buffer, cruft::cast::narrow (count)) ); } #if defined(PLATFORM_WIN32) #include "../win32/windows.hpp" #endif //----------------------------------------------------------------------------- ssize_t fd::pwrite (const void *buf, size_t count, size_t offset) { #if !defined(PLATFORM_WIN32) return error::try_value ( ::pwrite (native (), buf, count, offset) ); #else // Ideally we'd to use something like WriteFile with an OVERLAPPED // structure applied to the handle extracted with _get_osfhandle (m_fd). // // But this modifies the file cursor, and if we need to seek anyway then // we may as well just emulate the entire with a query and two seeks. // // NOTE: It is thus possible to observe the fd with an 'incorrect' file // pointer. Do not rely on POSIX behaviour across multiple threads. auto const initial = lseek (0, SEEK_CUR); ssize_t written = -1; try { (void)lseek (offset, SEEK_SET); written = write (buf, count); } catch (...) { (void)lseek (initial, SEEK_SET); throw; } (void)lseek (initial, SEEK_SET); return written; #endif } /////////////////////////////////////////////////////////////////////////////// off_t fd::lseek (off_t offset, int whence) { return error::try_value ( ::lseek (m_fd, offset, whence) ); } /////////////////////////////////////////////////////////////////////////////// void fd::truncate (off_t length) { error::try_call ( ::ftruncate, m_fd, length ); } /////////////////////////////////////////////////////////////////////////////// fd::operator int (void) const { return m_fd; }