summaryrefslogtreecommitdiffstatshomepage
path: root/src/osd/modules/file/posixfile.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/osd/modules/file/posixfile.cpp')
-rw-r--r--src/osd/modules/file/posixfile.cpp483
1 files changed, 483 insertions, 0 deletions
diff --git a/src/osd/modules/file/posixfile.cpp b/src/osd/modules/file/posixfile.cpp
new file mode 100644
index 00000000000..a5e3fe6a54e
--- /dev/null
+++ b/src/osd/modules/file/posixfile.cpp
@@ -0,0 +1,483 @@
+// license:BSD-3-Clause
+// copyright-holders:Olivier Galibert, R. Belmont, Vas Crabb
+//============================================================
+//
+// sdlfile.c - SDL file access functions
+//
+// SDLMAME by Olivier Galibert and R. Belmont
+//
+//============================================================
+
+
+#ifndef _LARGEFILE64_SOURCE
+#define _LARGEFILE64_SOURCE
+#endif
+
+#ifdef __linux__
+#define __USE_LARGEFILE64
+#endif
+
+#ifdef WIN32
+#define _FILE_OFFSET_BITS 64
+#endif
+
+#if !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__OpenBSD__) && !defined(__bsdi__) && !defined(__DragonFly__)
+#ifdef _XOPEN_SOURCE
+#if _XOPEN_SOURCE < 500
+#undef _XOPEN_SOURCE
+#endif
+#endif
+#ifndef _XOPEN_SOURCE
+#define _XOPEN_SOURCE 500
+#endif
+#endif
+
+// MAME headers
+#include "posixfile.h"
+
+#include <cassert>
+#include <cerrno>
+#include <cstring>
+#include <memory>
+#include <type_traits>
+#include <vector>
+
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+
+
+namespace {
+
+//============================================================
+// CONSTANTS
+//============================================================
+
+#if defined(WIN32)
+constexpr char PATHSEPCH = '\\';
+constexpr char INVPATHSEPCH = '/';
+#else
+constexpr char PATHSEPCH = '/';
+constexpr char INVPATHSEPCH = '\\';
+#endif
+
+
+
+class posix_osd_file : public osd_file
+{
+public:
+ posix_osd_file(posix_osd_file const &) = delete;
+ posix_osd_file(posix_osd_file &&) = delete;
+ posix_osd_file& operator=(posix_osd_file const &) = delete;
+ posix_osd_file& operator=(posix_osd_file &&) = delete;
+
+ posix_osd_file(int fd) : m_fd(fd)
+ {
+ assert(m_fd >= 0);
+ }
+
+ virtual ~posix_osd_file() override
+ {
+ ::close(m_fd);
+ }
+
+ virtual error read(void *buffer, std::uint64_t offset, std::uint32_t count, std::uint32_t &actual) override
+ {
+ ssize_t result;
+
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) || defined(EMSCRIPTEN)
+ result = ::pread(m_fd, buffer, size_t(count), off_t(std::make_unsigned_t<off_t>(offset)));
+#elif defined(WIN32) || defined(SDLMAME_NO64BITIO)
+ if (lseek(m_fd, off_t(std::make_unsigned_t<off_t>(offset)), SEEK_SET) < 0)
+ return errno_to_file_error(errno)
+ result = ::read(m_fd, buffer, size_t(count));
+#else
+ result = ::pread64(m_fd, buffer, size_t(count), off64_t(offset));
+#endif
+
+ if (result < 0)
+ return errno_to_file_error(errno);
+
+ actual = std::uint32_t(std::size_t(result));
+ return error::NONE;
+ }
+
+ virtual error write(void const *buffer, std::uint64_t offset, std::uint32_t count, std::uint32_t &actual) override
+ {
+ ssize_t result;
+
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) || defined(EMSCRIPTEN)
+ result = ::pwrite(m_fd, buffer, size_t(count), off_t(std::make_unsigned_t<off_t>(offset)));
+#elif defined(WIN32) || defined(SDLMAME_NO64BITIO)
+ if (lseek(m_fd, off_t(std::make_unsigned_t<off_t>(offset)), SEEK_SET) < 0)
+ return errno_to_file_error(errno)
+ result = ::write(m_fd, buffer, size_t(count));
+#else
+ result = ::pwrite64(m_fd, buffer, size_t(count), off64_t(offset));
+#endif
+
+ if (result < 0)
+ return errno_to_file_error(errno);
+
+ actual = std::uint32_t(std::size_t(result));
+ return error::NONE;
+ }
+
+ virtual error truncate(std::uint64_t offset) override
+ {
+ int result;
+
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) || defined(EMSCRIPTEN) || defined(WIN32) || defined(SDLMAME_NO64BITIO)
+ result = ::ftruncate(m_fd, off_t(std::make_unsigned_t<off_t>(offset)));
+#else
+ result = ::ftruncate64(m_fd, off64_t(offset));
+#endif
+
+ if (result < 0)
+ return errno_to_file_error(errno);
+
+ return error::NONE;
+ }
+
+ virtual error flush() override
+ {
+ // no user-space buffering on unistd I/O
+ return error::NONE;
+ }
+
+private:
+ int m_fd;
+};
+
+
+//============================================================
+// is_path_separator
+//============================================================
+
+bool is_path_separator(char c)
+{
+ return (c == PATHSEPCH) || (c == INVPATHSEPCH);
+}
+
+
+//============================================================
+// create_path_recursive
+//============================================================
+
+osd_file::error create_path_recursive(std::string const &path)
+{
+ // if there's still a separator, and it's not the root, nuke it and recurse
+ auto const sep = path.rfind(PATHSEPCH);
+ if ((sep != std::string::npos) && (sep > 0) && (path[sep] != ':') && (path[sep - 1] != PATHSEPCH))
+ {
+ osd_file::error const err = create_path_recursive(path.substr(0, sep));
+ if (err != osd_file::error::NONE)
+ return err;
+ }
+
+ // if the path already exists, we're done
+ struct stat st;
+ if (!::stat(path.c_str(), &st))
+ return osd_file::error::NONE;
+
+ // create the path
+#ifdef WIN32
+ if (mkdir(path.c_str()) < 0)
+#else
+ if (mkdir(path.c_str(), 0777) < 0)
+#endif
+ return errno_to_file_error(errno);
+ else
+ return osd_file::error::NONE;
+}
+
+} // anonymous namespace
+
+
+//============================================================
+// osd_file::open
+//============================================================
+
+osd_file::error osd_file::open(std::string const &path, std::uint32_t openflags, ptr &file, std::uint64_t &filesize)
+{
+ std::string dst;
+ if (posix_check_socket_path(path))
+ return posix_open_socket(path, openflags, file, filesize);
+ else if (posix_check_ptty_path(path))
+ return posix_open_ptty(openflags, file, filesize, dst);
+
+ // select the file open modes
+ int access;
+ if (openflags & OPEN_FLAG_WRITE)
+ {
+ access = (openflags & OPEN_FLAG_READ) ? O_RDWR : O_WRONLY;
+ access |= (openflags & OPEN_FLAG_CREATE) ? (O_CREAT | O_TRUNC) : 0;
+ }
+ else if (openflags & OPEN_FLAG_READ)
+ {
+ access = O_RDONLY;
+ }
+ else
+ {
+ return error::INVALID_ACCESS;
+ }
+#if defined(WIN32)
+ access |= O_BINARY;
+#endif
+
+ // convert the path into something compatible
+ dst = path;
+ for (auto it = dst.begin(); it != dst.end(); ++it)
+ *it = (INVPATHSEPCH == *it) ? PATHSEPCH : *it;
+ osd_subst_env(dst, dst);
+
+ // attempt to open the file
+ int fd = -1;
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) || defined(__HAIKU__) || defined(WIN32) || defined(SDLMAME_NO64BITIO)
+ fd = ::open(dst.c_str(), access, 0666);
+#else
+ fd = ::open64(dst.c_str(), access, 0666);
+#endif
+
+ if (fd < 0)
+ {
+ // create the path if necessary
+ if ((openflags & OPEN_FLAG_CREATE) && (openflags & OPEN_FLAG_CREATE_PATHS))
+ {
+ auto const pathsep = dst.rfind(PATHSEPCH);
+ if (pathsep != std::string::npos)
+ {
+ // create the path up to the file
+ osd_file::error const error = create_path_recursive(dst.substr(0, pathsep));
+
+ // attempt to reopen the file
+ if (error == osd_file::error::NONE)
+ {
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) || defined(__HAIKU__) || defined(WIN32) || defined(SDLMAME_NO64BITIO)
+ fd = ::open(dst.c_str(), access, 0666);
+#else
+ fd = ::open64(dst.c_str(), access, 0666);
+#endif
+ }
+ }
+ }
+
+ // if we still failed, clean up and osd_free
+ if (fd < 0)
+ {
+ return errno_to_file_error(errno);
+ }
+ }
+
+ // get the file size
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) || defined(__HAIKU__) || defined(WIN32) || defined(SDLMAME_NO64BITIO)
+ struct stat st;
+ if (::fstat(fd, &st) < 0)
+#else
+ struct stat64 st;
+ if (::fstat64(fd, &st) < 0)
+#endif
+ {
+ int const error = errno;
+ ::close(fd);
+ return errno_to_file_error(error);
+ }
+ filesize = std::uint64_t(std::make_unsigned_t<decltype(st.st_size)>(st.st_size));
+
+ try
+ {
+ file = std::make_unique<posix_osd_file>(fd);
+ return error::NONE;
+ }
+ catch (...)
+ {
+ ::close(fd);
+ return error::OUT_OF_MEMORY;
+ }
+}
+
+
+//============================================================
+// osd_file::openpty
+//============================================================
+
+osd_file::error osd_file::openpty(ptr &file, std::string &name)
+{
+ std::uint64_t filesize;
+ return posix_open_ptty(OPEN_FLAG_READ | OPEN_FLAG_WRITE, file, filesize, name);
+}
+
+
+//============================================================
+// osd_file::remove
+//============================================================
+
+osd_file::error osd_file::remove(std::string const &filename)
+{
+ if (::unlink(filename.c_str()) < -1)
+ return errno_to_file_error(errno);
+ else
+ return error::NONE;
+}
+
+
+//============================================================
+// osd_get_physical_drive_geometry
+//============================================================
+
+int osd_get_physical_drive_geometry(const char *filename, UINT32 *cylinders, UINT32 *heads, UINT32 *sectors, UINT32 *bps)
+{
+ return FALSE; // no, no way, huh-uh, forget it
+}
+
+
+//============================================================
+// osd_stat
+//============================================================
+
+osd_directory_entry *osd_stat(const std::string &path)
+{
+#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__bsdi__) || defined(__DragonFly__) || defined(__HAIKU__) || defined(WIN32) || defined(SDLMAME_NO64BITIO)
+ struct stat st;
+ int const err = ::stat(path.c_str(), &st);
+#else
+ struct stat64 st;
+ int const err = ::stat64(path.c_str(), &st);
+#endif
+ if (err < 0) return nullptr;
+
+ // create an osd_directory_entry; be sure to make sure that the caller can
+ // free all resources by just freeing the resulting osd_directory_entry
+ osd_directory_entry *const result = reinterpret_cast<osd_directory_entry *>(osd_malloc_array(sizeof(osd_directory_entry) + path.length() + 1));
+ std::strcpy(reinterpret_cast<char *>(result) + sizeof(*result), path.c_str());
+ result->name = reinterpret_cast<char *>(result) + sizeof(*result);
+ result->type = S_ISDIR(st.st_mode) ? ENTTYPE_DIR : ENTTYPE_FILE;
+ result->size = std::uint64_t(std::make_unsigned_t<decltype(st.st_size)>(st.st_size));
+
+ return result;
+}
+
+
+//============================================================
+// osd_get_full_path
+//============================================================
+
+osd_file::error osd_get_full_path(std::string &dst, std::string const &path)
+{
+ try
+ {
+#if defined(WIN32)
+ std::vector<char> path_buffer(MAX_PATH);
+ if (::_fullpath(&path_buffer[0], path.c_str(), MAX_PATH))
+ {
+ dst = &path_buffer[0];
+ return osd_file::error::NONE;
+ }
+ else
+ {
+ return osd_file::error::FAILURE;
+ }
+#else
+ std::vector<char> path_buffer(PATH_MAX);
+ if (::realpath(path.c_str(), &path_buffer[0]))
+ {
+ dst = &path_buffer[0];
+ return osd_file::error::NONE;
+ }
+ else if (path[0] == PATHSEPCH)
+ {
+ dst = path;
+ return osd_file::error::NONE;
+ }
+ else
+ {
+ while (!::getcwd(&path_buffer[0], path_buffer.size()))
+ {
+ if (errno != ERANGE)
+ return errno_to_file_error(errno);
+ else
+ path_buffer.resize(path_buffer.size() * 2);
+ }
+ dst.assign(&path_buffer[0]).push_back(PATHSEPCH);
+ dst.append(path);
+ return osd_file::error::NONE;
+ }
+#endif
+ }
+ catch (...)
+ {
+ return osd_file::error::OUT_OF_MEMORY;
+ }
+}
+
+
+//============================================================
+// osd_is_absolute_path
+//============================================================
+
+bool osd_is_absolute_path(std::string const &path)
+{
+ if (!path.empty() && is_path_separator(path[0]))
+ return true;
+#if !defined(WIN32)
+ else if (!path.empty() && (path[0] == '.'))
+ return true;
+#elif !defined(UNDER_CE)
+ else if ((path.length() > 1) && (path[1] == ':'))
+ return true;
+#endif
+ else
+ return false;
+}
+
+
+//============================================================
+// osd_get_volume_name
+//============================================================
+
+const char *osd_get_volume_name(int idx)
+{
+ if (idx == 0)
+ return "/";
+ else
+ return nullptr;
+ }
+
+
+//============================================================
+// errno_to_file_error
+//============================================================
+
+osd_file::error errno_to_file_error(int error)
+{
+ switch (error)
+ {
+ case 0:
+ return osd_file::error::NONE;
+
+ case ENOENT:
+ case ENOTDIR:
+ return osd_file::error::NOT_FOUND;
+
+ case EACCES:
+ case EROFS:
+#ifndef WIN32
+ case ETXTBSY:
+#endif
+ case EEXIST:
+ case EPERM:
+ case EISDIR:
+ case EINVAL:
+ return osd_file::error::ACCESS_DENIED;
+
+ case ENFILE:
+ case EMFILE:
+ return osd_file::error::TOO_MANY_FILES;
+
+ default:
+ return osd_file::error::FAILURE;
+ }
+}