From a97161e6da759b4b1aef61db449a982d1cfb9f27 Mon Sep 17 00:00:00 2001 From: Marcus Holland-Moritz Date: Tue, 23 Jan 2024 15:33:07 +0100 Subject: [PATCH] feat: support building on OSX (should address gh #132) --- .gitignore | 1 + CMakeLists.txt | 22 +++-- README.md | 66 +++++++++++++++ cmake/osx.sh | 45 +++++++++++ src/dwarfs/cached_block.cpp | 3 +- src/dwarfs/categorizer.cpp | 21 ++++- src/dwarfs/file_stat.cpp | 6 ++ src/dwarfs/inode_manager.cpp | 3 +- src/dwarfs/os_access_generic.cpp | 25 +++++- src/dwarfs_main.cpp | 24 +++++- test/block_cache_test.cpp | 5 +- test/tool_main_test.cpp | 15 +++- test/tools_test.cpp | 135 ++++++++++++++++++++++++------- 13 files changed, 320 insertions(+), 51 deletions(-) create mode 100755 cmake/osx.sh diff --git a/.gitignore b/.gitignore index 579e8646..cb8d20cf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.DS_Store /@* /*.*fs /tmp/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 41c70bd6..2a656293 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -225,6 +225,11 @@ endif() find_package(PkgConfig) +if(APPLE) + # For whatever reason, thrift is unhappy if we don't do this + find_package(OpenSSL 1.1.1 MODULE REQUIRED) +endif() + if(STATIC_BUILD_DO_NOT_USE) if (PKG_CONFIG_FOUND) list(APPEND PKG_CONFIG_EXECUTABLE "--static") @@ -576,7 +581,7 @@ list(APPEND BINARY_TARGETS dwarfsuniversal) list(APPEND MAIN_TARGETS mkdwarfs_main dwarfsck_main dwarfsbench_main dwarfsextract_main) -if(FUSE3_FOUND OR WINFSP) +if(FUSE3_FOUND OR WINFSP OR APPLE) add_library(dwarfs_main src/dwarfs_main.cpp) target_compile_definitions(dwarfs_main PRIVATE _FILE_OFFSET_BITS=64) if(WINFSP) @@ -586,6 +591,9 @@ if(FUSE3_FOUND OR WINFSP) target_link_libraries(dwarfs_main ${WINFSP}) target_link_libraries(dwarfsuniversal delayimp.lib) target_link_options(dwarfsuniversal PRIVATE /DELAYLOAD:winfsp-x64.dll) + elseif(APPLE) + target_compile_definitions(dwarfs_main PRIVATE FUSE_USE_VERSION=29) + target_link_libraries(dwarfs_main PkgConfig::FUSE) else() target_compile_definitions(dwarfs_main PRIVATE FUSE_USE_VERSION=35) target_link_libraries(dwarfs_main PkgConfig::FUSE3) @@ -608,7 +616,7 @@ if(FUSE3_FOUND OR WINFSP) list(APPEND MAIN_TARGETS dwarfs_main) endif() -if(FUSE_FOUND AND (WITH_LEGACY_FUSE OR NOT FUSE3_FOUND)) +if(FUSE_FOUND AND (NOT APPLE) AND (WITH_LEGACY_FUSE OR NOT FUSE3_FOUND)) add_library(dwarfs2_main src/dwarfs_main.cpp) target_compile_definitions(dwarfs2_main PRIVATE _FILE_OFFSET_BITS=64 FUSE_USE_VERSION=29) @@ -1086,11 +1094,15 @@ if(FALSE) endif() endif() -if(STATIC_BUILD_DO_NOT_USE AND NOT WIN32) +if(STATIC_BUILD_DO_NOT_USE OR APPLE) foreach(tgt ${BINARY_TARGETS}) list(APPEND FILES_TO_STRIP $) endforeach() - add_custom_target(strip COMMAND strip -s ${FILES_TO_STRIP}) + if(APPLE) + add_custom_target(strip COMMAND strip ${FILES_TO_STRIP}) + else() + add_custom_target(strip COMMAND strip -s ${FILES_TO_STRIP}) + endif() endif() if(PRJ_VERSION_FULL) @@ -1110,7 +1122,7 @@ if(PRJ_VERSION_FULL) endif() endif() -if(STATIC_BUILD_DO_NOT_USE OR WIN32) +if(STATIC_BUILD_DO_NOT_USE OR WIN32 OR APPLE) find_program(UPX_EXE upx upx.exe PATHS "c:/bin" DOC "ultimate packer for executables" REQUIRED) set(UNIVERSAL_PACKED diff --git a/README.md b/README.md index 584040d6..34d1018f 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,8 @@ A fast high compression read-only file system for Linux and Windows. - [Usage](#usage) - [Windows Support](#windows-support) - [Building on Windows](#building-on-windows) +- [macOS Support](#macos-support) + - [Building on macOS](#building-on-macos) - [Extended Attributes](#extended-attributes) - [Comparison](#comparison) - [With SquashFS](#with-squashfs) @@ -507,6 +509,70 @@ your machine. > ninja test ``` +## macOS Support + +Support for the macOS operating system is currently experimental. + +The macOS version of the DwarFS filesystem driver relies on the awesome +[macFUSE](https://https://osxfuse.github.io/) project. + +### Building on macOS + +Building on macOS involves a few steps, but should be relatively +straightforward. This has so far only been tested on a 2014 iMac running +macOS Big Sur. However, it seems to be possible to build universal (fat) +binaries even on such an old platform. + +- First, install [macFUSE](https://https://osxfuse.github.io/). + +- Install the [mistletoe](https://pypi.org/project/mistletoe/) Python + library, e.g.: + +``` +$ pip3 install --user mistletoe +``` + +- Install `ninja` using Homebrew (you can optionally also install `ccache`): + +``` +$ brew install ninja +``` + +- Clone the [vcpkg](https://vcpkg.io/), + [lipo-dir-merge](https://github.com/faaxm/lipo-dir-merge) and + DwarFS repositories: + +``` +$ cd ~ +$ mkdir git +$ cd git +$ git clone https://github.com/Microsoft/vcpkg.git +$ git clon ne https://github.com/faaxm/lipo-dir-merge +$ git clone --recurse-submodules https://github.com/mhx/dwarfs +``` + +- Bootstrap `vcpkg`: + +``` +$ ./vcpkg/bootstrap-vcpkg.sh +``` + +- Build DwarFS: + +``` +$ cd dwarfs +$ git checkout experimental/osx-build +$ mkdir build +$ cd build +$ sh ../cmake/osx.sh rebuild-vcpkg +$ sh ../cmake/osx.sh +$ ninja +$ export CTEST_PARALLEL_LEVEL=$(sysctl -n hw.logicalcpu) +$ ninja test +``` + +That's it! + ## Extended Attributes ### Preserving Extended Attributes in DwarFS Images diff --git a/cmake/osx.sh b/cmake/osx.sh new file mode 100755 index 00000000..0eb771cc --- /dev/null +++ b/cmake/osx.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +set -e + +if [[ -z "${VCPKG_ROOT}" ]]; then + VCPKG_ROOT=$HOME/git/vcpkg +fi + +if [[ -z "${VCPKG_INSTALL_ROOT}" ]]; then + VCPKG_INSTALL_ROOT=$HOME/git/@vcpkg-install +fi + +if [[ -z "${LIPO_DIR_MERGE}" ]]; then + LIPO_DIR_MERGE=$HOME/git/lipo-dir-merge/lipo-dir-merge.py +fi + +build_mode=$1 + +if [[ -z $build_mode ]]; then + build_mode="Release" +fi + +if [[ $build_mode == "rebuild-vcpkg" ]]; then + for triplet in x64-osx arm64-osx; do + rm -rf $VCPKG_INSTALL_ROOT/tmp/$triplet + $VCPKG_ROOT/vcpkg install --triplet=$triplet --x-install-root=$VCPKG_INSTALL_ROOT/tmp/$triplet + done + + rm -rf $VCPKG_INSTALL_ROOT/uni-osx + + echo "merging x64-osx and arm64-osx to uni-osx..." + + python3 $LIPO_DIR_MERGE \ + $VCPKG_INSTALL_ROOT/tmp/x64-osx/x64-osx \ + $VCPKG_INSTALL_ROOT/tmp/arm64-osx/arm64-osx \ + $VCPKG_INSTALL_ROOT/uni-osx + + echo "DONE" +else + cmake .. -GNinja \ + -DWITH_TESTS=ON -DPREFER_SYSTEM_ZSTD=ON -DUSE_JEMALLOC=OFF \ + -DCMAKE_BUILD_TYPE=$build_mode \ + -DCMAKE_PREFIX_PATH=$VCPKG_INSTALL_ROOT/uni-osx \ + -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" +fi diff --git a/src/dwarfs/cached_block.cpp b/src/dwarfs/cached_block.cpp index 6d2baca4..ce4fef66 100644 --- a/src/dwarfs/cached_block.cpp +++ b/src/dwarfs/cached_block.cpp @@ -93,7 +93,8 @@ class cached_block_ final : public cached_block { bool any_pages_swapped_out(std::vector& tmp [[maybe_unused]]) const override { -#ifndef _WIN32 +#if !(defined(_WIN32) || defined(__APPLE__)) + // TODO: should be possible to do this on Windows and macOS as well auto page_size = ::sysconf(_SC_PAGESIZE); tmp.resize((data_.size() + page_size - 1) / page_size); if (::mincore(const_cast(data_.data()), data_.size(), diff --git a/src/dwarfs/categorizer.cpp b/src/dwarfs/categorizer.cpp index 44450dd9..5a83c7ff 100644 --- a/src/dwarfs/categorizer.cpp +++ b/src/dwarfs/categorizer.cpp @@ -328,10 +328,25 @@ void categorizer_manager_::set_metadata_requirements( template bool categorizer_manager_::deterministic_less( fragment_category a, fragment_category b) const { - auto cmp = category_name(a.value()) <=> category_name(b.value()); - if (cmp != 0) { - return cmp < 0; + auto cna = category_name(a.value()); + auto cnb = category_name(b.value()); + + if (cna < cnb) { + return true; } + + if (cna > cnb) { + return false; + } + + // TODO: the above can be replaced by the following once we have support for + // spaceship operator everywhere + // + // auto cmp = category_name(a.value()) <=> category_name(b.value()); + // if (cmp != 0) { + // return cmp < 0; + // } + auto cat = DWARFS_NOTHROW(categories_.at(a.value())); auto categorizer = DWARFS_NOTHROW(categorizers_.at(cat.second)); return categorizer->subcategory_less(a, b); diff --git a/src/dwarfs/file_stat.cpp b/src/dwarfs/file_stat.cpp index 7e6cc2ab..3dd66695 100644 --- a/src/dwarfs/file_stat.cpp +++ b/src/dwarfs/file_stat.cpp @@ -207,9 +207,15 @@ file_stat make_file_stat(fs::path const& path) { rv.size = st.st_size; rv.blksize = st.st_blksize; rv.blocks = st.st_blocks; +#ifdef __APPLE__ + rv.atime = st.st_atimespec.tv_sec; + rv.mtime = st.st_mtimespec.tv_sec; + rv.ctime = st.st_ctimespec.tv_sec; +#else rv.atime = st.st_atim.tv_sec; rv.mtime = st.st_mtim.tv_sec; rv.ctime = st.st_ctim.tv_sec; +#endif return rv; } diff --git a/src/dwarfs/inode_manager.cpp b/src/dwarfs/inode_manager.cpp index d4f7421d..dfc7f447 100644 --- a/src/dwarfs/inode_manager.cpp +++ b/src/dwarfs/inode_manager.cpp @@ -716,7 +716,8 @@ void inode_manager_::try_scan_invalid(worker_group& wg, if (mm) { LOG_DEBUG << "successfully opened: " << p->path_as_string(); - wg.add_job([this, p, ino, mm = std::move(mm)] { + // TODO: p = p is a workaround for older Clang versions + wg.add_job([this, p = p, ino, mm = std::move(mm)] { ino->scan(mm.get(), opts_, prog_); update_prog(ino, p); }); diff --git a/src/dwarfs/os_access_generic.cpp b/src/dwarfs/os_access_generic.cpp index f6ac5a53..81aefe84 100644 --- a/src/dwarfs/os_access_generic.cpp +++ b/src/dwarfs/os_access_generic.cpp @@ -27,6 +27,11 @@ #include +#ifdef __APPLE__ +#include +#include +#endif + #include "dwarfs/mmap.h" #include "dwarfs/os_access_generic.h" #include "dwarfs/util.h" @@ -132,7 +137,7 @@ void os_access_generic::thread_set_affinity(std::thread::id tid [[maybe_unused]], std::error_code& ec [[maybe_unused]]) const { -#ifndef _WIN32 +#if !(defined(_WIN32) || defined(__APPLE__)) cpu_set_t cpuset; for (auto cpu : cpus) { @@ -174,6 +179,24 @@ os_access_generic::thread_get_cpu_time(std::thread::id tid, return std::chrono::nanoseconds(100 * (sys + user)); +#elif defined(__APPLE__) + + auto port = ::pthread_mach_thread_np(std_to_pthread_id(tid)); + + ::thread_basic_info_data_t ti; + ::mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT; + + if (::thread_info(port, THREAD_BASIC_INFO, + reinterpret_cast(&ti), + &count) != KERN_SUCCESS) { + ec = std::make_error_code(std::errc::not_supported); + return {}; + } + + return std::chrono::seconds(ti.user_time.seconds + ti.system_time.seconds) + + std::chrono::microseconds(ti.user_time.microseconds + + ti.system_time.microseconds); + #else ::clockid_t cid; diff --git a/src/dwarfs_main.cpp b/src/dwarfs_main.cpp index e156888f..1e8cc4f0 100644 --- a/src/dwarfs_main.cpp +++ b/src/dwarfs_main.cpp @@ -816,13 +816,21 @@ int op_statfs(char const* path, native_statvfs* st) { #if DWARFS_FUSE_LOWLEVEL template -void op_getxattr(fuse_req_t req, fuse_ino_t ino, char const* name, - size_t size) { +void op_getxattr(fuse_req_t req, fuse_ino_t ino, char const* name, size_t size +#ifdef __APPLE__ + , + uint32_t position +#endif +) { dUSERDATA; PERFMON_EXT_SCOPED_SECTION(userdata, op_getxattr) LOG_PROXY(LoggerPolicy, userdata.lgr); - LOG_DEBUG << __func__ << "(" << ino << ", " << name << ", " << size << ")"; + LOG_DEBUG << __func__ << "(" << ino << ", " << name << ", " << size +#ifdef __APPLE__ + << ", " << position +#endif + << ")"; checked_reply_err(log_, req, [&] { std::ostringstream oss; @@ -857,7 +865,12 @@ void op_getxattr(fuse_req_t req, fuse_ino_t ino, char const* name, } } +// TODO: figure out under which conditions we don't have ::view() +#ifdef __APPLE__ + auto value = oss.str(); +#else auto value = oss.view(); +#endif LOG_TRACE << __func__ << ": value.size=" << value.size() << ", extra_size=" << extra_size; @@ -912,7 +925,12 @@ void op_listxattr(fuse_req_t req, fuse_ino_t ino, size_t size) { oss << inodeinfo_xattr << '\0'; +// TODO: figure out under which conditions we don't have ::view() +#ifdef __APPLE__ + auto xattrs = oss.str(); +#else auto xattrs = oss.view(); +#endif LOG_TRACE << __func__ << ": xattrs.size=" << xattrs.size(); diff --git a/test/block_cache_test.cpp b/test/block_cache_test.cpp index 86db57da..cd691d64 100644 --- a/test/block_cache_test.cpp +++ b/test/block_cache_test.cpp @@ -248,8 +248,9 @@ TEST_P(options_test, cache_stress) { for (auto const& [i, reqs] : folly::enumerate(data)) { auto& succ = success[i]; - threads.emplace_back([&] { - for (auto const& req : reqs) { + // TODO: preqs is a workaround for older Clang versions + threads.emplace_back([&, preqs = &reqs] { + for (auto const& req : *preqs) { auto fh = fs.open(req.inode); auto range = fs.readv(fh, req.size, req.offset); if (!range) { diff --git a/test/tool_main_test.cpp b/test/tool_main_test.cpp index 000dfe1b..d39d5cf6 100644 --- a/test/tool_main_test.cpp +++ b/test/tool_main_test.cpp @@ -57,6 +57,13 @@ namespace fs = std::filesystem; namespace { +// TODO: this is a workaround for older Clang versions +struct fs_path_hash { + auto operator()(const std::filesystem::path& p) const noexcept { + return std::filesystem::hash_value(p); + } +}; + auto test_dir = fs::path(TEST_DATA_DIR).make_preferred(); auto audio_data_dir = test_dir / "pcmaudio"; @@ -700,7 +707,7 @@ TEST_P(term_logging_test, end_to_end) { auto err = t.err(); auto it = log_level_strings.begin(); - auto make_contains_regex = [fancy](auto m) { + auto make_contains_regex = [fancy = fancy](auto m) { auto const& [color, prefix] = m->second; auto beg = fancy ? color : std::string_view{}; auto end = fancy && !color.empty() ? "" : ""; @@ -2311,7 +2318,7 @@ TEST_P(map_file_error_test, delayed) { auto fs = t.fs_from_file("test.dwarfs"); // fs.dump(std::cout, 2); - std::unordered_map actual_files; + std::unordered_map actual_files; fs.walk([&](auto const& dev) { auto iv = dev.inode(); if (iv.is_regular_file()) { @@ -2360,8 +2367,8 @@ TEST_P(map_file_error_test, delayed) { failed_expected.end(), std::inserter(surprisingly_missing, surprisingly_missing.begin())); - std::unordered_map original_files(files.begin(), - files.end()); + std::unordered_map original_files( + files.begin(), files.end()); EXPECT_EQ(0, surprisingly_missing.size()); diff --git a/test/tools_test.cpp b/test/tools_test.cpp index 28bbd995..c4896b9e 100644 --- a/test/tools_test.cpp +++ b/test/tools_test.cpp @@ -38,7 +38,11 @@ #include #include #include +#ifdef __APPLE__ +#include +#else #include +#endif #include #endif @@ -123,10 +127,27 @@ class scoped_no_leak_check { }; #ifndef _WIN32 +ssize_t portable_listxattr(const char* path, char* list, size_t size) { +#ifdef __APPLE__ + return ::listxattr(path, list, size, 0); +#else + return ::listxattr(path, list, size); +#endif +} + +ssize_t portable_getxattr(const char* path, const char* name, void* value, + size_t size) { +#ifdef __APPLE__ + return ::getxattr(path, name, value, size, 0, 0); +#else + return ::getxattr(path, name, value, size); +#endif +} + pid_t get_dwarfs_pid(fs::path const& path) { std::array attr_buf; - auto attr_len = ::getxattr(path.c_str(), "user.dwarfs.driver.pid", - attr_buf.data(), attr_buf.size()); + auto attr_len = portable_getxattr(path.c_str(), "user.dwarfs.driver.pid", + attr_buf.data(), attr_buf.size()); if (attr_len < 0) { throw std::runtime_error("could not read pid from xattr"); } @@ -332,7 +353,7 @@ class subprocess { (append_arg(cmdline_, std::forward(args)), ...); try { - // std::cout << "running: " << cmdline() << "\n"; + // std::cerr << "running: " << cmdline() << "\n"; c_ = bp::child(prog.string(), bp::args(cmdline_), bp::std_in.close(), bp::std_out > out_, bp::std_err > err_, ios_ #ifdef _WIN32 @@ -346,6 +367,14 @@ class subprocess { } } + ~subprocess() { + if (pt_) { + std::cerr << "subprocess still running in destructor: " << cmdline() + << "\n"; + pt_->join(); + } + } + std::string cmdline() const { std::string cmd = prog_.string(); if (!cmdline_.empty()) { @@ -378,10 +407,13 @@ class subprocess { } void interrupt() { + std::cerr << "interrupting: " << cmdline() << "\n"; #ifdef _WIN32 ::GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, pid()); #else - ::kill(pid(), SIGINT); + if (auto rv = ::kill(pid(), SIGINT); rv != 0) { + std::cerr << "kill(" << pid() << ", SIGINT) = " << rv << "\n"; + } #endif } @@ -438,7 +470,7 @@ class subprocess { std::vector cmdline_; }; -#ifndef _WIN32 +#if !(defined(_WIN32) || defined(__APPLE__)) class process_guard { public: process_guard() = default; @@ -497,11 +529,21 @@ class driver_runner { wait_until_file_ready(mountpoint, std::chrono::seconds(5)); #else + std::vector options; +#ifdef __APPLE__ + options.push_back("-o"); + options.push_back("noapplexattr"); +#endif if (!subprocess::check_run(driver, make_tool_arg(tool_arg), image, - mountpoint, std::forward(args)...)) { + mountpoint, options, + std::forward(args)...)) { throw std::runtime_error("error running " + driver.string()); } +#ifdef __APPLE__ + wait_until_file_ready(mountpoint, std::chrono::seconds(5)); +#else dwarfs_guard_ = process_guard(get_dwarfs_pid(mountpoint)); +#endif #endif } @@ -518,7 +560,7 @@ class driver_runner { #endif std::forward(args)...); process_->run_background(); -#ifndef _WIN32 +#if !(defined(_WIN32) || defined(__APPLE__)) dwarfs_guard_ = process_guard(process_->pid()); #endif } @@ -531,6 +573,27 @@ class driver_runner { #endif if (!mountpoint_.empty()) { +#ifdef __APPLE__ + auto umount = dwarfs::test::find_binary("umount"); + if (!umount) { + throw std::runtime_error("no umount binary found"); + } + subprocess::check_run(umount.value(), mountpoint_); + bool rv{true}; + if (process_) { + process_->wait(); + auto ec = process_->exit_code(); + if (ec != 0) { + std::cerr << "driver failed to unmount:\nout:\n" + << process_->out() << "err:\n" + << process_->err() << "exit code: " << ec << "\n"; + rv = false; + } + } + process_.reset(); + mountpoint_.clear(); + return rv; +#else #ifndef _WIN32 if (process_) { #endif @@ -552,6 +615,7 @@ class driver_runner { mountpoint_.clear(); return dwarfs_guard_.check_exit(std::chrono::seconds(5)); } +#endif #endif } return false; @@ -582,7 +646,7 @@ class driver_runner { } private: -#ifndef _WIN32 +#if !(defined(_WIN32) || defined(__APPLE__)) static fs::path find_fusermount() { auto fusermount_bin = dwarfs::test::find_binary("fusermount"); if (!fusermount_bin) { @@ -606,7 +670,7 @@ class driver_runner { fs::path mountpoint_; std::unique_ptr process_; -#ifndef _WIN32 +#if !(defined(_WIN32) || defined(__APPLE__)) process_guard dwarfs_guard_; #endif }; @@ -878,24 +942,26 @@ TEST_P(tools_test, end_to_end) { std::string buf; buf.resize(1); - auto r = ::listxattr(path.c_str(), buf.data(), buf.size()); + auto r = portable_listxattr(path.c_str(), buf.data(), buf.size()); EXPECT_LT(r, 0) << runner.cmdline(); EXPECT_EQ(ERANGE, errno) << runner.cmdline(); - r = ::listxattr(path.c_str(), buf.data(), 0); - EXPECT_GT(r, 0) << runner.cmdline(); + r = portable_listxattr(path.c_str(), nullptr, 0); + ASSERT_GT(r, 0) << runner.cmdline() << ::strerror(errno); buf.resize(r); - r = ::listxattr(path.c_str(), buf.data(), buf.size()); + r = portable_listxattr(path.c_str(), buf.data(), buf.size()); EXPECT_GT(r, 0) << runner.cmdline(); EXPECT_EQ(ref, buf) << runner.cmdline(); buf.resize(1); - r = ::getxattr(path.c_str(), kInodeInfoXattr, buf.data(), buf.size()); + r = portable_getxattr(path.c_str(), kInodeInfoXattr, buf.data(), + buf.size()); EXPECT_LT(r, 0) << runner.cmdline(); EXPECT_EQ(ERANGE, errno) << runner.cmdline(); - r = ::getxattr(path.c_str(), kInodeInfoXattr, buf.data(), 0); - EXPECT_GT(r, 0) << runner.cmdline(); + r = portable_getxattr(path.c_str(), kInodeInfoXattr, nullptr, 0); + ASSERT_GT(r, 0) << runner.cmdline() << ::strerror(errno); buf.resize(r); - r = ::getxattr(path.c_str(), kInodeInfoXattr, buf.data(), buf.size()); + r = portable_getxattr(path.c_str(), kInodeInfoXattr, buf.data(), + buf.size()); EXPECT_GT(r, 0) << runner.cmdline(); auto info = folly::parseJson(buf); @@ -1045,18 +1111,25 @@ TEST_P(tools_test, end_to_end) { EXPECT_EQ(cdr.symlinks.size(), 2) << cdr; } +#define EXPECT_EC_IMPL(ec, cat, val) \ + EXPECT_TRUE(ec) << runner.cmdline(); \ + EXPECT_EQ(cat, (ec).category()) << runner.cmdline(); \ + EXPECT_EQ(val, (ec).value()) << runner.cmdline() << ": " << (ec).message() + #ifdef _WIN32 -#define EXPECT_EC_UNIX_WIN(ec, unix, windows) \ - EXPECT_TRUE(ec) << runner.cmdline(); \ - EXPECT_EQ(std::system_category(), (ec).category()) << runner.cmdline(); \ - EXPECT_EQ(windows, (ec).value()) << runner.cmdline() << ": " << (ec).message() +#define EXPECT_EC_UNIX_MAC_WIN(ec, unix, mac, windows) \ + EXPECT_EC_IMPL(ec, std::system_category(), windows) +#elif defined(__APPLE__) +#define EXPECT_EC_UNIX_MAC_WIN(ec, unix, mac, windows) \ + EXPECT_EC_IMPL(ec, std::generic_category(), mac) #else -#define EXPECT_EC_UNIX_WIN(ec, unix, windows) \ - EXPECT_TRUE(ec) << runner.cmdline(); \ - EXPECT_EQ(std::generic_category(), (ec).category()) << runner.cmdline(); \ - EXPECT_EQ(unix, (ec).value()) << runner.cmdline() << ": " << (ec).message() +#define EXPECT_EC_UNIX_MAC_WIN(ec, unix, mac, windows) \ + EXPECT_EC_IMPL(ec, std::generic_category(), unix) #endif +#define EXPECT_EC_UNIX_WIN(ec, unix, windows) \ + EXPECT_EC_UNIX_MAC_WIN(ec, unix, unix, windows) + TEST_P(tools_test, mutating_and_error_ops) { auto mode = GetParam(); @@ -1136,7 +1209,7 @@ TEST_P(tools_test, mutating_and_error_ops) { { std::error_code ec; fs::rename(file, name_inside_fs, ec); - EXPECT_EC_UNIX_WIN(ec, ENOSYS, ERROR_ACCESS_DENIED); + EXPECT_EC_UNIX_MAC_WIN(ec, ENOSYS, EACCES, ERROR_ACCESS_DENIED); } { @@ -1148,7 +1221,7 @@ TEST_P(tools_test, mutating_and_error_ops) { { std::error_code ec; fs::rename(empty_dir, name_inside_fs, ec); - EXPECT_EC_UNIX_WIN(ec, ENOSYS, ERROR_ACCESS_DENIED); + EXPECT_EC_UNIX_MAC_WIN(ec, ENOSYS, EACCES, ERROR_ACCESS_DENIED); } { @@ -1162,7 +1235,7 @@ TEST_P(tools_test, mutating_and_error_ops) { { std::error_code ec; fs::create_hard_link(file, name_inside_fs, ec); - EXPECT_EC_UNIX_WIN(ec, ENOSYS, ERROR_ACCESS_DENIED); + EXPECT_EC_UNIX_MAC_WIN(ec, ENOSYS, EACCES, ERROR_ACCESS_DENIED); } { @@ -1176,7 +1249,7 @@ TEST_P(tools_test, mutating_and_error_ops) { { std::error_code ec; fs::create_symlink(file, name_inside_fs, ec); - EXPECT_EC_UNIX_WIN(ec, ENOSYS, ERROR_ACCESS_DENIED); + EXPECT_EC_UNIX_MAC_WIN(ec, ENOSYS, EACCES, ERROR_ACCESS_DENIED); } { @@ -1189,7 +1262,7 @@ TEST_P(tools_test, mutating_and_error_ops) { { std::error_code ec; fs::create_directory_symlink(empty_dir, name_inside_fs, ec); - EXPECT_EC_UNIX_WIN(ec, ENOSYS, ERROR_ACCESS_DENIED); + EXPECT_EC_UNIX_MAC_WIN(ec, ENOSYS, EACCES, ERROR_ACCESS_DENIED); } { @@ -1212,7 +1285,7 @@ TEST_P(tools_test, mutating_and_error_ops) { { std::error_code ec; fs::create_directory(name_inside_fs, ec); - EXPECT_EC_UNIX_WIN(ec, ENOSYS, ERROR_ACCESS_DENIED); + EXPECT_EC_UNIX_MAC_WIN(ec, ENOSYS, EACCES, ERROR_ACCESS_DENIED); } // read directory as file (non-mutating)