Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package unionfs-fuse for openSUSE:Factory checked in at 2026-10-18 19:26:46 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/unionfs-fuse (Old) and /work/SRC/openSUSE:Factory/.unionfs-fuse.new.3698736 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "unionfs-fuse" Sun Oct 18 19:26:46 2026 rev:24 rq:1385992 version:3.8 Changes: -------- --- /work/SRC/openSUSE:Factory/unionfs-fuse/unionfs-fuse.changes 2025-12-31 10:48:39.117490183 +0100 +++ /work/SRC/openSUSE:Factory/.unionfs-fuse.new.3698736/unionfs-fuse.changes 2026-10-18 19:27:21.771898014 +0200 @@ -1,0 +2,12 @@ +Sun Oct 18 05:33:56 UTC 2026 - Martin Pluskal <[email protected]> + +- Update to 3.8: + * fix data corruption when copying files larger than 8 MiB + concurrently in cow mode + * fix "bad address" error when setting a debug file with + unionfsctl -p + * fix creating unix sockets on macos + * mount.unionfs helper no longer requires bash and accepts + multiple -o options + +------------------------------------------------------------------- Old: ---- v3.7.tar.gz New: ---- v3.8.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ unionfs-fuse.spec ++++++ --- /var/tmp/diff_new_pack.wLp23q/_old 2026-10-18 19:27:22.574931659 +0200 +++ /var/tmp/diff_new_pack.wLp23q/_new 2026-10-18 19:27:22.575931701 +0200 @@ -1,7 +1,7 @@ # # spec file for package unionfs-fuse # -# Copyright (c) 2025 SUSE LLC +# Copyright (c) 2026 SUSE LLC and contributors # Copyright (c) 2025 Andreas Stieger <[email protected]> # # All modifications and additions to the file contributed by third parties @@ -18,7 +18,7 @@ Name: unionfs-fuse -Version: 3.7 +Version: 3.8 Release: 0 Summary: Userspace Unionfs File System License: BSD-3-Clause @@ -28,12 +28,8 @@ BuildRequires: cmake BuildRequires: pkgconfig BuildRequires: pkgconfig(fuse3) -Requires: fuse3 -%if 0%{?suse_version} >= 1600 BuildRequires: pkgconfig(libattr) -%else -BuildRequires: libattr-devel -%endif +Requires: fuse3 %description unionfs-fuse overlays several directory into one single mount point ++++++ v3.7.tar.gz -> v3.8.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/unionfs-fuse-3.7/.github/workflows/cmake.yml new/unionfs-fuse-3.8/.github/workflows/cmake.yml --- old/unionfs-fuse-3.7/.github/workflows/cmake.yml 2025-09-14 19:29:51.000000000 +0200 +++ new/unionfs-fuse-3.8/.github/workflows/cmake.yml 2026-10-10 20:53:12.000000000 +0200 @@ -30,9 +30,9 @@ clang_major_version: 18 runs-on: ubuntu-24.04 # macFUSE on macOS - - cc: gcc-12 + - cc: gcc-14 clang_major_version: null - runs-on: macos-13 + runs-on: macos-14 - cc: clang-15 clang_major_version: 15 runs-on: macos-14 @@ -60,7 +60,7 @@ brew install --cask macfuse - name: Checkout Git branch - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: 'Configure with CMake' run: |- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/unionfs-fuse-3.7/.github/workflows/makefile.yml new/unionfs-fuse-3.8/.github/workflows/makefile.yml --- old/unionfs-fuse-3.7/.github/workflows/makefile.yml 2025-09-14 19:29:51.000000000 +0200 +++ new/unionfs-fuse-3.8/.github/workflows/makefile.yml 2026-10-10 20:53:12.000000000 +0200 @@ -43,7 +43,7 @@ python3-pytest - name: Checkout Git branch - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: 'Build and test (without coverage)' run: |- diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/unionfs-fuse-3.7/CREDITS new/unionfs-fuse-3.8/CREDITS --- old/unionfs-fuse-3.7/CREDITS 2025-09-14 19:29:51.000000000 +0200 +++ new/unionfs-fuse-3.8/CREDITS 2026-10-10 20:53:12.000000000 +0200 @@ -41,6 +41,7 @@ xabrouck (github) Timothy C. Quinn <[email protected]> Benjy Wiener <[email protected]> +Samuel Duann <[email protected]> Hashtable routines taken from: Christopher Clark <[email protected]> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/unionfs-fuse-3.7/LICENSE new/unionfs-fuse-3.8/LICENSE --- old/unionfs-fuse-3.7/LICENSE 2025-09-14 19:29:51.000000000 +0200 +++ new/unionfs-fuse-3.8/LICENSE 2026-10-10 20:53:12.000000000 +0200 @@ -1,6 +1,6 @@ Unionfs-fuse is provided under the terms of the new BSD license. -Copyright (c) 2025, Radek Podgorny, Bernd Schubert +Copyright (c) 2026, Radek Podgorny, Bernd Schubert All rights reserved. Redistribution and use in source and binary forms, with or without diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/unionfs-fuse-3.7/NEWS new/unionfs-fuse-3.8/NEWS --- old/unionfs-fuse-3.7/NEWS 2025-09-14 19:29:51.000000000 +0200 +++ new/unionfs-fuse-3.8/NEWS 2026-10-10 20:53:12.000000000 +0200 @@ -1,3 +1,9 @@ +3.8 +- fix data corruption when copying files larger than 8 MiB concurrently in cow mode +- fix "bad address" error when setting a debug file with unionfsctl -p +- fix creating unix sockets on macos +- mount.unionfs helper no longer requires bash and accepts multiple -o options + 3.7 - fix chmod on symlink itself diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/unionfs-fuse-3.7/man/unionfs.8 new/unionfs-fuse-3.8/man/unionfs.8 --- old/unionfs-fuse-3.7/man/unionfs.8 2025-09-14 19:29:51.000000000 +0200 +++ new/unionfs-fuse-3.8/man/unionfs.8 2026-10-10 20:53:12.000000000 +0200 @@ -7,7 +7,7 @@ .ft R .fi .. -.TH "unionfs" "8" "2025" "unionfs-fuse 3.7" "" +.TH "unionfs" "8" "2026" "unionfs-fuse 3.8" "" .SH "NAME" unionfs\-fuse \- A userspace unionfs implementation .SH "SYNOPSIS" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/unionfs-fuse-3.7/mount.unionfs new/unionfs-fuse-3.8/mount.unionfs --- old/unionfs-fuse-3.7/mount.unionfs 2025-09-14 19:29:51.000000000 +0200 +++ new/unionfs-fuse-3.8/mount.unionfs 2026-10-10 20:53:12.000000000 +0200 @@ -1,9 +1,27 @@ -#!/bin/bash - -label=$1 -mpoint=$2 -shift 3 -dirs=${@##*,dirs=} -dirs=${dirs/,/:} -mount.fuse "unionfs#$dirs" "$mpoint" -o ${@%%,dirs=*} - +#!/bin/sh +set -f +print_usage() { echo "usage: ${0##*/} <ignored> destination" \ + "-o dirs=UNION_DIRS[,opt[,opts...]]"; } +first_arg=y first_pos=y is_opt='' dirs='' +IFS=; for arg in "$@"; do + [ -z "$first_arg" ] || { first_arg=; set --; } + if [ -n "$is_opt" ]; then + is_opt='' new_arg='' + IFS=,; for part in $arg; do + case "$part" in + dirs=*) dirs="${dirs:+$dirs:}${part#dirs=}" ;; + *) new_arg="${new_arg:+$new_arg,}$part" ;; + esac + done + [ -n "$new_arg" ] || continue + set -- "$@" -o "$new_arg" + elif [ "-o" = "$arg" ]; then + is_opt=y + elif [ -n "$first_pos" ] && [ "${arg#-}" = "$arg" ]; then + first_pos='' + else + set -- "$@" "$arg" + fi +done +[ -n "$dirs" ] && [ -z "$is_opt" ] || { print_usage >&2; exit 1; } +exec mount.fuse "unionfs#$dirs" "$@" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/unionfs-fuse-3.7/src/cow_utils.c new/unionfs-fuse-3.8/src/cow_utils.c --- old/unionfs-fuse-3.7/src/cow_utils.c 2025-09-14 19:29:51.000000000 +0200 +++ new/unionfs-fuse-3.8/src/cow_utils.c 2026-10-10 20:53:12.000000000 +0200 @@ -149,7 +149,6 @@ { DBG("from %s to %s\n", cow->from_path, cow->to_path); - static char buf[MAXBSIZE]; struct stat to_stat, *fs; int from_fd, rcount, to_fd, wcount; int rval = 0; @@ -199,6 +198,14 @@ } else #endif { + char *buf = malloc(MAXBSIZE); + if (!buf) { + USYSLOG(LOG_WARNING, "malloc failed: %s", cow->from_path); + (void)close(from_fd); + (void)close(to_fd); + RETURN(1); + } + while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { wcount = write(to_fd, buf, rcount); if (rcount != wcount || wcount == -1) { @@ -211,6 +218,8 @@ USYSLOG(LOG_WARNING, "copy failed: %s", cow->from_path); rval = 1; } + + free(buf); } if (rval == 1) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/unionfs-fuse-3.7/src/fuse_ops.c new/unionfs-fuse-3.8/src/fuse_ops.c --- old/unionfs-fuse-3.7/src/fuse_ops.c 2025-09-14 19:29:51.000000000 +0200 +++ new/unionfs-fuse-3.8/src/fuse_ops.c 2026-10-10 20:53:12.000000000 +0200 @@ -39,6 +39,11 @@ #include <sys/statvfs.h> #endif +#ifdef __APPLE__ + #include <sys/socket.h> + #include <sys/un.h> +#endif + #ifdef HAVE_XATTR #include <sys/xattr.h> #endif @@ -391,6 +396,22 @@ res = creat(p, 0); if (res > 0 && close(res) == -1) USYSLOG(LOG_WARNING, "Warning, cannot close file\n"); +#ifdef __APPLE__ + } else if ((file_type) == S_IFSOCK) { + int sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + + if (sockfd >= 0) { + // Bind (and immediately close) the socket in order to create the file on + // the underlying filesystem + struct sockaddr_un sock_addr; + sock_addr.sun_family = AF_UNIX; + strncpy(sock_addr.sun_path, p, sizeof(sock_addr.sun_path)); + res = bind(sockfd, (struct sockaddr*)&sock_addr, sizeof(sock_addr)); + close(sockfd); + } else { + res = -1; + } +#endif } else { res = mknod(p, file_type, rdev); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/unionfs-fuse-3.7/src/unionfsctl.c new/unionfs-fuse-3.8/src/unionfsctl.c --- old/unionfs-fuse-3.7/src/unionfsctl.c 2025-09-14 19:29:51.000000000 +0200 +++ new/unionfs-fuse-3.8/src/unionfsctl.c 2026-10-10 20:53:12.000000000 +0200 @@ -44,25 +44,27 @@ int opt; const char* argument_param; + char debug_file[PATHLEN_MAX] = {0}; int debug_on_off; int ioctl_res; while ((opt = getopt(argc, argv, "d:p:")) != -1) { switch (opt) { case 'p': - argument_param = optarg; - if (strlen(argument_param) < 1) { + if (strlen(optarg) < 1) { fprintf(stderr, "Not a valid debug path given!\n"); print_help(progname); exit(1); } - if (strlen(argument_param) > PATHLEN_MAX) { + if (strlen(optarg) > PATHLEN_MAX) { fprintf(stderr, "Debug path too long!\n"); exit(1); } - ioctl_res = ioctl(fd, UNIONFS_SET_DEBUG_FILE, argument_param); + strncpy(debug_file, optarg, PATHLEN_MAX - 1); + + ioctl_res = ioctl(fd, UNIONFS_SET_DEBUG_FILE, debug_file); if (ioctl_res == -1) { fprintf(stderr, "debug-file ioctl failed: %s\n", strerror(errno) ); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/unionfs-fuse-3.7/src/version.h new/unionfs-fuse-3.8/src/version.h --- old/unionfs-fuse-3.7/src/version.h 2025-09-14 19:29:51.000000000 +0200 +++ new/unionfs-fuse-3.8/src/version.h 2026-10-10 20:53:12.000000000 +0200 @@ -4,5 +4,5 @@ */ #ifndef _VERSION_H -#define VERSION "3.7" +#define VERSION "3.8" #endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/unionfs-fuse-3.7/test_all.py new/unionfs-fuse-3.8/test_all.py --- old/unionfs-fuse-3.7/test_all.py 2025-09-14 19:29:51.000000000 +0200 +++ new/unionfs-fuse-3.8/test_all.py 2026-10-10 20:53:12.000000000 +0200 @@ -13,6 +13,7 @@ import stat import platform import errno +import threading def call(cmd): @@ -264,6 +265,57 @@ self.assertFalse(os.path.isfile('rw1/ro_file')) self.assertEqual(read_from_file('ro1/ro1_file'), 'ro1') + def test_concurrent_cow(self): + # copy_file() used to share a static buffer between threads. Files of + # up to 8 MiB are copied via mmap and never touch that buffer, so the + # files have to be bigger than that to exercise the read/write loop. + num_threads = 4 + file_size = 9 * 1024 * 1024 + files = [] + + for idx in range(num_threads): + fn = 'ro1/concurrent_cow_%d' % idx + pattern = (chr(ord('A') + idx) * 1024).encode('ascii') * (file_size // 1024) + with open(fn, 'wb') as f: + f.write(pattern) + files.append(('concurrent_cow_%d' % idx, pattern)) + + barrier = threading.Barrier(num_threads) + errors = [] + + def worker(fname, pat): + try: + barrier.wait() + # Open with read+write to trigger COW copy of original file from ro1 to rw1 + with open('union/%s' % fname, 'r+b') as f: + data = f.read() + if data != pat: + errors.append("Initial read mismatch for %s: got len %d, expected %d" % (fname, len(data), len(pat))) + f.seek(0) + f.write(b'MODIFIED_' + pat[9:]) + except Exception as e: + errors.append("Exception in worker for %s: %s" % (fname, e)) + + threads = [ + threading.Thread(target=worker, args=(fn, pat)) + for fn, pat in files + ] + for t in threads: + t.start() + for t in threads: + t.join() + + self.assertEqual(errors, []) + + for fn, pat in files: + expected = b'MODIFIED_' + pat[9:] + with open('union/%s' % fn, 'rb') as f: + self.assertEqual(f.read(), expected) + with open('rw1/%s' % fn, 'rb') as f: + self.assertEqual(f.read(), expected) + with open('ro1/%s' % fn, 'rb') as f: + self.assertEqual(f.read(), pat) + def test_write_new(self): write_to_file('union/new_file', 'something') self.assertEqual(read_from_file('union/new_file'), 'something')
