Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package s3backer for openSUSE:Factory 
checked in at 2025-05-23 14:31:13
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/s3backer (Old)
 and      /work/SRC/openSUSE:Factory/.s3backer.new.2732 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "s3backer"

Fri May 23 14:31:13 2025 rev:31 rq:1279338 version:2.1.5

Changes:
--------
--- /work/SRC/openSUSE:Factory/s3backer/s3backer.changes        2025-05-20 
09:39:21.072725739 +0200
+++ /work/SRC/openSUSE:Factory/.s3backer.new.2732/s3backer.changes      
2025-05-23 14:32:08.744611894 +0200
@@ -1,0 +2,7 @@
+Thu May 22 18:07:25 UTC 2025 - Archie Cobbs <archie.co...@gmail.com>
+
+- Upgrade to release 2.1.5
+    - Add new flag "--statsFileMirror" (issue #237)
+    - Update to FUSE 3.x (#239)
+
+-------------------------------------------------------------------

Old:
----
  s3backer-2.1.4.tar.gz

New:
----
  s3backer-2.1.5.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ s3backer.spec ++++++
--- /var/tmp/diff_new_pack.NVEE1H/_old  2025-05-23 14:32:09.280634510 +0200
+++ /var/tmp/diff_new_pack.NVEE1H/_new  2025-05-23 14:32:09.280634510 +0200
@@ -25,14 +25,14 @@
 %endif
 
 Name:           s3backer
-Version:        2.1.4
+Version:        2.1.5
 Release:        0
 Summary:        FUSE and NBD single file backing store via Amazon S3
 License:        GPL-2.0-or-later
 Group:          System/Filesystems
 URL:            https://github.com/archiecobbs/%{name}
 Source:         
https://s3.amazonaws.com/archie-public/%{name}/%{name}-%{version}.tar.gz
-BuildRequires:  fuse-devel >= 2.5
+BuildRequires:  fuse3-devel >= 3.0.0
 BuildRequires:  libcurl-devel >= 7.16.2
 BuildRequires:  libexpat-devel
 BuildRequires:  libopenssl-devel

++++++ s3backer-2.1.4.tar.gz -> s3backer-2.1.5.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/s3backer-2.1.4/CHANGES new/s3backer-2.1.5/CHANGES
--- old/s3backer-2.1.4/CHANGES  2025-05-10 23:30:38.000000000 +0200
+++ new/s3backer-2.1.5/CHANGES  2025-05-22 19:59:47.000000000 +0200
@@ -1,3 +1,8 @@
+Version 2.1.5 released May 22, 2025
+
+    - Add new flag "--statsFileMirror" (issue #237)
+    - Update to FUSE 3.x (#239)
+
 Version 2.1.4 released May 10, 2025
 
     - Add new flag "--sharedDiskMode" (issue #236)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/s3backer-2.1.4/block_part.c 
new/s3backer-2.1.5/block_part.c
--- old/s3backer-2.1.4/block_part.c     2023-10-08 19:15:18.000000000 +0200
+++ new/s3backer-2.1.5/block_part.c     2025-05-20 19:04:16.000000000 +0200
@@ -91,6 +91,8 @@
 {
     struct block_part *const priv = *block_partp;
 
+    if (priv == NULL)
+        return;
     *block_partp = NULL;
     pthread_cond_destroy(&priv->wakeup);
     pthread_mutex_destroy(&priv->mutex);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/s3backer-2.1.4/config.h.in 
new/s3backer-2.1.5/config.h.in
--- old/s3backer-2.1.4/config.h.in      2025-05-10 23:31:46.000000000 +0200
+++ new/s3backer-2.1.5/config.h.in      2025-05-22 20:00:42.000000000 +0200
@@ -1,8 +1,5 @@
 /* config.h.in.  Generated from configure.ac by autoheader.  */
 
-/* FUSE fallocate() support */
-#undef FUSE_FALLOCATE
-
 /* FUSE API version */
 #undef FUSE_USE_VERSION
 
@@ -67,8 +64,8 @@
 /* Define to 1 if you have the `expat' library (-lexpat). */
 #undef HAVE_LIBEXPAT
 
-/* Define to 1 if you have the `fuse' library (-lfuse). */
-#undef HAVE_LIBFUSE
+/* Define to 1 if you have the `fuse3' library (-lfuse3). */
+#undef HAVE_LIBFUSE3
 
 /* Define to 1 if you have the `pthread' library (-lpthread). */
 #undef HAVE_LIBPTHREAD
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/s3backer-2.1.4/configure new/s3backer-2.1.5/configure
--- old/s3backer-2.1.4/configure        2025-05-10 23:31:46.000000000 +0200
+++ new/s3backer-2.1.5/configure        2025-05-22 20:00:42.000000000 +0200
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for s3backer FUSE filesystem backed by Amazon 
S3 2.1.4.
+# Generated by GNU Autoconf 2.69 for s3backer FUSE filesystem backed by Amazon 
S3 2.1.5.
 #
 # Report bugs to <https://github.com/archiecobbs/s3backer>.
 #
@@ -590,8 +590,8 @@
 # Identity of this package.
 PACKAGE_NAME='s3backer FUSE filesystem backed by Amazon S3'
 PACKAGE_TARNAME='s3backer'
-PACKAGE_VERSION='2.1.4'
-PACKAGE_STRING='s3backer FUSE filesystem backed by Amazon S3 2.1.4'
+PACKAGE_VERSION='2.1.5'
+PACKAGE_STRING='s3backer FUSE filesystem backed by Amazon S3 2.1.5'
 PACKAGE_BUGREPORT='https://github.com/archiecobbs/s3backer'
 PACKAGE_URL=''
 
@@ -1346,7 +1346,7 @@
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-\`configure' configures s3backer FUSE filesystem backed by Amazon S3 2.1.4 to 
adapt to many kinds of systems.
+\`configure' configures s3backer FUSE filesystem backed by Amazon S3 2.1.5 to 
adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -1416,7 +1416,7 @@
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of s3backer FUSE filesystem 
backed by Amazon S3 2.1.4:";;
+     short | recursive ) echo "Configuration of s3backer FUSE filesystem 
backed by Amazon S3 2.1.5:";;
    esac
   cat <<\_ACEOF
 
@@ -1547,7 +1547,7 @@
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-s3backer FUSE filesystem backed by Amazon S3 configure 2.1.4
+s3backer FUSE filesystem backed by Amazon S3 configure 2.1.5
 generated by GNU Autoconf 2.69
 
 Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1962,7 +1962,7 @@
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by s3backer FUSE filesystem backed by Amazon S3 $as_me 2.1.4, 
which was
+It was created by s3backer FUSE filesystem backed by Amazon S3 $as_me 2.1.5, 
which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   $ $0 $@
@@ -2825,7 +2825,7 @@
 
 # Define the identity of the package.
  PACKAGE='s3backer'
- VERSION='2.1.4'
+ VERSION='2.1.5'
 
 
 cat >>confdefs.h <<_ACEOF
@@ -12084,10 +12084,7 @@
 
 # Compile flags for FUSE
 
-$as_echo "#define FUSE_USE_VERSION 26" >>confdefs.h
-
-
-$as_echo "#define FUSE_FALLOCATE 0" >>confdefs.h
+$as_echo "#define FUSE_USE_VERSION 35" >>confdefs.h
 
 
 # Check for required programs
@@ -12942,19 +12939,19 @@
 fi
 
 pkg_failed=no
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fuse" >&5
-$as_echo_n "checking for fuse... " >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fuse3" >&5
+$as_echo_n "checking for fuse3... " >&6; }
 
 if test -n "$FUSE_CFLAGS"; then
     pkg_cv_FUSE_CFLAGS="$FUSE_CFLAGS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists 
--print-errors \"fuse\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "fuse") 2>&5
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists 
--print-errors \"fuse3\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "fuse3") 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_FUSE_CFLAGS=`$PKG_CONFIG --cflags "fuse" 2>/dev/null`
+  pkg_cv_FUSE_CFLAGS=`$PKG_CONFIG --cflags "fuse3" 2>/dev/null`
                      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -12966,12 +12963,12 @@
     pkg_cv_FUSE_LIBS="$FUSE_LIBS"
  elif test -n "$PKG_CONFIG"; then
     if test -n "$PKG_CONFIG" && \
-    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists 
--print-errors \"fuse\""; } >&5
-  ($PKG_CONFIG --exists --print-errors "fuse") 2>&5
+    { { $as_echo "$as_me:${as_lineno-$LINENO}: \$PKG_CONFIG --exists 
--print-errors \"fuse3\""; } >&5
+  ($PKG_CONFIG --exists --print-errors "fuse3") 2>&5
   ac_status=$?
   $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
   test $ac_status = 0; }; then
-  pkg_cv_FUSE_LIBS=`$PKG_CONFIG --libs "fuse" 2>/dev/null`
+  pkg_cv_FUSE_LIBS=`$PKG_CONFIG --libs "fuse3" 2>/dev/null`
                      test "x$?" != "x0" && pkg_failed=yes
 else
   pkg_failed=yes
@@ -12992,18 +12989,18 @@
         _pkg_short_errors_supported=no
 fi
         if test $_pkg_short_errors_supported = yes; then
-               FUSE_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors 
--cflags --libs "fuse" 2>&1`
+               FUSE_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors 
--cflags --libs "fuse3" 2>&1`
         else
-               FUSE_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs 
"fuse" 2>&1`
+               FUSE_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs 
"fuse3" 2>&1`
         fi
        # Put the nasty error message in config.log where it belongs
        echo "$FUSE_PKG_ERRORS" >&5
 
-       as_fn_error $? "\"fuse\" not found in pkg-config" "$LINENO" 5
+       as_fn_error $? "\"fuse3\" not found in pkg-config" "$LINENO" 5
 elif test $pkg_failed = untried; then
         { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
 $as_echo "no" >&6; }
-       as_fn_error $? "\"fuse\" not found in pkg-config" "$LINENO" 5
+       as_fn_error $? "\"fuse3\" not found in pkg-config" "$LINENO" 5
 else
        FUSE_CFLAGS=$pkg_cv_FUSE_CFLAGS
        FUSE_LIBS=$pkg_cv_FUSE_LIBS
@@ -13541,13 +13538,13 @@
   as_fn_error $? "required library expat missing" "$LINENO" 5
 fi
 
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fuse_version in -lfuse" 
>&5
-$as_echo_n "checking for fuse_version in -lfuse... " >&6; }
-if ${ac_cv_lib_fuse_fuse_version+:} false; then :
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fuse_version in -lfuse3" 
>&5
+$as_echo_n "checking for fuse_version in -lfuse3... " >&6; }
+if ${ac_cv_lib_fuse3_fuse_version+:} false; then :
   $as_echo_n "(cached) " >&6
 else
   ac_check_lib_save_LIBS=$LIBS
-LIBS="-lfuse  $LIBS"
+LIBS="-lfuse3  $LIBS"
 cat confdefs.h - <<_ACEOF >conftest.$ac_ext
 /* end confdefs.h.  */
 
@@ -13567,25 +13564,25 @@
 }
 _ACEOF
 if ac_fn_c_try_link "$LINENO"; then :
-  ac_cv_lib_fuse_fuse_version=yes
+  ac_cv_lib_fuse3_fuse_version=yes
 else
-  ac_cv_lib_fuse_fuse_version=no
+  ac_cv_lib_fuse3_fuse_version=no
 fi
 rm -f core conftest.err conftest.$ac_objext \
     conftest$ac_exeext conftest.$ac_ext
 LIBS=$ac_check_lib_save_LIBS
 fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_fuse_fuse_version" 
>&5
-$as_echo "$ac_cv_lib_fuse_fuse_version" >&6; }
-if test "x$ac_cv_lib_fuse_fuse_version" = xyes; then :
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: 
$ac_cv_lib_fuse3_fuse_version" >&5
+$as_echo "$ac_cv_lib_fuse3_fuse_version" >&6; }
+if test "x$ac_cv_lib_fuse3_fuse_version" = xyes; then :
   cat >>confdefs.h <<_ACEOF
-#define HAVE_LIBFUSE 1
+#define HAVE_LIBFUSE3 1
 _ACEOF
 
-  LIBS="-lfuse $LIBS"
+  LIBS="-lfuse3 $LIBS"
 
 else
-  as_fn_error $? "required library libfuse missing" "$LINENO" 5
+  as_fn_error $? "required library libfuse3 missing" "$LINENO" 5
 fi
 
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for compressBound in -lz" >&5
@@ -13681,33 +13678,6 @@
 fi
 rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
 
-# See if FUSE version is 2.9.2 or later
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fallocate() support in 
fuse" >&5
-$as_echo_n "checking for fallocate() support in fuse... " >&6; }
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h.  */
-
-#include <fuse/fuse.h>
-struct fuse_operations x = { fallocate: (void*)1 };
-
-int
-main ()
-{
-
-  ;
-  return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }; $as_echo "#define FUSE_FALLOCATE 1" >>confdefs.h
-
-else
-  { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-
 # Set some O/S specific stuff
 case `uname -s` in
     Darwin|FreeBSD)
@@ -14445,7 +14415,7 @@
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by s3backer FUSE filesystem backed by Amazon S3 $as_me 
2.1.4, which was
+This file was extended by s3backer FUSE filesystem backed by Amazon S3 $as_me 
2.1.5, which was
 generated by GNU Autoconf 2.69.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -14511,7 +14481,7 @@
 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; 
s/[\\""\`\$]/\\\\&/g'`"
 ac_cs_version="\\
-s3backer FUSE filesystem backed by Amazon S3 config.status 2.1.4
+s3backer FUSE filesystem backed by Amazon S3 config.status 2.1.5
 configured by $0, generated by GNU Autoconf 2.69,
   with options \\"\$ac_cs_config\\"
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/s3backer-2.1.4/configure.ac 
new/s3backer-2.1.5/configure.ac
--- old/s3backer-2.1.4/configure.ac     2025-05-10 23:30:47.000000000 +0200
+++ new/s3backer-2.1.5/configure.ac     2025-05-22 19:59:56.000000000 +0200
@@ -32,7 +32,7 @@
 # this exception statement from all source files in the program, then
 # also delete it here.
 
-AC_INIT([s3backer FUSE filesystem backed by Amazon 
S3],[2.1.4],[https://github.com/archiecobbs/s3backer],[s3backer])
+AC_INIT([s3backer FUSE filesystem backed by Amazon 
S3],[2.1.5],[https://github.com/archiecobbs/s3backer],[s3backer])
 AC_CONFIG_AUX_DIR(scripts)
 AM_INIT_AUTOMAKE(foreign)
 LT_INIT()
@@ -56,8 +56,7 @@
 AC_DEFINE(_DARWIN_C_SOURCE, 1, MacOS functions)
 
 # Compile flags for FUSE
-AC_DEFINE(FUSE_USE_VERSION, 26, FUSE API version)
-AC_DEFINE(FUSE_FALLOCATE, 0, FUSE fallocate() support)
+AC_DEFINE(FUSE_USE_VERSION, 35, FUSE API version)
 
 # Check for required programs
 AC_PROG_INSTALL
@@ -65,10 +64,10 @@
 
 # Check for required pkg-config'd stuff
 PKG_PROG_PKG_CONFIG(0.19)
-PKG_CHECK_MODULES(FUSE, fuse,
+PKG_CHECK_MODULES(FUSE, fuse3,
     [CFLAGS="${CFLAGS} ${FUSE_CFLAGS}"
     LDFLAGS="${LDFLAGS} ${FUSE_LIBS}"],
-    [AC_MSG_ERROR(["fuse" not found in pkg-config])])
+    [AC_MSG_ERROR(["fuse3" not found in pkg-config])])
 
 # Check for zstd (optional)
 PKG_CHECK_MODULES([ZSTD], libzstd,
@@ -127,8 +126,8 @@
        [AC_MSG_ERROR([required library libcrypto missing])])
 AC_CHECK_LIB(expat, XML_ParserCreate,,
        [AC_MSG_ERROR([required library expat missing])])
-AC_CHECK_LIB(fuse, fuse_version,,
-       [AC_MSG_ERROR([required library libfuse missing])])
+AC_CHECK_LIB(fuse3, fuse_version,,
+       [AC_MSG_ERROR([required library libfuse3 missing])])
 AC_CHECK_LIB(z, compressBound,,
        [AC_MSG_ERROR([required library zlib missing])])
 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
@@ -145,13 +144,6 @@
 #endif
 ]])],, [AC_MSG_ERROR([curl 8.11.0 is broken; upgrade/downgrade to another 
version. See issue 232 for details.])])
 
-# See if FUSE version is 2.9.2 or later
-AC_MSG_CHECKING([for fallocate() support in fuse])
-AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
-#include <fuse/fuse.h>
-struct fuse_operations x = { fallocate: (void*)1 };
-]])],[AC_MSG_RESULT([yes]); AC_DEFINE(FUSE_FALLOCATE)],AC_MSG_RESULT([no]))
-
 # Set some O/S specific stuff
 case `uname -s` in
     Darwin|FreeBSD)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/s3backer-2.1.4/fuse_ops.c 
