summaryrefslogtreecommitdiffstatshomepage
path: root/src/osd/modules/file/posixfile.cpp
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2016-03-13 13:54:57 +1100
committer Vas Crabb <vas@vastheman.com>2016-03-14 18:55:00 +1100
commit42fbb9c3967fcda37f2d9ae17d5796a79cf027b9 (patch)
treea43047ee00431e5e22bfc5f8f612ff775586e415 /src/osd/modules/file/posixfile.cpp
parent5fc27747031b6ed6f6c0582502098ebfb960be67 (diff)
Make osd_file a polymorphic class that's held with smart pointers
Make avi_file a class that's held with smart pointers, encapsulate various AVI I/O structures Make zip_file and _7z_file classes rather than having free functions everywhere Hide zip/7z class implementation behind an interface, no longer need to call close() to send back to the cache Don't dump as much crap in global namespace Add solaris PTY implementation Improve variable expansion for SDL OSD - supports ~/$FOO/${BAR} syntax Rearrange stuff so the same things are in file module for all OSDs Move file stuff into its own module 7z/zip open and destruct are still not thread-safe due to lack of interlocks around cache access Directory functions still need to be moved to file module SDL OSD may not initialise WinSock on Windows
Diffstat (limited to 'src/osd/modules/file/posixfile.cpp')
-rw-r--r--src/osd/modules/file/posixfile.cpp482
1 files changed, 482 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..3fe7ca9fa90
--- /dev/null
+++ b/src/osd/modules/file/posixfile.cpp
@@ -0,0 +1,482 @@
+// 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 <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(dst, 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;
+ }
+}