diff --git a/CMakeLists.txt b/CMakeLists.txt index 8c80eb3..115b3c0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,12 +1,17 @@ cmake_minimum_required(VERSION 3.1) +set(PROJECT_NAME afp) +set(PROJECT_TYPE CXX) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED TRUE) set(CMAKE_CXX_EXTENSIONS FALSE) +if (CYGWIN OR MSYS OR MINGW) +set(REMAP src/remap_os_error.c) +endif() -add_library(afp src/finder_info.cpp src/resource_fork.cpp src/xattr.c) +add_library(afp src/finder_info.cpp src/resource_fork.cpp src/xattr.c ${REMAP}) target_include_directories(afp INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include/) target_include_directories(afp PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include/afp) diff --git a/include/afp/finder_info.h b/include/afp/finder_info.h index d3794a7..c7d2bd3 100644 --- a/include/afp/finder_info.h +++ b/include/afp/finder_info.h @@ -6,8 +6,11 @@ #include +#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MSYS__) +#define AFP_WIN32 +#endif -#if defined(_WIN32) +#if defined(AFP_WIN32) #pragma pack(push, 2) struct AFP_Info { uint32_t magic; @@ -56,7 +59,7 @@ namespace afp { } - #if defined(_WIN32) + #if defined(AFP_WIN32) bool read(const std::wstring &path, std::error_code &ec) { return open(path, read_only, ec); } @@ -75,7 +78,7 @@ namespace afp { uint32_t creator_type() const; uint32_t file_type() const; -#if defined(_WIN32) +#if defined(AFP_WIN32) const uint8_t *data() const { return _afp.finder_info; } @@ -114,11 +117,12 @@ namespace afp { void clear(); - private: - void close(); - #if defined(_WIN32) + private: + + + #if defined(AFP_WIN32) void *_fd = (void *)-1; AFP_Info _afp; #else @@ -130,6 +134,8 @@ namespace afp { #endif }; + } +#undef AFP_WIN32 #endif diff --git a/include/afp/resource_fork.h b/include/afp/resource_fork.h index 759bb64..cc9b7b4 100644 --- a/include/afp/resource_fork.h +++ b/include/afp/resource_fork.h @@ -4,6 +4,10 @@ #include #include +#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MSYS__) +#define AFP_WIN32 +#endif + namespace afp { class resource_fork { @@ -26,7 +30,7 @@ namespace afp { ~resource_fork() { close(); } static size_t size(const std::string &path, std::error_code &ec); -#ifdef _WIN32 +#ifdef AFP_WIN32 static size_t size(const std::wstring &path, std::error_code &ec); #endif @@ -36,7 +40,7 @@ namespace afp { return open(s, read_only, ec); } -#ifdef _WIN32 +#ifdef AFP_WIN32 bool open(const std::wstring &s, open_mode mode, std::error_code &ec); bool open(const std::wstring &s, std::error_code &ec) { return open(s, read_only, ec); @@ -53,7 +57,7 @@ namespace afp { private: - #ifdef _WIN32 + #ifdef AFP_WIN32 void *_fd = (void *)-1; #else int _fd = -1; @@ -66,5 +70,6 @@ namespace afp { }; } +#undef AFP_WIN32 #endif diff --git a/src/finder_info.cpp b/src/finder_info.cpp index c09df11..a1b33f1 100644 --- a/src/finder_info.cpp +++ b/src/finder_info.cpp @@ -6,6 +6,13 @@ #include #include +#if defined(__CYGWIN__) || defined(__MSYS__) +#if !defined(_WIN32) +#define _WIN32 +#endif +#endif + + #if defined (_WIN32) #define WIN32_LEAN_AND_MEAN #include @@ -13,6 +20,7 @@ #else #include #include +#include #endif #if defined(__APPLE__) @@ -51,22 +59,28 @@ namespace { #endif - - #if defined(_WIN32) + +#ifdef MSVC +#define AFP_ERROR_FILE_NOT_FOUND ERROR_FILE_NOT_FOUND +#define remap_os_error(x) x +#else +#define AFP_ERROR_FILE_NOT_FOUND ENOENT +extern "C" int remap_os_error(unsigned long); +#endif + BOOL _(BOOL x, std::error_code &ec) { - if (!x) ec = std::error_code(GetLastError(), std::system_category()); + if (!x) ec = std::error_code(remap_os_error(GetLastError()), std::system_category()); return x; } HANDLE _(HANDLE x, std::error_code &ec) { if (x == INVALID_HANDLE_VALUE) - ec = std::error_code(GetLastError(), std::system_category()); + ec = std::error_code(remap_os_error(GetLastError()), std::system_category()); return x; } - template HANDLE CreateFileX(const std::string &s, Args... args) { return CreateFileA(s.c_str(), std::forward(args)...); @@ -80,7 +94,6 @@ namespace { template HANDLE CreateFileX(const StringType &s, afp::finder_info::open_mode mode, std::error_code &ec) { - DWORD access = 0; DWORD create = 0; @@ -102,6 +115,33 @@ namespace { return _(CreateFileX(s, access, FILE_SHARE_READ, nullptr, create, FILE_ATTRIBUTE_NORMAL, nullptr), ec); } + DWORD GetFileAttributesX(const std::string &path) { + return GetFileAttributesA(path.c_str()); + } + DWORD GetFileAttributesX(const std::wstring &path) { + return GetFileAttributesW(path.c_str()); + } + + template + bool regular_file(const StringType &path, std::error_code &ec) { + + // make sure this isn't a directory. + DWORD st = GetFileAttributesX(path); + if (st == INVALID_FILE_ATTRIBUTES) { + ec = std::error_code(remap_os_error(GetLastError()), std::system_category()); + return false; + } + if (st & FILE_ATTRIBUTE_DIRECTORY) { + ec = std::make_error_code(std::errc::is_a_directory); + return false; + } + if (st & FILE_ATTRIBUTE_DEVICE) { + ec = std::make_error_code(std::errc::invalid_seek); + return false; + } + return true; + } + #else @@ -111,6 +151,31 @@ namespace { return x; } + bool regular_file(int fd, std::error_code &ec) { + struct stat st; + if (_(::fstat(fd, &st), ec) < 0) { + return false; + } + if (S_ISREG(st.st_mode)) return true; + + if (S_ISDIR(st.st_mode)) { + ec = std::make_error_code(std::errc::is_a_directory); + } else { + ec = std::make_error_code(std::errc::invalid_seek); // ESPIPE. + } + return false; + } + + /* opens a file read-only and verifies it's a regular file */ + int openX(const std::string &path, std::error_code &ec) { + int fd = _(::open(path.c_str(), O_RDONLY, O_NONBLOCK), ec); + if (fd >= 0 && !regular_file(fd, ec)) { + ::close(fd); + fd = -1; + } + return fd; + } + #endif @@ -423,13 +488,17 @@ bool finder_info::open(const std::string &path, open_mode mode, std::error_code s += ":" XATTR_FINDERINFO_NAME; /* open the base file, then the finder info, so we can clarify the error */ + if (!regular_file(path, ec)) { + return false; + } HANDLE h = CreateFileX(path, read_only, ec); if (ec) return false; + _fd = CreateFileX(s, mode, ec); CloseHandle(h); if (ec) { - if (ec.value() == ERROR_FILE_NOT_FOUND) + if (ec.value() == AFP_ERROR_FILE_NOT_FOUND) ec = std::make_error_code(std::errc::no_message_available); return false; } @@ -466,13 +535,17 @@ bool finder_info::open(const std::wstring &path, open_mode mode, std::error_code s += L":" XATTR_FINDERINFO_NAME; /* open the base file, then the finder info, so we can clarify the error */ + if (!regular_file(path, ec)) { + return false; + } HANDLE h = CreateFileX(path, read_only, ec); if (ec) return false; + _fd = CreateFileX(s, mode, ec); CloseHandle(h); if (ec) { - if (ec.value() == ERROR_FILE_NOT_FOUND) + if (ec.value() == AFP_ERROR_FILE_NOT_FOUND) ec = std::make_error_code(std::errc::no_message_available); return false; } @@ -584,7 +657,7 @@ bool finder_info::open(const std::string &path, open_mode mode, std::error_code // attropen is a front end for open / openat. // do it ourselves so we can distinguish file doesn't exist vs attr doesn't exist. - int fd = _(::open(path.c_str(), O_RDONLY), ec); + int fd = openX(path, ec); if (ec) return false; _fd = _(::openat(fd, XATTR_FINDERINFO_NAME, umode | O_XATTR, 0666), ec); @@ -631,7 +704,7 @@ bool finder_info::open(const std::string &path, open_mode mode, std::error_code close(); clear(); - _fd = _(::open(path.c_str(), O_RDONLY), ec); + _fd = openX(path, ec); if (ec) return false; if (mode == read_only || mode == read_write) { diff --git a/src/remap_os_error.c b/src/remap_os_error.c new file mode 100644 index 0000000..85f991e --- /dev/null +++ b/src/remap_os_error.c @@ -0,0 +1,100 @@ +/* + * libstdc++ (cygwin, msys, mingw, etc) use posix errno for std::system_category() + * rather than create a new category, just remap to a posix errno. + */ +#include +#define WIN32_LEAN_AND_MEAN +#include + +int remap_os_error(unsigned long e) { + + switch(e) { + case NO_ERROR: return 0; + default: return EIO; + + case ERROR_INVALID_HANDLE: + return EBADF; + + case ERROR_CANTOPEN: + case ERROR_CANTREAD: + case ERROR_CANTWRITE: + case ERROR_OPEN_FAILED: + case ERROR_READ_FAULT: + case ERROR_SEEK: + case ERROR_WRITE_FAULT: + return EIO; + + + case ERROR_ACCESS_DENIED: + case ERROR_CANNOT_MAKE: + case ERROR_CURRENT_DIRECTORY: + case ERROR_INVALID_ACCESS: + case ERROR_NOACCESS: + case ERROR_SHARING_VIOLATION: + case ERROR_WRITE_PROTECT: + return EACCES; + + case ERROR_ALREADY_EXISTS: + case ERROR_FILE_EXISTS: + return EEXIST; + + case ERROR_BAD_UNIT: + case ERROR_DEV_NOT_EXIST: + case ERROR_INVALID_DRIVE: + return ENODEV; + + case ERROR_BUFFER_OVERFLOW: + return ENAMETOOLONG; + + case ERROR_BUSY: + case ERROR_BUSY_DRIVE: + case ERROR_DEVICE_IN_USE: + case ERROR_OPEN_FILES: + return EBUSY; + + case ERROR_DIR_NOT_EMPTY: + return ENOTEMPTY; + + case ERROR_DIRECTORY: + case ERROR_INVALID_NAME: + case ERROR_NEGATIVE_SEEK: + case ERROR_INVALID_PARAMETER: + return EINVAL; + + case ERROR_DISK_FULL: + case ERROR_HANDLE_DISK_FULL: + return ENOSPC; + + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + case ERROR_BAD_NETPATH: + case ERROR_BAD_NET_NAME: + return ENOENT; + + case ERROR_INVALID_FUNCTION: + return ENOSYS; + + case ERROR_LOCK_VIOLATION: + case ERROR_LOCKED: + return ENOLCK; + + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_OUTOFMEMORY: + case ERROR_NOT_ENOUGH_QUOTA: + return ENOMEM; + + case ERROR_NOT_READY: + case ERROR_RETRY: + return EAGAIN; + + case ERROR_NOT_SAME_DEVICE: + return EXDEV; + + case ERROR_OPERATION_ABORTED: + return ECANCELED; + + case ERROR_TOO_MANY_OPEN_FILES: + return EMFILE; + } + +} \ No newline at end of file diff --git a/src/resource_fork.cpp b/src/resource_fork.cpp index ea4f43b..3426466 100644 --- a/src/resource_fork.cpp +++ b/src/resource_fork.cpp @@ -3,6 +3,13 @@ #include +#if defined(__CYGWIN__) || defined(__MSYS__) +#if !defined(_WIN32) +#define _WIN32 +#endif +#endif + + #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include