new/s3backer-2.1.5/fuse_ops.c
--- old/s3backer-2.1.4/fuse_ops.c       2025-05-06 19:20:12.000000000 +0200
+++ new/s3backer-2.1.5/fuse_ops.c       2025-05-20 19:04:18.000000000 +0200
@@ -66,12 +66,11 @@
  ****************************************************************************/
 
 // FUSE functions
-static void *fuse_op_init(struct fuse_conn_info *conn);
+static void *fuse_op_init(struct fuse_conn_info *conn, struct fuse_config 
*cfg);
 static void fuse_op_destroy(void *data);
-static int fuse_op_getattr(const char *path, struct stat *st);
-static int fuse_op_fgetattr(const char *path, struct stat *st, struct 
fuse_file_info *);
+static int fuse_op_getattr(const char *path, struct stat *st, struct 
fuse_file_info *fi);
 static int fuse_op_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
-    off_t offset, struct fuse_file_info *fi);
+    off_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags flags);
 static int fuse_op_open(const char *path, struct fuse_file_info *fi);
 static int fuse_op_release(const char *path, struct fuse_file_info *fi);
 static int fuse_op_read(const char *path, char *buf, size_t size, off_t offset,
@@ -79,13 +78,11 @@
 static int fuse_op_write(const char *path, const char *buf, size_t size, off_t 
offset,
     struct fuse_file_info *fi);
 static int fuse_op_statfs(const char *path, struct statvfs *st);
-static int fuse_op_truncate(const char *path, off_t size);
+static int fuse_op_truncate(const char *path, off_t size, struct 
fuse_file_info *fi);
 static int fuse_op_flush(const char *path, struct fuse_file_info *fi);
 static int fuse_op_fsync(const char *path, int isdatasync, struct 
fuse_file_info *fi);
 static int fuse_op_unlink(const char *path);
-#if FUSE_FALLOCATE
 static int fuse_op_fallocate(const char *path, int mode, off_t offset, off_t 
len, struct fuse_file_info *fi);
-#endif
 
 // Attribute functions
 static void fuse_op_getattr_file(struct fuse_ops_private *priv, struct stat 
*st);
@@ -95,6 +92,8 @@
 static struct stat_file *fuse_op_stats_create(struct fuse_ops_private *priv);
 static void fuse_op_stats_destroy(struct stat_file *sfile);
 static printer_t fuse_op_stats_printer;
+static printer_t stats_mirror_printer;
+static void *stats_mirror_thread(void *arg);
 
 /****************************************************************************
  *                          VARIABLE DEFINITIONS                            *
@@ -105,7 +104,6 @@
     .init       = fuse_op_init,
     .destroy    = fuse_op_destroy,
     .getattr    = fuse_op_getattr,
-    .fgetattr   = fuse_op_fgetattr,
     .readdir    = fuse_op_readdir,
     .open       = fuse_op_open,
     .read       = fuse_op_read,
@@ -116,9 +114,7 @@
     .fsync      = fuse_op_fsync,
     .release    = fuse_op_release,
     .unlink     = fuse_op_unlink,
-#if FUSE_FALLOCATE
     .fallocate  = fuse_op_fallocate,
-#endif
 };
 
 // Configuration and underlying s3backer_store
@@ -162,10 +158,12 @@
 void
 fuse_ops_destroy(void)
 {
-    struct fuse_ops_private *const priv = the_priv;
-
-    block_part_destroy(&priv->block_part);
-    fuse_op_destroy(priv);
+    if (the_priv == NULL)
+        return;
+    block_part_destroy(&the_priv->block_part);
+    fuse_op_destroy(the_priv);
+    free(the_priv);
+    the_priv = NULL;
 }
 
 /****************************************************************************
@@ -173,7 +171,7 @@
  ****************************************************************************/
 
 static void *
-fuse_op_init(struct fuse_conn_info *conn)
+fuse_op_init(struct fuse_conn_info *conn, struct fuse_config *cfg)
 {
     struct s3b_config *const s3bconf = config->s3bconf;
     struct fuse_ops_private *const priv = the_priv;
@@ -194,12 +192,28 @@
     // Apply process tweaks
     apply_process_tweaks();
 
-    // Startup background threads now that we have fork()'d
+    // Start up stats mirror thread
+    priv->stats_mirror_state = STATS_MIRROR_INITIAL;
+    if (config->stats_mirror_path != NULL
+      && (r = pthread_create(&priv->stats_mirror_thread, NULL, 
stats_mirror_thread, priv)) != 0) {
+        (*config->log)(LOG_ERR, "failed to create stats mirror thread: %s", 
strerror(r));
+        return NULL;
+    }
+
+    // Startup other background threads
     if ((r = (*priv->s3b->create_threads)(priv->s3b)) != 0) {
         (*config->log)(LOG_ERR, "fuse_op_init(): can't create threads: %s", 
strerror(errno));
+        if (config->stats_mirror_path != NULL) {
+            (void)pthread_cancel(priv->stats_mirror_thread);
+            (void)pthread_join(priv->stats_mirror_thread, NULL);
+        }
         return NULL;
     }
 
+    // Allow stats mirror thread to start writing
+    if (config->stats_mirror_path != NULL)
+        priv->stats_mirror_state = STATS_MIRROR_RUNNING;
+
     // Done
     (*config->log)(LOG_INFO, "mounting %s", s3bconf->mount);
     return priv;
@@ -218,7 +232,15 @@
         return;
     (*config->log)(LOG_INFO, "unmount %s: initiated", s3bconf->mount);
 
-    // Shutdown (flush dirty data)
+    // Shutdown stats mirror thread, if any
+    if (priv->stats_mirror_state == STATS_MIRROR_RUNNING) {
+        priv->stats_mirror_state = STATS_MIRROR_SHUTDOWN;
+        (void)pthread_cancel(priv->stats_mirror_thread);
+        (void)pthread_join(priv->stats_mirror_thread, NULL);
+        priv->stats_mirror_state = STATS_MIRROR_INITIAL;
+    }
+
+    // Shutdown S3 statck (flush dirty data)
     (*config->log)(LOG_INFO, "unmount %s: shutting down filesystem", 
s3bconf->mount);
     if ((r = (*s3b->shutdown)(s3b)) != 0)
         (*config->log)(LOG_ERR, "unmount %s: filesystem shutdown failed: %s", 
s3bconf->mount, strerror(r));
@@ -232,12 +254,12 @@
 
     // Destroy
     (*s3b->destroy)(s3b);
+    priv->s3b = NULL;
     (*config->log)(LOG_INFO, "unmount %s: completed", s3bconf->mount);
-    free(priv);
 }
 
 static int
-fuse_op_getattr(const char *path, struct stat *st)
+fuse_op_getattr(const char *path, struct stat *st, struct fuse_file_info *fi)
 {
     struct fuse_ops_private *const priv = (struct fuse_ops_private 
*)fuse_get_context()->private_data;
 
@@ -273,20 +295,6 @@
     return -ENOENT;
 }
 
-static int
-fuse_op_fgetattr(const char *path, struct stat *st, struct fuse_file_info *fi)
-{
-    struct fuse_ops_private *const priv = (struct fuse_ops_private 
*)fuse_get_context()->private_data;
-
-    if (fi->fh != 0) {
-        struct stat_file *const sfile = (struct stat_file *)(uintptr_t)fi->fh;
-
-        fuse_op_getattr_stats(priv, sfile, st);
-    } else
-        fuse_op_getattr_file(priv, st);
-    return 0;
-}
-
 static void
 fuse_op_getattr_file(struct fuse_ops_private *priv, struct stat *st)
 {
@@ -321,7 +329,7 @@
 
 static int
 fuse_op_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
-    off_t offset, struct fuse_file_info *fi)
+    off_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags flags)
 {
     struct fuse_ops_private *const priv = (struct fuse_ops_private 
*)fuse_get_context()->private_data;
 
@@ -329,15 +337,15 @@
     (void)fi;
     if (strcmp(path, "/") != 0)
         return -ENOENT;
-    if (filler(buf, ".", NULL, 0) != 0)
+    if (filler(buf, ".", NULL, 0, 0) != 0)
         return -ENOMEM;
-    if (filler(buf, "..", NULL, 0) != 0)
+    if (filler(buf, "..", NULL, 0, 0) != 0)
         return -ENOMEM;
     if (priv != NULL) {
-        if (filler(buf, config->filename, NULL, 0) != 0)
+        if (filler(buf, config->filename, NULL, 0, 0) != 0)
             return -ENOMEM;
         if (config->print_stats != NULL && config->stats_filename != NULL) {
-            if (filler(buf, config->stats_filename, NULL, 0) != 0)
+            if (filler(buf, config->stats_filename, NULL, 0, 0) != 0)
                 return -ENOMEM;
         }
     }
@@ -499,7 +507,7 @@
 }
 
 static int
