diff options
author | Martin Pärtel <martin.partel@gmail.com> | 2025-06-08 14:45:34 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-06-08 14:45:34 +0300 |
commit | ba548d828d8680ebe329ea1bb2d069ec881066a5 (patch) | |
tree | 27af010adc569eda1d6d9c02326c10bbe40d715b | |
parent | a276dbe66faaf7af02d967a648f7598589bf5c5b (diff) | |
parent | 2dce69c4dea0bcb9e98c2965e6ac6b25402fcae9 (diff) | |
download | bindfs-ba548d828d8680ebe329ea1bb2d069ec881066a5.tar.gz |
Merge pull request #161 from mpartel/fix-gh-actions
Fix GitHub actions
-rw-r--r-- | .github/workflows/tests.yml | 94 | ||||
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | README.md | 8 | ||||
-rw-r--r-- | configure.ac | 22 | ||||
-rw-r--r-- | src/bindfs.c | 25 | ||||
-rw-r--r-- | tests/common.rb | 32 | ||||
-rwxr-xr-x | tests/test_bindfs.rb | 25 |
7 files changed, 112 insertions, 95 deletions
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3c16512..9673c5d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,7 +15,7 @@ permissions: contents: read jobs: - linux: + tests: name: Build (${{ matrix.cc }} and ${{ matrix.fuse_package }} on ${{ matrix.runs-on }}) runs-on: ${{ matrix.runs-on }} strategy: @@ -26,42 +26,36 @@ jobs: - cc: gcc cxx: g++ clang_major_version: null - clang_repo_suffix: null runs-on: ubuntu-22.04 fuse_package: libfuse-dev # FUSE 3 - cc: gcc cxx: g++ clang_major_version: null - clang_repo_suffix: null runs-on: ubuntu-22.04 fuse_package: libfuse3-dev - cc: clang-17 cxx: clang++-17 clang_major_version: 17 - clang_repo_suffix: -17 runs-on: ubuntu-22.04 fuse_package: libfuse3-dev - cc: clang-18 cxx: clang++-18 clang_major_version: 18 - clang_repo_suffix: -18 runs-on: ubuntu-22.04 fuse_package: libfuse3-dev - # fuse-t - - cc: gcc-13 - cxx: g++-13 + # fuse-t, gcc + - cc: gcc + cxx: g++ + clang_major_version: null + runs-on: macos-15 + fuse_package: fuse-t + # fuse-t, clang + - cc: clang + cxx: clang++ clang_major_version: null - clang_repo_suffix: null - runs-on: macos-13 + runs-on: macos-15 fuse_package: fuse-t - # macFUSE - - cc: clang-15 - cxx: clang++-15 - clang_major_version: 15 - clang_repo_suffix: null - runs-on: macos-13 - fuse_package: macfuse steps: - name: Add Clang/LLVM repositories if: "${{ runner.os == 'Linux' && contains(matrix.cxx, 'clang') }}" @@ -69,7 +63,7 @@ jobs: set -x source /etc/os-release wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add - - sudo add-apt-repository "deb http://apt.llvm.org/${UBUNTU_CODENAME}/ llvm-toolchain-${UBUNTU_CODENAME}${{ matrix.clang_repo_suffix }} main" + sudo add-apt-repository "deb http://apt.llvm.org/${UBUNTU_CODENAME}/ llvm-toolchain-${UBUNTU_CODENAME}-${{ matrix.clang_major_version }} main" - name: Install build dependencies if: "${{ runner.os == 'Linux' }}" @@ -88,9 +82,11 @@ jobs: run: |- set -x - if [[ ${{ matrix.fuse_package }} = macfuse ]]; then - brew install --cask macfuse - elif [[ ${{ matrix.fuse_package }} = fuse-t ]]; then + # Installing MacFUSE requires visiting the MacOS recovery environment, + # so testing it in CI seems to be hopeless. Therefore we only test with fuse-t. + # Apparently the best security is making it so inconvenient that you can't run the thing at all. + if [[ ${{ matrix.fuse_package }} = fuse-t ]]; then + sudo mkdir -p /usr/local/include # https://github.com/macos-fuse-t/fuse-t/issues/77 brew tap macos-fuse-t/homebrew-cask brew install fuse-t else @@ -110,7 +106,7 @@ jobs: clang-${{ matrix.clang_major_version }} - name: Add versioned aliases for Clang ${{ matrix.clang_major_version }} - if: "${{ runner.os == 'macOS' && contains(matrix.cxx, 'clang') }}" + if: "${{ runner.os == 'macOS' && matrix.clang_major_version != null }}" run: |- set -x sudo ln -s "$(brew --prefix llvm@${{ matrix.clang_major_version }})"/bin/clang /usr/local/bin/clang-${{ matrix.clang_major_version }} @@ -135,30 +131,19 @@ jobs: - name: 'Build' run: |- set -x - make -j$(nproc || sysctl -n hw.logicalcpu) VERBOSE=1 + cpu_threads="$(nproc 2>/dev/null || sysctl -n hw.logicalcpu || echo 4)" + make "-j${cpu_threads}" VERBOSE=1 - name: 'Test as non-root' run: |- - if [[ ${{ runner.os }} = macOS ]]; then - ignore_errors=true # while unfixed - else - ignore_errors=false - fi - set -x whoami - make check || ${ignore_errors} + make check - name: 'Test as root' run: |- - if [[ ${{ runner.os }} = macOS ]]; then - ignore_errors=true # while unfixed - else - ignore_errors=false - fi - set -x - sudo make check || ${ignore_errors} + sudo make check - name: 'Install' run: |- @@ -168,52 +153,43 @@ jobs: vagrant: name: Run Vagrant tests - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: # TODO: automatically check that this list is up-to-date box: - - centos8 - debian10 - debian11 - freebsd14 - - ubuntu1804 + - rockylinux9 - ubuntu2004 - ubuntu2204 + - ubuntu2404 steps: - name: Install dependencies run: |- set -x - for FILE in /etc/apt/sources.list /etc/apt/sources.list.d/*; do - if [[ -e "${FILE}" ]]; then - sudo sed -i 's/# deb-src /deb-src /' "${FILE}" - fi - done + sudo sed -i 's/Types: deb/Types: deb deb-src/' /etc/apt/sources.list.d/ubuntu.sources # The following is based on these instructions: # https://developer.hashicorp.com/vagrant/install?product_intent=vagrant # https://vagrant-libvirt.github.io/vagrant-libvirt/installation.html - wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg - echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list + wget -O - https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg + echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list sudo apt-get update - sudo apt-get build-dep vagrant ruby-libvirt + sudo apt-get build-dep ruby-libvirt sudo apt-get install -y vagrant \ qemu-system-x86 \ autoconf automake libtool pkg-config \ - qemu libvirt-daemon-system ebtables libguestfs-tools \ - libxslt-dev libxml2-dev zlib1g-dev ruby-dev - vagrant plugin install vagrant-libvirt - sudo adduser "${USER}" libvirt + libvirt-daemon-system ebtables libguestfs-tools \ + libxslt-dev libxml2-dev zlib1g-dev ruby ruby-dev + sudo vagrant plugin install vagrant-libvirt - name: Checkout Git branch uses: actions/checkout@v4 - - name: 'Generate build files with ./autogen.sh' - run: |- - ./autogen.sh - - name: Check for KVM run: |- # This check is probably obsolete now: https://github.com/actions/runner-images/discussions/7191 @@ -227,6 +203,8 @@ jobs: env: VAGRANT_DEFAULT_PROVIDER: libvirt run: |- - # sudo needed because the current shell is not yet in group "libvirt" + # We use vagrant with sudo because it has proven stupendously difficult to + # get the current shell into group "libvirt" in a way that Vagrant is happy with. # https://github.com/actions/runner-images/discussions/5981 - sudo -E -s -u "${USER}" vagrant/test.rb --print-logs ${{ matrix.box }} + sudo chmod a+rx /root # Give libvirt access to /root/.vagrant.d which is about to be created + sudo ruby vagrant/test.rb --print-logs ${{ matrix.box }} @@ -46,6 +46,7 @@ tests/internals/test_internals tests/internals/test_rate_limiter tests/internals/*.log tests/internals/*.trs +tests/tmp_test_bindfs # Vagrant @@ -17,6 +17,10 @@ Some things bindfs can be used for: Non-root users can use almost all features, but most interesting use-cases need `user_allow_other` to be defined in `/etc/fuse.conf`. +bindfs is developed and tested primarily on Linux with [FUSE 3](https://github.com/libfuse/libfuse). + +Support for Linux with FUSE 2, for MacOS with [fuse-t](https://www.fuse-t.org/) and for FreeBSD with [fuse4bsd](http://www.freshports.org/sysutils/fusefs-kmod/) is best-effort. +[MacFUSE](https://macfuse.github.io/) might work, but is not properly supported, since it's impossible to test it without access to a physical Mac. Volunteer maintenance of MacFUSE support is welcome. ## Installation ## @@ -25,7 +29,9 @@ FUSE 3 is supported. When using FUSE 3, libfuse 3.10.2 or newer is recommended to avoid a [bug with readdir](https://github.com/libfuse/libfuse/issues/583), though it only seems to affect a few applications. -To compile from source on Linux, first `apt install build-essential pkg-config libfuse3-dev` (or `libfuse-dev` on older systems). On MacOS, install XCode (and let it install Developer Tools), [pkg-config](https://formulae.brew.sh/formula/pkg-config#default) and either [MacFuse](https://osxfuse.github.io/) or [fuse-t](https://www.fuse-t.org/). +To compile from source on Linux, first `apt install build-essential pkg-config libfuse3-dev` (or `libfuse-dev` on older systems). +On MacOS, install XCode (and let it install Developer Tools), [pkg-config](https://formulae.brew.sh/formula/pkg-config#default) +and [fuse-t](https://www.fuse-t.org/) ([MacFuse](https://osxfuse.github.io/) support is highly best-effort). Download a [release](https://bindfs.org/downloads/) or clone this repository. diff --git a/configure.ac b/configure.ac index f20b632..dde224e 100644 --- a/configure.ac +++ b/configure.ac @@ -5,7 +5,7 @@ AC_CONFIG_HEADERS([config.h]) AC_PROG_CC AC_LANG(C) -AC_PROG_LIBTOOL +LT_INIT # --enable and --with options AC_ARG_ENABLE([debug-output], @@ -35,9 +35,23 @@ AM_CONDITIONAL([INSTALL_MACOS_FS_LINK], [case $build_os in darwin* ) test x"$ena # _BSD_SOURCE is for stat() nanosecond precision and lutimes(). # _DEFAULT_SOURCE is the new non-deprecated version of _BSD_SOURCE. # _DARWIN_BETTER_REALPATH fixes MacOS realpath() broken around Catalina (#83). -my_CPPFLAGS="-D_REENTRANT -D_FILE_OFFSET_BITS=64 -D_XOPEN_SOURCE=700 -D__BSD_VISIBLE=1 -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_DARWIN_BETTER_REALPATH" - -my_CFLAGS="-std=c99 -Wall -Wextra -Wpedantic -fno-common" +# _DARWIN_C_SOURCE for DT_LNK (#163) +my_CPPFLAGS="-D_REENTRANT -D_FILE_OFFSET_BITS=64 -D_XOPEN_SOURCE=700 -D__BSD_VISIBLE=1 -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_DARWIN_BETTER_REALPATH -D_DARWIN_C_SOURCE" + +dnl libfuse >=3.17 started requiring gnu11 from the C compiler +AS_IF([test "x$with_fuse2" == "xyes"], [ + my_std=c99 +], [ + my_std=gnu11 +]) + +my_CFLAGS="-std=${my_std} -Wall -Wextra -Wpedantic -fno-common" +case $build_os in + darwin* ) + # For MacFuse 5.x (2025-06-08) + my_CFLAGS="${my_CFLAGS} -Wno-language-extension-token -Wno-dollar-in-identifier-extension -DFUSE_DARWIN_ENABLE_EXTENSIONS=0" + ;; +esac my_LDFLAGS="-pthread" AC_SUBST([my_CPPFLAGS]) AC_SUBST([my_CFLAGS]) diff --git a/src/bindfs.c b/src/bindfs.c index 08defc6..e0e4c1c 100644 --- a/src/bindfs.c +++ b/src/bindfs.c @@ -101,18 +101,13 @@ /* Apple Structs */ #ifdef __APPLE__ #include <sys/param.h> +#include <sys/dirent.h> #define G_PREFIX "org" #define G_KAUTH_FILESEC_XATTR G_PREFIX ".apple.system.Security" #define A_PREFIX "com" #define A_KAUTH_FILESEC_XATTR A_PREFIX ".apple.system.Security" #define XATTR_APPLE_PREFIX "com.apple." -// Yes, Apple asks us to copy/paste these -.- -#define LOCK_SH 1 /* shared lock */ -#define LOCK_EX 2 /* exclusive lock */ -#define LOCK_NB 4 /* don't block when locking */ -#define LOCK_UN 8 /* unlock */ -int flock(int fd, int operation); #endif /* We pessimistically assume signed uid_t and gid_t in our overflow checks, @@ -331,7 +326,7 @@ static int bindfs_ioctl(const char *path, int cmd, void *arg, void *data); #endif static int bindfs_statfs(const char *path, struct statvfs *stbuf); -#if __APPLE__ +#ifdef HAVE_FUSE_T static int bindfs_statfs_x(const char *path, struct statfs *stbuf); #endif static int bindfs_release(const char *path, struct fuse_file_info *fi); @@ -1533,7 +1528,7 @@ static int bindfs_statfs(const char *path, struct statvfs *stbuf) return 0; } -#if __APPLE__ +#ifdef HAVE_FUSE_T static int bindfs_statfs_x(const char *path, struct statfs *stbuf) { int res; @@ -1586,7 +1581,7 @@ static int bindfs_fsync(const char *path, int isdatasync, understanding from the osxfuse example file: https://github.com/osxfuse/fuse/blob/master/example/fusexmp_fh.c */ -#ifdef __APPLE__ +#ifdef HAVE_FUSE_T static int bindfs_setxattr(const char *path, const char *name, const char *value, size_t size, int flags, uint32_t position) #else @@ -1606,7 +1601,7 @@ static int bindfs_setxattr(const char *path, const char *name, const char *value if (real_path == NULL) return -errno; -#if defined(__APPLE__) +#if HAVE_FUSE_T if (!strncmp(name, XATTR_APPLE_PREFIX, sizeof(XATTR_APPLE_PREFIX) - 1)) { flags &= ~(XATTR_NOSECURITY); } @@ -1631,7 +1626,7 @@ static int bindfs_setxattr(const char *path, const char *name, const char *value return 0; } -#ifdef __APPLE__ +#ifdef HAVE_FUSE_T static int bindfs_getxattr(const char *path, const char *name, char *value, size_t size, uint32_t position) #else @@ -1648,7 +1643,7 @@ static int bindfs_getxattr(const char *path, const char *name, char *value, if (real_path == NULL) return -errno; -#if defined(__APPLE__) +#if HAVE_FUSE_T if (strcmp(name, A_KAUTH_FILESEC_XATTR) == 0) { char new_name[MAXPATHLEN]; memcpy(new_name, A_KAUTH_FILESEC_XATTR, sizeof(A_KAUTH_FILESEC_XATTR)); @@ -1790,7 +1785,7 @@ static struct fuse_operations bindfs_oper = { .ioctl = bindfs_ioctl, #endif .statfs = bindfs_statfs, -#ifdef __APPLE__ +#ifdef HAVE_FUSE_T .statfs_x = bindfs_statfs_x, #endif .release = bindfs_release, @@ -2883,6 +2878,10 @@ int main(int argc, char *argv[]) fuse_opt_add_arg(&args, "-ouse_ino"); #endif +#ifdef HAVE_FUSE_T + fuse_opt_add_arg(&args, "-onoattrcache"); +#endif + /* Show the source dir in the first field on /etc/mtab, to be consistent with "real" filesystems. diff --git a/tests/common.rb b/tests/common.rb index 69f2eb2..a0c4bfc 100644 --- a/tests/common.rb +++ b/tests/common.rb @@ -29,6 +29,11 @@ File.umask 0022 EXECUTABLE_PATH = '../src/bindfs' TESTDIR_NAME = 'tmp_test_bindfs' +$fuse_t = Proc.new do + system("pkg-config --exists fuse-t") + $?.success? +end.call + # If set to an array of test names, only those will be run $only_these_tests = nil @@ -48,6 +53,10 @@ def fail!(msg, error = nil, options = {}) fail(msg, error, options) end +def sh!(cmd) + raise Exception.new("Command failed: #{cmd}") unless system(cmd) +end + def wait_for(options = {}, &condition) options = { :initial_sleep => 0.01, @@ -168,8 +177,13 @@ def testenv(bindfs_args, options = {}, &block) end if !$?.success? - fail("exit status: #{$?}") - testcase_ok = false + # Known issue with fuse-t: unmount kills bindfs with "short read on fuse device" / SIGPIPE. + # No idea why. + ignore = $?.signaled? && $?.termsig == Signal.list['PIPE'] && $fuse_t + unless ignore + fail("exit status: #{$?}") + testcase_ok = false + end end begin @@ -191,12 +205,16 @@ end # Like testenv but skips the test if not running as root def root_testenv(bindfs_args, options = {}, &block) - if Process.uid == 0 - testenv(bindfs_args, options, &block) - else + if Process.uid != 0 puts "--- #{bindfs_args} ---" puts "[ #{bindfs_args} ]" puts "SKIP (requires root)" + elsif $fuse_t + puts "--- #{bindfs_args} ---" + puts "[ #{bindfs_args} ]" + puts "SKIP (fuse-t - several known issues, contributions to debugging them welcome)" + else + testenv(bindfs_args, options, &block) end end @@ -213,7 +231,9 @@ def nonroot_testenv(bindfs_args, options = {}, &block) end def umount_cmd - if !`which fusermount3`.strip.empty? + if $fuse_t + 'diskutil unmount force' + elsif !`which fusermount3`.strip.empty? 'fusermount3 -uz' elsif !`which fusermount`.strip.empty? 'fusermount -uz' diff --git a/tests/test_bindfs.rb b/tests/test_bindfs.rb index d5af03b..c068b28 100755 --- a/tests/test_bindfs.rb +++ b/tests/test_bindfs.rb @@ -32,17 +32,14 @@ include Errno $have_fuse_3 = Proc.new do system("pkg-config --exists fuse3") - $?.success? || Proc.new do - system("pkg-config --exists fuse-t") - $?.success? - end.call + $?.success? end.call $have_fuse_3_readdir_bug = $have_fuse_3 && Proc.new do system("pkg-config --max-version=3.10.1 fuse3") $?.success? end.call -$have_fuse_29 = !$have_fuse_3 && Proc.new do +$have_fuse_29 = !$have_fuse_3 && !$fuse_t && Proc.new do v = `pkg-config --modversion fuse`.split('.') raise "failed to get FUSE version with pkg-config" if v.size < 2 v = v.map(&:to_i) @@ -120,9 +117,9 @@ end root_testenv("", :title => "--create-as-user should be default for root") do chmod(0777, 'src') - `sudo -u nobody -g #{nobody_group} touch mnt/file` - `sudo -u nobody -g #{nobody_group} mkdir mnt/dir` - `sudo -u nobody -g #{nobody_group} ln -sf /tmp/foo mnt/lnk` + sh!("sudo -u nobody -g #{nobody_group} touch mnt/file") + sh!("sudo -u nobody -g #{nobody_group} mkdir mnt/dir") + sh!("sudo -u nobody -g #{nobody_group} ln -sf /tmp/foo mnt/lnk") assert { File.stat('mnt/file').uid == nobody_uid } assert { File.stat('mnt/file').gid == nobody_gid } @@ -307,7 +304,8 @@ testenv("--chmod-deny --chmod-allow-x") do assert_exception(EPERM) { chmod(0777, 'mnt/file') } assert_exception(EPERM) { chmod(0000, 'mnt/file') } - if `uname`.strip != 'FreeBSD' # FreeBSD doesn't let us set the sticky bit on files + # FreeBSD and apparently Apple doesn't let us set the sticky bit on files + unless ['FreeBSD', 'Darwin'].include?(`uname`.strip) assert_exception(EPERM) { chmod(01700, 'mnt/file') } # sticky bit end @@ -475,11 +473,11 @@ root_testenv("--uid-offset=2 --gid-offset=20", :title => "file creation with --u end # This test requires user 1k to actually exist so we can sudo to it -if user_1k +if user_1k && `uname`.strip != 'Darwin' # Don't feel like debugging this on MacOS root_testenv("--uid-offset=-2 --gid-offset=-20", :title => "file creation with negative --uid-offset and --gid-offset") do chown(user_1k, user_1k_group, 'src') chmod(0777, 'src') - `sudo -u #{user_1k} -g #{user_1k_group} touch mnt/file` + sh!("sudo -u #{user_1k} -g #{user_1k_group} touch mnt/file") assert { File.stat('src/file').uid == 1002 } assert { File.stat('mnt/file').uid == 1000 } @@ -757,6 +755,7 @@ testenv("--resolve-symlinks", :title => "resolving broken symlinks") do end # Issue #28 reproduction attempt. +# Observation (2025-06-08): Flaky on fuse-t without noattrcache. Enabled by default by bindfs since. testenv("", :title => "many files in a directory") do mkdir('src/dir') expected_entries = ['.', '..'] @@ -939,13 +938,13 @@ if `uname`.strip == 'Linux' end # Issue 94 -if `uname`.strip != 'FreeBSD' # -o fsname is not supported on FreeBSD +unless ['FreeBSD', 'Darwin'].include?(`uname`.strip) # -o fsname is not supported on FreeBSD or fuse-t testenv("-o fsname=_bindfs_test_123_", :title => "fsname") do assert { `mount` =~ /^_bindfs_test_123_\s+on\s+/m } end end -if `uname`.strip != 'FreeBSD' # -o dev is not supported on FreeBSD +unless ['FreeBSD', 'Darwin'].include?(`uname`.strip) # -o dev is not supported on FreeBSD or fuse-t root_testenv("-odev") do system("mknod mnt/zero c 1 5") data = File.read("mnt/zero", 3) |