feat: support building on OSX (should address gh #132)

This commit is contained in:
Marcus Holland-Moritz 2024-01-23 15:33:07 +01:00
parent b5fc39c496
commit a97161e6da
13 changed files with 320 additions and 51 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
.DS_Store
/@*
/*.*fs
/tmp/

View File

@ -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 $<TARGET_FILE:${tgt}>)
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

View File

@ -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

45
cmake/osx.sh Executable file
View File

@ -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

View File

@ -93,7 +93,8 @@ class cached_block_ final : public cached_block {
bool any_pages_swapped_out(std::vector<uint8_t>& 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<uint8_t*>(data_.data()), data_.size(),

View File

@ -328,10 +328,25 @@ void categorizer_manager_<LoggerPolicy>::set_metadata_requirements(
template <typename LoggerPolicy>
bool categorizer_manager_<LoggerPolicy>::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);

View File

@ -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;
}

View File

@ -716,7 +716,8 @@ void inode_manager_<LoggerPolicy>::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);
});

View File

@ -27,6 +27,11 @@
#include <boost/process/search_path.hpp>
#ifdef __APPLE__
#include <mach/mach.h>
#include <mach/thread_info.h>
#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<thread_info_t>(&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;

View File

@ -816,13 +816,21 @@ int op_statfs(char const* path, native_statvfs* st) {
#if DWARFS_FUSE_LOWLEVEL
template <typename LoggerPolicy>
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();

View File

@ -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) {

View File

@ -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() ? "<normal>" : "";
@ -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<fs::path, std::string> actual_files;
std::unordered_map<fs::path, std::string, fs_path_hash> 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<fs::path, std::string> original_files(files.begin(),
files.end());
std::unordered_map<fs::path, std::string, fs_path_hash> original_files(
files.begin(), files.end());
EXPECT_EQ(0, surprisingly_missing.size());

View File

@ -38,7 +38,11 @@
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#ifdef __APPLE__
#include <sys/mount.h>
#else
#include <sys/vfs.h>
#endif
#include <sys/xattr.h>
#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<char, 32> 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>(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<std::string> 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<std::string> 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>(args)...)) {
mountpoint, options,
std::forward<Args>(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>(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<subprocess> 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)