-fuse_op_truncate(const char *path, off_t size)
+fuse_op_truncate(const char *path, off_t size, struct fuse_file_info *fi)
 {
     return 0;
 }
@@ -544,7 +552,6 @@
 }
 
 
-#if FUSE_FALLOCATE
 static int
 fuse_op_fallocate(const char *path, int mode, off_t offset, off_t len, struct 
fuse_file_info *fi)
 {
@@ -586,7 +593,6 @@
     priv->file_mtime = time(NULL);
     return 0;
 }
-#endif
 
 /****************************************************************************
  *                    OTHER INTERNAL FUNCTIONS                              *
@@ -650,3 +656,84 @@
     goto again;
 }
 
+// Stats mirror thread
+static void *
+stats_mirror_thread(void *arg)
+{
+    struct fuse_ops_private *const priv = the_priv;
+    int file_was_there = 0;
+
+    while (1) {
+
+        // Time to leave?
+        if (priv->stats_mirror_state == STATS_MIRROR_SHUTDOWN)
+            break;
+
+        // If system is up & running, update the stats file
+        if (priv->stats_mirror_state == STATS_MIRROR_RUNNING) {
+            struct stat sb;
+            char *temp_path;
+            FILE *fp;
+
+            // If file existed before but was since deleted, reset stats
+            if (file_was_there && stat(config->stats_mirror_path, &sb) == -1) {
+                if (errno != ENOENT) {
+                    (*config->log)(LOG_ERR, "error %s stats mirror file %s 
(mirroring stopped): %s",
+                      "accessing", config->stats_mirror_path, strerror(errno));
+                    break;
+                }
+                (*config->clear_stats)();
+                file_was_there = 0;
+            }
+
+            // Format temporary name string
+            if (asprintf(&temp_path, "%s.new", config->stats_mirror_path) == 
-1) {
+                (*config->log)(LOG_ERR, "error %s stats mirror file %s 
(mirroring stopped): %s",
+                  "updating", config->stats_mirror_path, strerror(errno));
+                break;
+            }
+
+            // Write into temporary file
+            if ((fp = fopen(temp_path, "w")) == NULL) {
+                (*config->log)(LOG_ERR, "error %s stats mirror file %s 
(mirroring stopped): %s",
+                  "creating", temp_path, strerror(errno));
+                free(temp_path);
+                break;
+            }
+            (*config->print_stats)(fp, stats_mirror_printer);
+            if (fclose(fp) == EOF) {
+                (*config->log)(LOG_ERR, "error %s stats mirror file %s 
(mirroring stopped): %s",
+                  "writing", config->stats_mirror_path, strerror(errno));
+                unlink(temp_path);
+                free(temp_path);
+                break;
+            }
+            if (rename(temp_path, config->stats_mirror_path) == -1) {
+                (*config->log)(LOG_ERR, "error %s stats mirror file %s 
(mirroring stopped): %s",
+                  "updating", config->stats_mirror_path, strerror(errno));
+                unlink(temp_path);
+                free(temp_path);
+                break;
+            }
+            free(temp_path);
+            file_was_there = 1;
+        }
+
+        // Sleep for a while
+        usleep(config->stats_mirror_interval * 1000);
+    }
+
+    // Done
+    return NULL;
+}
+
+static void
+stats_mirror_printer(void *prarg, const char *fmt, ...)
+{
+    FILE *const fp = prarg;
+    va_list args;
+
+    va_start(args, fmt);
+    vfprintf(fp, fmt, args);
+    va_end(args);
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/s3backer-2.1.4/fuse_ops.h 
new/s3backer-2.1.5/fuse_ops.h
--- old/s3backer-2.1.4/fuse_ops.h       2023-10-08 19:15:18.000000000 +0200
+++ new/s3backer-2.1.5/fuse_ops.h       2025-05-20 18:48:06.000000000 +0200
@@ -39,6 +39,11 @@
 struct s3backer_store;
 struct block_part;
 
+// Stats mirror state values
+#define STATS_MIRROR_INITIAL        0
+#define STATS_MIRROR_RUNNING        1
+#define STATS_MIRROR_SHUTDOWN       2
+
 // Function types
 typedef void printer_t(void *prarg, const char *fmt, ...) __attribute__ 
((__format__ (__printf__, 2, 3)));
 typedef void print_stats_t(void *prarg, printer_t *printer);
@@ -53,6 +58,8 @@
     int                     direct_io;
     const char              *filename;
     const char              *stats_filename;
+    const char              *stats_mirror_path;
+    u_int                   stats_mirror_interval;
     uid_t                   uid;
     gid_t                   gid;
     u_int                   block_size;
@@ -71,6 +78,10 @@
     time_t                  file_atime;
     time_t                  file_mtime;
     time_t                  stats_atime;
+
+    // Stats mirror
+    pthread_t               stats_mirror_thread;
+    volatile int            stats_mirror_state;
 };
 
 // fuse_ops.c
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/s3backer-2.1.4/gitrev.c new/s3backer-2.1.5/gitrev.c
--- old/s3backer-2.1.4/gitrev.c 2025-05-10 23:31:54.000000000 +0200
+++ new/s3backer-2.1.5/gitrev.c 2025-05-22 20:00:49.000000000 +0200
@@ -1 +1 @@
-const char *const s3backer_version = "2.1.4";
+const char *const s3backer_version = "2.1.5";
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/s3backer-2.1.4/main.c new/s3backer-2.1.5/main.c
--- old/s3backer-2.1.4/main.c   2024-09-13 17:30:31.000000000 +0200
+++ new/s3backer-2.1.5/main.c   2025-05-20 19:00:08.000000000 +0200
@@ -75,6 +75,7 @@
     struct s3backer_store *s3b;
     int nbd = 0;
     int i;
+    int r;
 
     // Look for "--nbd" flag
     for (i = 1; i < argc; i++) {
@@ -90,11 +91,11 @@
     // Handle `--nbd' flag
     if (nbd) {
 #if NBDKIT
-        if ((i = trampoline_to_nbd(argc, argv)) == 2) {
+        if ((r = trampoline_to_nbd(argc, argv)) == 2) {
             usage();
-            i = 1;
+            r = 1;
         }
-        return i;
+        return r;
 #else
         errx(1, "invalid flag \"--nbd\": %s was not built with NBD support", 
PACKAGE);
 #endif
@@ -137,14 +138,12 @@
 
     // Start
     (*config->log)(LOG_INFO, "s3backer process %lu for %s started", 
(u_long)getpid(), config->mount);
-    if (fuse_main(config->fuse_args.argc, config->fuse_args.argv, fuse_ops, 
NULL) != 0) {
-        (*config->log)(LOG_ERR, "error starting FUSE");
-        fuse_ops_destroy();
-        return 1;
-    }
+    if ((r = fuse_main(config->fuse_args.argc, config->fuse_args.argv, 
fuse_ops, NULL)) != 0)
+        (*config->log)(LOG_ERR, "error %s FUSE: fuse_main() returned %d", r < 
7 ? "starting" : "running", r);
 
     // Done
-    return 0;
+    fuse_ops_destroy();
+    return r;
 }
 
 #if NBDKIT
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/s3backer-2.1.4/nbdkit.c new/s3backer-2.1.5/nbdkit.c
--- old/s3backer-2.1.4/nbdkit.c 2024-06-25 16:39:03.000000000 +0200
+++ new/s3backer-2.1.5/nbdkit.c 2025-05-20 19:03:43.000000000 +0200
@@ -320,15 +320,18 @@
         set_config_log(config, s3b_nbd_logger);
 
     // Startup threads etc.
-    fuse_priv = (*fuse_ops->init)(NULL);
+    fuse_priv = (*fuse_ops->init)(NULL, NULL);
     return 0;
 }
 
 static void
 s3b_nbd_plugin_unload(void)
 {
-    if (fuse_priv != NULL)
-        (*fuse_ops->destroy)(fuse_priv);
+    if (fuse_ops != NULL) {
+        fuse_ops_destroy();
+        fuse_ops = NULL;
+    }
+    fuse_priv = NULL;
     free_strings(&params);
     s3b_cleanup();
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/s3backer-2.1.4/s3b_config.c 
new/s3backer-2.1.5/s3b_config.c
--- old/s3backer-2.1.4/s3b_config.c     2025-05-07 00:25:19.000000000 +0200
+++ new/s3backer-2.1.5/s3b_config.c     2025-05-20 18:20:00.000000000 +0200
@@ -67,6 +67,7 @@
 #define S3BACKER_DEFAULT_PREFIX                     ""
 #define S3BACKER_DEFAULT_FILENAME                   "file"
 #define S3BACKER_DEFAULT_STATS_FILENAME             "stats"
+#define S3BACKER_DEFAULT_STATS_MIRROR_INTERVAL      1000            // 1s
 #define S3BACKER_DEFAULT_BLOCKSIZE                  4096
 #define S3BACKER_DEFAULT_TIMEOUT                    30              // 30s
 #define S3BACKER_DEFAULT_FILE_MODE                  0600
@@ -194,6 +195,7 @@
     .fuse_ops= {
         .filename=              S3BACKER_DEFAULT_FILENAME,
         .stats_filename=        S3BACKER_DEFAULT_STATS_FILENAME,
+        .stats_mirror_interval= S3BACKER_DEFAULT_STATS_MIRROR_INTERVAL,
         .file_mode=             -1,             // default depends on 
'read_only'
     },
 
@@ -454,6 +456,14 @@
         .offset=    offsetof(struct s3b_config, fuse_ops.stats_filename),
     },
     {
+        .templ=     "--statsFileMirror=%s",
+        .offset=    offsetof(struct s3b_config, fuse_ops.stats_mirror_path),
+    },
+    {
+        .templ=     "--statsFileMirrorInterval=%u",
+        .offset=    offsetof(struct s3b_config, 
fuse_ops.stats_mirror_interval),
+    },
+    {
         .templ=     "--storageClass=%s",
         .offset=    offsetof(struct s3b_config, http_io.storage_class),
     },
@@ -546,8 +556,6 @@
 static const char *const s3backer_fuse_defaults[] = {
     "-okernel_cache",
     "-oallow_other",
-    "-ouse_ino",
-    "-omax_readahead=0",
     "-osubtype=s3backer",
     "-oentry_timeout=31536000",
     "-onegative_timeout=31536000",
@@ -939,6 +947,7 @@
     FORCE_FREE(config.file_size_str);
     FORCE_FREE2(config.fuse_ops.filename, S3BACKER_DEFAULT_FILENAME);
     FORCE_FREE2(config.fuse_ops.stats_filename, 
S3BACKER_DEFAULT_STATS_FILENAME);
+    FORCE_FREE(config.fuse_ops.stats_mirror_path);
     FORCE_FREE(config.http_io.storage_class);
     FORCE_FREE(config.http_io.cacert);
     FORCE_FREE2(config.compress_alg, S3BACKER_DEFAULT_COMPRESSION);
@@ -2031,6 +2040,8 @@
     (*c->log)(LOG_DEBUG, "%24s: \"%s\"", "mount", c->mount);
     (*c->log)(LOG_DEBUG, "%24s: \"%s\"", "filename", c->fuse_ops.filename);
     (*c->log)(LOG_DEBUG, "%24s: \"%s\"", "stats_filename", 
c->fuse_ops.stats_filename);
+    (*c->log)(LOG_DEBUG, "%24s: \"%s\"", "stats_mirror_path", 
c->fuse_ops.stats_mirror_path != NULL ? c->fuse_ops.stats_mirror_path : "");
+    (*c->log)(LOG_DEBUG, "%24s: %ums", "stats_mirror_interval", 
c->fuse_ops.stats_mirror_interval);
     (*c->log)(LOG_DEBUG, "%24s: %s (%u)", "block_size", c->block_size_str != 
NULL ? c->block_size_str : "-", c->block_size);
     (*c->log)(LOG_DEBUG, "%24s: %s (%jd)", "file_size", c->file_size_str != 
NULL ? c->file_size_str : "-", (intmax_t)c->file_size);
     (*c->log)(LOG_DEBUG, "%24s: %jd", "num_blocks", (intmax_t)c->num_blocks);
@@ -2204,7 +2215,6 @@
     fprintf(stderr, "\t%-29s %s\n", "-o uid=UID", "Set user ID");
     fprintf(stderr, "\t%-29s %s\n", "-o gid=GID", "Set group ID");
     fprintf(stderr, "\t%-29s %s\n", "-o sync_read", "Do synchronous reads");
-    fprintf(stderr, "\t%-29s %s\n", "-o max_readahead=NUM", "Set maximum 
read-ahead (bytes)");
     fprintf(stderr, "\t%-29s %s\n", "-f", "Run in the foreground (do not 
fork)");
     fprintf(stderr, "\t%-29s %s\n", "-d", "Debug mode (implies -f)");
     fprintf(stderr, "\t%-29s %s\n", "-s", "Run in single-threaded mode");
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/s3backer-2.1.4/s3backer.1 
new/s3backer-2.1.5/s3backer.1
--- old/s3backer-2.1.4/s3backer.1       2025-05-10 23:31:51.000000000 +0200
+++ new/s3backer-2.1.5/s3backer.1       2025-05-22 20:00:47.000000000 +0200
@@ -358,6 +358,18 @@
 flag causes
 .Nm
 to function as a wrapper process for managing an NBD session; see below.
+.Pp
+When running in NBD mode, no
+.Nm
+filesystem is created and therefore no statistics file automatically appears.
+You can use
+.Fl \-statsFileMirror
+to specify a regular file for
+.Nm
+to periodically update instead.
+Statistics can be reset by removing the file, although because
+.Nm
+is also periodically regenerating the file there is a small race window during 
which such a removal is ignored.
 .Ss Logging
 In normal operation
 .Nm
@@ -1058,6 +1070,16 @@
 filesystem.
 A value of empty string disables the appearance of this file.
 Default is `stats'.
+This setting is ignored in NBD mode.
+.It Fl \-statsFileMirror=PATH
+Specify a file that should be periodically updated with a copy of the 
statistics file.
+This option is primarily intended for NBD mode but works in FUSE mode as well.
+.It Fl \-statsFileMirrorInterval=MILLIS
+Specify the update interval for the stats file mirror.
+Default value is every 1000ms.
+Ignored unless
+.Fl \-statsFileMirror
+is specified.
 .It Fl \-storageClass=TYPE
 Specify storage class.
 .Pp
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/s3backer-2.1.4/s3backer.1.in 
new/s3backer-2.1.5/s3backer.1.in
--- old/s3backer-2.1.4/s3backer.1.in    2025-05-06 18:58:43.000000000 +0200
+++ new/s3backer-2.1.5/s3backer.1.in    2025-05-16 23:50:48.000000000 +0200
@@ -358,6 +358,18 @@
 flag causes
 .Nm
 to function as a wrapper process for managing an NBD session; see below.
+.Pp
+When running in NBD mode, no
+.Nm
+filesystem is created and therefore no statistics file automatically appears.
+You can use
+.Fl \-statsFileMirror
+to specify a regular file for
+.Nm
+to periodically update instead.
+Statistics can be reset by removing the file, although because
+.Nm
+is also periodically regenerating the file there is a small race window during 
which such a removal is ignored.
 .Ss Logging
 In normal operation
 .Nm
@@ -1058,6 +1070,16 @@
 filesystem.
 A value of empty string disables the appearance of this file.
 Default is `stats'.
+This setting is ignored in NBD mode.
+.It Fl \-statsFileMirror=PATH
+Specify a file that should be periodically updated with a copy of the 
statistics file.
+This option is primarily intended for NBD mode but works in FUSE mode as well.
+.It Fl \-statsFileMirrorInterval=MILLIS
+Specify the update interval for the stats file mirror.
+Default value is every 1000ms.
+Ignored unless
+.Fl \-statsFileMirror
+is specified.
 .It Fl \-storageClass=TYPE
 Specify storage class.
 .Pp

Reply via email to