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(¶ms); 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