Hello community, here is the log from the commit of package sshfs for openSUSE:Factory checked in at 2018-10-04 19:02:22 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/sshfs (Old) and /work/SRC/openSUSE:Factory/.sshfs.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "sshfs" Thu Oct 4 19:02:22 2018 rev:29 rq:639882 version:2.10 Changes: -------- --- /work/SRC/openSUSE:Factory/sshfs/sshfs.changes 2017-07-27 11:13:42.277093877 +0200 +++ /work/SRC/openSUSE:Factory/.sshfs.new/sshfs.changes 2018-10-04 19:02:29.099140900 +0200 @@ -1,0 +2,20 @@ +Tue Oct 2 17:16:31 UTC 2018 - [email protected] + +- sshfs 2.10: + * Fix a crash due to a race condition when listing directory contents + * Document limited hardlink support + * Add support for more SSH options. + * Drop support for the nodelay workaround + * Drop support for the nodelaysrv workaround + The same effect (enabling NODELAY on the server side and + enabling X11 forwarding) can be achieved by explicitly passing + -o ForwardX11 + * Remove support for -o workaround=all + +------------------------------------------------------------------- +Tue Oct 2 16:50:34 UTC 2018 - [email protected] + +- Add source signature and verify source signature +- clean up spec file + +------------------------------------------------------------------- Old: ---- sshfs-2.9.tar.gz New: ---- sshfs-2.10.tar.gz sshfs-2.10.tar.gz.asc sshfs.keyring ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ sshfs.spec ++++++ --- /var/tmp/diff_new_pack.Nvdqwb/_old 2018-10-04 19:02:30.039139911 +0200 +++ /var/tmp/diff_new_pack.Nvdqwb/_new 2018-10-04 19:02:30.039139911 +0200 @@ -1,7 +1,7 @@ # # spec file for package sshfs # -# Copyright (c) 2017 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,18 +17,19 @@ Name: sshfs -Version: 2.9 +Version: 2.10 Release: 0 Summary: Filesystem client based on SSH file transfer protocol -License: GPL-2.0+ +License: GPL-2.0-or-later Group: System/Filesystems -Url: https://github.com/libfuse/sshfs +URL: https://github.com/libfuse/sshfs Source: https://github.com/libfuse/sshfs/releases/download/%{name}-%{version}/%{name}-%{version}.tar.gz -BuildRequires: automake -BuildRequires: fuse-devel +Source2: https://github.com/libfuse/sshfs/releases/download/%{name}-%{version}/%{name}-%{version}.tar.gz.asc +Source3: %{name}.keyring +BuildRequires: fuse-devel >= 2.3 BuildRequires: glib2-devel -Requires: fuse -BuildRoot: %{_tmppath}/%{name}-%{version}-build +BuildRequires: pkgconfig >= 0.9.0 +Requires: fuse >= 2.3 %description SSHFS is a filesystem client based on the SSH File Transfer Protocol. @@ -41,17 +42,15 @@ %setup -q %build -%{?suse_update_config:%{suse_update_config -f}} -autoreconf -fi -%configure --disable-sshnodelay -make %{?_smp_mflags} V=1 +%configure +make %{?_smp_mflags} %install -make %{?_smp_mflags} DESTDIR=%{buildroot} install +%make_install %files -%defattr(-,root,root) -%doc AUTHORS COPYING ChangeLog +%license COPYING +%doc AUTHORS %{_mandir}/*/* %{_bindir}/* ++++++ sshfs-2.9.tar.gz -> sshfs-2.10.tar.gz ++++++ ++++ 4402 lines of diff (skipped) ++++ retrying with extended exclude list diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/sshfs-2.9/AUTHORS new/sshfs-2.10/AUTHORS --- old/sshfs-2.9/AUTHORS 2017-04-17 22:05:45.000000000 +0200 +++ new/sshfs-2.10/AUTHORS 2017-08-03 18:00:34.000000000 +0200 @@ -21,6 +21,7 @@ Chris Wolfe <[email protected]> gala <[email protected]> George Vlahavas <[email protected]> +Jakub Jelen <[email protected]> Julio Merino <[email protected]> Julio Merino <[email protected]> Mike Kelly <[email protected]> @@ -30,4 +31,5 @@ Percy Jahn <[email protected]> Qais Patankar <[email protected]> Rian Hunter <[email protected]> +tpoindessous <[email protected]> Zoltan Kuscsik <[email protected]> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/sshfs-2.9/ChangeLog new/sshfs-2.10/ChangeLog --- old/sshfs-2.9/ChangeLog 2017-04-17 22:10:28.000000000 +0200 +++ new/sshfs-2.10/ChangeLog 1970-01-01 01:00:00.000000000 +0100 @@ -1,160 +0,0 @@ -Release 2.9 (2017-04-17) ------------------------- - -* Improved support for Cygwin. -* Various small bugfixes. - -Release 2.8 (2016-06-22) ------------------------- - -* Added support for the "fsync" extension. -* Fixed a build problem with bitbake - -Release 2.7 (2016-03-01) ------------------------- - -* Integrated osxfuse's copy of sshfs, which means that sshfs now works - on OS X out of the box. -* Added -o cache_max_size=N option to let users tune the maximum size of - the cache in number of entries. -* Added -o cache_clean_interval=N and -o cache_min_clean_interval=N - options to let users tune the cleaning behavior of the cache. - -Release 2.6 (2015-01-28) ------------------------- - -* New maintainer (Nikolaus Rath <[email protected]>) - -Release 2.5 (2014-01-14) ------------------------- - -* Some performance improvements for large directories. -* New `disable_hardlink` option. -* Various small bugfixes. - -Release 2.4 (2012-03-08) ------------------------- - -* New `slave` option. -* New `idmap`, `uidmap` and `gidmap` options. -* Various small bugfixes. - -Release 2.3 (2011-07-01) ------------------------- - -* Support hard link creation if server is OpenSSH 5.7 or later -* Small improvements and bug fixes -* Check mount point and options before connecting to ssh server -* New 'delay_connect' option - -Release 2.2 (2008-10-20) ------------------------- - -* Handle numerical IPv6 addresses enclosed in square brackets -* Handle commas in usernames - -Release 2.1 (2008-07-11) ------------------------- - -* Small improvements and bug fixes - -Release 2.0 (2008-04-23) ------------------------- - -* Support password authentication with pam_mount - -* Support atomic renames if server is OpenSSH 4.9 or later - -* Support getting disk usage if server is OpenSSH 5.1 or later - -* Small enhancements and bug fixes - -What is new in 1.9 ------------------- - -* Fix a serious bug, that could result in sshfs hanging, crashing, or - reporting out-of-memory - -What is new in 1.8 ------------------- - -* Bug fixes - -What is new in 1.7 ------------------- - -* Tolerate servers which print a banner on login - -* Small improvements - -What is new in 1.6 ------------------- - -* Workaround for missing truncate operation on old sftp servers - -* Bug fixes - -What is new in 1.5 ------------------- - -* Improvements to read performance. Now both read and write - throughput should be very close to 'scp' - -* If used with FUSE 2.6.0 or later, then perform better data caching. - This should show dramatic speed improvements when a file is opened - more than once - -* Bug fixes - -What is new in 1.4 ------------------- - -* Updated to version 25 of libfuse API - -* This means that the 'cp' of readonly file to sshfs bug is finally - solved (as long as using libfuse 2.5.0 or later *and* Linux 2.6.15 - or later) - -* Sshfs now works on FreeBSD - -* Added option to "transform" absolute symbolic links - -What is new in 1.3 ------------------- - -* Add workaround for failure to rename to an existing file - -* Simple user ID mapping - -* Estimate disk usage of files based on size - -* Report "infinite" disk space - -* Bug fixes - -What is new in 1.2 ------------------- - -* Better compatibility with different sftp servers - -* Automatic reconnect (optional) - -What is new in 1.1 ------------------- - -* Performance improvements: - - - directory content caching - - - symlink caching - - - asynchronous writeback - - - readahead - -* Fixed '-p' option - -What is new in 1.0 ------------------- - -* Initial release diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/sshfs-2.9/Makefile.am new/sshfs-2.10/Makefile.am --- old/sshfs-2.9/Makefile.am 2017-04-17 22:03:50.000000000 +0200 +++ new/sshfs-2.10/Makefile.am 2017-08-03 18:00:34.000000000 +0200 @@ -2,6 +2,8 @@ bin_PROGRAMS = sshfs +SUBDIRS = test + sshfs_SOURCES = sshfs.c cache.c cache.h if FUSE_OPT_COMPAT sshfs_SOURCES += compat/fuse_opt.c compat/fuse_opt.h @@ -15,28 +17,14 @@ sshfs_CPPFLAGS = -D_REENTRANT -DFUSE_USE_VERSION=26 -DLIBDIR=\"$(libdir)\" \ -DIDMAP_DEFAULT="\"$(IDMAP_DEFAULT)\"" -EXTRA_DIST = sshnodelay.c sshfs.1.in -CLEANFILES = sshnodelay.so sshfs.1 sshfs.1.tmp +EXTRA_DIST = sshfs.1.in meson.build +CLEANFILES = sshfs.1 sshfs.1.tmp dist_man_MANS = sshfs.1 sshfs.1: sshfs.1.in $(AM_V_GEN)sed \ - -e 's,__IDMAP_DEFAULT__,$(IDMAP_DEFAULT),g' \ - -e 's,__UNMOUNT_COMMAND__,$(UNMOUNT_COMMAND),g' \ + -e 's/[@]IDMAP_DEFAULT@/$(IDMAP_DEFAULT)/g' \ + -e 's/[@]UNMOUNT_COMMAND@/$(UNMOUNT_COMMAND)/g' \ <$(srcdir)/sshfs.1.in >sshfs.1.tmp || exit 1; \ mv sshfs.1.tmp sshfs.1 - -if SSH_NODELAY_SO -all-local: sshnodelay.so - -install-exec-local: sshnodelay.so - test -z "$(libdir)" || $(mkdir_p) "$(DESTDIR)$(libdir)" - $(INSTALL) -m 755 sshnodelay.so "$(DESTDIR)$(libdir)/sshnodelay.so" - -uninstall-local: - rm -f "$(DESTDIR)$(libdir)/sshnodelay.so" - -sshnodelay.so: - $(CC) -Wall -W -s --shared -fPIC $(sshnodelay_libs) sshnodelay.c -o sshnodelay.so -endif diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/sshfs-2.9/compile new/sshfs-2.10/compile --- old/sshfs-2.9/compile 2016-01-29 02:13:22.000000000 +0100 +++ new/sshfs-2.10/compile 2017-08-03 17:53:13.000000000 +0200 @@ -3,7 +3,7 @@ scriptversion=2012-10-14.11; # UTC -# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# Copyright (C) 1999-2014 Free Software Foundation, Inc. # Written by Tom Tromey <[email protected]>. # # This program is free software; you can redistribute it and/or modify diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/sshfs-2.9/config.h.in new/sshfs-2.10/config.h.in --- old/sshfs-2.9/config.h.in 2017-04-17 22:13:13.000000000 +0200 +++ new/sshfs-2.10/config.h.in 2017-08-03 18:00:47.000000000 +0200 @@ -21,8 +21,5 @@ /* Define to the version of this package. */ #undef PACKAGE_VERSION -/* Compile ssh NODELAY workaround */ -#undef SSH_NODELAY_WORKAROUND - /* Version number of package */ #undef VERSION diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/sshfs-2.9/configure.ac new/sshfs-2.10/configure.ac --- old/sshfs-2.9/configure.ac 2017-04-17 22:05:56.000000000 +0200 +++ new/sshfs-2.10/configure.ac 2017-08-03 18:00:34.000000000 +0200 @@ -1,4 +1,4 @@ -AC_INIT(sshfs, 2.9) +AC_INIT(sshfs, 2.10) AC_CANONICAL_TARGET AM_INIT_AUTOMAKE([foreign subdir-objects]) AM_CONFIG_HEADER(config.h) @@ -8,8 +8,6 @@ CFLAGS="$CFLAGS -Wall -W" LIBS= AC_SEARCH_LIBS(dlsym, [dl]) -sshnodelay_libs=$LIBS -AC_SUBST(sshnodelay_libs) LIBS= case "$target_os" in @@ -18,31 +16,6 @@ *) osname=unknown;; esac -AC_ARG_ENABLE(sshnodelay, - [ --disable-sshnodelay Don't compile NODELAY workaround for ssh]) - -if test -z "$enable_sshnodelay"; then - AC_MSG_CHECKING([OpenSSH version]) - [eval `ssh -V 2>&1 | sed -n 's/^OpenSSH_\([1-9][0-9]*\)\.\([0-9][0-9]*\).*/ssh_major=\1 ssh_minor=\2/p'`] - if test "x$ssh_major" != x -a "x$ssh_minor" != x; then - if test $ssh_major -gt 4 -o \( $ssh_major = 4 -a $ssh_minor -ge 4 \); then - AC_MSG_RESULT([$ssh_major.$ssh_minor >= 4.4, disabling NODELAY workaround]) - enable_sshnodelay=no - else - AC_MSG_RESULT([$ssh_major.$ssh_minor < 4.4, enabling NODELAY workaround]) - enable_sshnodelay=yes - fi - else - AC_MSG_RESULT([not found]) - fi -fi - -if test "$enable_sshnodelay" = "yes"; then - AC_DEFINE(SSH_NODELAY_WORKAROUND, 1, [Compile ssh NODELAY workaround]) -fi - -AM_CONDITIONAL(SSH_NODELAY_SO, test "$enable_sshnodelay" = "yes") - export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH PKG_CHECK_MODULES([SSHFS], [fuse >= 2.3 glib-2.0 gthread-2.0]) have_fuse_opt_parse=no @@ -67,5 +40,5 @@ esac AC_SUBST(IDMAP_DEFAULT) -AC_CONFIG_FILES([Makefile]) +AC_CONFIG_FILES([Makefile test/Makefile]) AC_OUTPUT diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/sshfs-2.9/meson.build new/sshfs-2.10/meson.build --- old/sshfs-2.9/meson.build 1970-01-01 01:00:00.000000000 +0100 +++ new/sshfs-2.10/meson.build 2017-08-03 18:00:34.000000000 +0200 @@ -0,0 +1,64 @@ +project('sshfs', 'c', version: '2.10.0', + meson_version: '>= 0.38', + default_options: [ 'buildtype=plain' ]) + +add_global_arguments('-D_REENTRANT', '-DHAVE_CONFIG_H', '-O2', '-g', + '-Wall', '-Wextra', '-Wno-sign-compare', + '-Wmissing-declarations', '-Wwrite-strings', + language: 'c') + +# Some (stupid) GCC versions warn about unused return values even when they are +# casted to void. This makes -Wunused-result pretty useless, since there is no +# way to suppress the warning when we really *want* to ignore the value. +cc = meson.get_compiler('c') +code = ''' +__attribute__((warn_unused_result)) int get_4() { + return 4; +} +int main(void) { + (void) get_4(); + return 0; +}''' +if not cc.compiles(code, args: [ '-O0', '-Werror=unused-result' ]) + message('Compiler warns about unused result even when casting to void') + add_global_arguments('-Wno-unused-result', language: 'c') +endif + + +cfg = configuration_data() + +cfg.set_quoted('PACKAGE_VERSION', meson.project_version()) + +include_dirs = [ include_directories('.') ] +sshfs_sources = ['sshfs.c', 'cache.c'] +if target_machine.system() == 'darwin' + cfg.set_quoted('IDMAP_DEFAULT', 'user') + sshfs_sources += [ 'compat/fuse_opt.c', 'compat/darwin_compat.c' ] + include_dirs += [ include_directories('compat') ] +else + cfg.set_quoted('IDMAP_DEFAULT', 'none') +endif + +configure_file(input: 'sshfs.1.in', + output: 'sshfs.1', + configuration : cfg) +configure_file(output: 'config.h', + configuration : cfg) + +sshfs_deps = [ dependency('fuse', version: '>= 2.3'), + dependency('glib-2.0'), + dependency('gthread-2.0') ] + +executable('sshfs', sshfs_sources, + include_directories: include_dirs, + dependencies: sshfs_deps, + c_args: ['-DFUSE_USE_VERSION=26'], + install: true, + install_dir: get_option('bindir')) + +# This is a little ugly. Is there a better way to tell Meson that the +# manpage is in the build directory? +install_man(join_paths(meson.current_build_dir(), 'sshfs.1')) + +subdir('test') + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/sshfs-2.9/missing new/sshfs-2.10/missing --- old/sshfs-2.9/missing 2016-01-29 02:13:22.000000000 +0100 +++ new/sshfs-2.10/missing 2017-08-03 17:53:13.000000000 +0200 @@ -3,7 +3,7 @@ scriptversion=2013-10-28.13; # UTC -# Copyright (C) 1996-2013 Free Software Foundation, Inc. +# Copyright (C) 1996-2014 Free Software Foundation, Inc. # Originally written by Fran,cois Pinard <[email protected]>, 1996. # This program is free software; you can redistribute it and/or modify diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/sshfs-2.9/sshfs.1.in new/sshfs-2.10/sshfs.1.in --- old/sshfs-2.9/sshfs.1.in 2017-04-17 22:03:50.000000000 +0200 +++ new/sshfs-2.10/sshfs.1.in 2017-07-12 17:06:18.000000000 +0200 @@ -7,7 +7,7 @@ \fBsshfs\fP [\fIuser\fP@]\fBhost\fP:[\fIdir\fP] \fBmountpoint\fP [\fIoptions\fP] .SS unmounting .TP -\fB__UNMOUNT_COMMAND__ mountpoint\fP +\fB@UNMOUNT_COMMAND@ mountpoint\fP .SH DESCRIPTION SSHFS (Secure SHell FileSystem) is a file system for Linux (and other operating systems with a FUSE implementation, such as Mac OS X or FreeBSD) @@ -97,7 +97,7 @@ .RE .TP \fB\-o\fR idmap=TYPE -user/group ID mapping (default: __IDMAP_DEFAULT__) +user/group ID mapping (default: @IDMAP_DEFAULT@) .RS 8 .TP none diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/sshfs-2.9/sshfs.c new/sshfs-2.10/sshfs.c --- old/sshfs-2.9/sshfs.c 2017-04-17 22:03:50.000000000 +0200 +++ new/sshfs-2.10/sshfs.c 2017-08-03 17:51:39.000000000 +0200 @@ -132,8 +132,6 @@ #define SFTP_SERVER_PATH "/usr/lib/sftp-server" -#define SSHNODELAY_SO "sshnodelay.so" - /* Asynchronous readdir parameters */ #define READDIR_START 2 #define READDIR_MAX 32 @@ -216,8 +214,6 @@ struct fuse_args ssh_args; char *workarounds; int rename_workaround; - int nodelay_workaround; - int nodelaysrv_workaround; int truncate_workaround; int buflimit_workaround; int fstat_workaround; @@ -297,6 +293,7 @@ "AddressFamily", "BatchMode", "BindAddress", + "CertificateFile", "ChallengeResponseAuthentication", "CheckHostIP", "Cipher", @@ -307,28 +304,40 @@ "ConnectTimeout", "ControlMaster", "ControlPath", + "ControlPersist", + "FingerprintHash", "GlobalKnownHostsFile", "GSSAPIAuthentication", "GSSAPIDelegateCredentials", "HostbasedAuthentication", + "HostbasedKeyTypes", "HostKeyAlgorithms", "HostKeyAlias", "HostName", "IdentitiesOnly", "IdentityFile", + "IdentityAgent", + "IPQoS", "KbdInteractiveAuthentication", "KbdInteractiveDevices", + "KexAlgorithms", "LocalCommand", "LogLevel", "MACs", "NoHostAuthenticationForLocalhost", "NumberOfPasswordPrompts", "PasswordAuthentication", + "PermitLocalCommand", + "PKCS11Provider", "Port", "PreferredAuthentications", "ProxyCommand", + "ProxyJump", + "ProxyUseFdpass", + "PubkeyAcceptedKeyTypes" "PubkeyAuthentication", "RekeyLimit", + "RevokedHostKeys", "RhostsRSAAuthentication", "RSAAuthentication", "ServerAliveCountMax", @@ -336,9 +345,11 @@ "SmartcardDevice", "StrictHostKeyChecking", "TCPKeepAlive", + "UpdateHostKeys", "UsePrivilegedPort", "UserKnownHostsFile", "VerifyHostKeyDNS", + "VisualHostKey", NULL, }; @@ -408,23 +419,11 @@ static struct fuse_opt workaround_opts[] = { SSHFS_OPT("none", rename_workaround, 0), - SSHFS_OPT("none", nodelay_workaround, 0), - SSHFS_OPT("none", nodelaysrv_workaround, 0), SSHFS_OPT("none", truncate_workaround, 0), SSHFS_OPT("none", buflimit_workaround, 0), SSHFS_OPT("none", fstat_workaround, 0), - SSHFS_OPT("all", rename_workaround, 1), - SSHFS_OPT("all", nodelay_workaround, 1), - SSHFS_OPT("all", nodelaysrv_workaround, 1), - SSHFS_OPT("all", truncate_workaround, 1), - SSHFS_OPT("all", buflimit_workaround, 1), - SSHFS_OPT("all", fstat_workaround, 1), SSHFS_OPT("rename", rename_workaround, 1), SSHFS_OPT("norename", rename_workaround, 0), - SSHFS_OPT("nodelay", nodelay_workaround, 1), - SSHFS_OPT("nonodelay", nodelay_workaround, 0), - SSHFS_OPT("nodelaysrv", nodelaysrv_workaround, 1), - SSHFS_OPT("nonodelaysrv", nodelaysrv_workaround, 0), SSHFS_OPT("truncate", truncate_workaround, 1), SSHFS_OPT("notruncate", truncate_workaround, 0), SSHFS_OPT("buflimit", buflimit_workaround, 1), @@ -877,85 +876,6 @@ _exit(1); } -#ifdef SSH_NODELAY_WORKAROUND -static int do_ssh_nodelay_workaround(void) -{ -#ifdef __APPLE__ - char *oldpreload = getenv("DYLD_INSERT_LIBRARIES"); -#else - char *oldpreload = getenv("LD_PRELOAD"); -#endif - char *newpreload; - char sopath[PATH_MAX]; - int res; - -#ifdef __APPLE__ - char *sshfs_program_path_base = NULL; - if (!sshfs_program_path[0]) { - goto nobundle; - } - sshfs_program_path_base = dirname(sshfs_program_path); - if (!sshfs_program_path_base) { - goto nobundle; - } - snprintf(sopath, sizeof(sopath), "%s/%s", sshfs_program_path_base, - SSHNODELAY_SO); - res = access(sopath, R_OK); - if (res == -1) { - goto nobundle; - } - goto pathok; - -nobundle: -#endif /* __APPLE__ */ - - snprintf(sopath, sizeof(sopath), "%s/%s", LIBDIR, SSHNODELAY_SO); - res = access(sopath, R_OK); - if (res == -1) { - char *s; - if (!realpath(sshfs.progname, sopath)) - return -1; - - s = strrchr(sopath, '/'); - if (!s) - s = sopath; - else - s++; - - if (s + strlen(SSHNODELAY_SO) >= sopath + sizeof(sopath)) - return -1; - - strcpy(s, SSHNODELAY_SO); - res = access(sopath, R_OK); - if (res == -1) { - fprintf(stderr, "sshfs: cannot find %s\n", - SSHNODELAY_SO); - return -1; - } - } - -#ifdef __APPLE__ -pathok: -#endif - - newpreload = g_strdup_printf("%s%s%s", - oldpreload ? oldpreload : "", - oldpreload ? " " : "", - sopath); - -#ifdef __APPLE__ - if (!newpreload || setenv("DYLD_INSERT_LIBRARIES", newpreload, 1) == -1) - fprintf(stderr, "warning: failed set DYLD_INSERT_LIBRARIES for ssh nodelay workaround\n"); -#else /* !__APPLE__ */ - if (!newpreload || setenv("LD_PRELOAD", newpreload, 1) == -1) { - fprintf(stderr, "warning: failed set LD_PRELOAD " - "for ssh nodelay workaround\n"); - } -#endif /* __APPLE__ */ - g_free(newpreload); - return 0; -} -#endif static int pty_expect_loop(void) { @@ -1089,29 +1009,6 @@ } else if (pid == 0) { int devnull; -#ifdef SSH_NODELAY_WORKAROUND - if (sshfs.nodelay_workaround && - do_ssh_nodelay_workaround() == -1) { - fprintf(stderr, - "warning: ssh nodelay workaround disabled\n"); - } -#endif - - if (sshfs.nodelaysrv_workaround) { - int i; - /* - * Hack to work around missing TCP_NODELAY - * setting in sshd - */ - for (i = 1; i < sshfs.ssh_args.argc; i++) { - if (strcmp(sshfs.ssh_args.argv[i], "-x") == 0) { - replace_arg(&sshfs.ssh_args.argv[i], - "-X"); - break; - } - } - } - devnull = open("/dev/null", O_WRONLY); if (dup2(sockpair[1], 0) == -1 || dup2(sockpair[1], 1) == -1) { @@ -2000,6 +1897,9 @@ &buf, SSH_FXP_ATTRS, &outbuf); if (!err) { err = buf_get_attrs(&outbuf, stbuf, NULL); +#ifdef __APPLE__ + stbuf->st_blksize = 0; +#endif buf_free(&outbuf); } buf_free(&buf); @@ -2175,11 +2075,16 @@ outstanding--; if (done) { + /* We need to cache want_reply, since processing + thread may free req right after unlock() if + want_reply == 0 */ + int want_reply; pthread_mutex_lock(&sshfs.lock); if (sshfs_req_pending(req)) req->want_reply = 0; + want_reply = req->want_reply; pthread_mutex_unlock(&sshfs.lock); - if (!req->want_reply) + if (!want_reply) continue; } @@ -2642,7 +2547,8 @@ int err; (void) isdatasync; - if (err = sshfs_flush(path, fi)) + err = sshfs_flush(path, fi); + if (err) return err; if (!sshfs.ext_fsync) @@ -3203,6 +3109,9 @@ err = sftp_request(SSH_FXP_FSTAT, &buf, SSH_FXP_ATTRS, &outbuf); if (!err) { err = buf_get_attrs(&outbuf, stbuf, NULL); +#ifdef __APPLE__ + stbuf->st_blksize = 0; +#endif buf_free(&outbuf); } buf_free(&buf); @@ -3416,14 +3325,10 @@ " cache if full (default: 5)\n" " -o workaround=LIST colon separated list of workarounds\n" " none no workarounds enabled\n" -" all all workarounds enabled\n" " [no]rename fix renaming to existing file (default: off)\n" -#ifdef SSH_NODELAY_WORKAROUND -" [no]nodelay set nodelay tcp flag in ssh (default: on)\n" -#endif -" [no]nodelaysrv set nodelay tcp flag in sshd (default: off)\n" " [no]truncate fix truncate for old servers (default: off)\n" " [no]buflimit fix buffer fillup bug in server (default: on)\n" +" [no]fstat fix fstat for old servers (default: off)\n" " -o idmap=TYPE user/group ID mapping (default: " IDMAP_DEFAULT ")\n" " none no translation of the ID space\n" " user only translate UID/GID of connecting user\n" @@ -3543,10 +3448,14 @@ return -1; } -int parse_workarounds(void) +static int parse_workarounds(void) { int res; - char *argv[] = { "", "-o", sshfs.workarounds, NULL }; + /* Need separate variables because literals are const + char */ + char argv0[] = ""; + char argv1[] = "-o"; + char *argv[] = { argv0, argv1, sshfs.workarounds, NULL }; struct fuse_args args = FUSE_ARGS_INIT(3, argv); char *s = sshfs.workarounds; if (!s) @@ -3952,14 +3861,11 @@ memset(sshfs_program_path, 0, PATH_MAX); } #endif /* __APPLE__ */ - g_thread_init(NULL); sshfs.blksize = 4096; /* SFTP spec says all servers should allow at least 32k I/O */ sshfs.max_read = 32768; sshfs.max_write = 32768; - sshfs.nodelay_workaround = 1; - sshfs.nodelaysrv_workaround = 0; #ifdef __APPLE__ sshfs.rename_workaround = 1; #else @@ -4115,7 +4021,7 @@ char *mountpoint; int multithreaded; int foreground; -#if !defined(__CYGWIN__) +#if !defined(__APPLE__) && !defined(__CYGWIN__) struct stat st; #endif @@ -4129,14 +4035,14 @@ foreground = 1; } -#if !defined(__CYGWIN__) +#if !defined(__APPLE__) && !defined(__CYGWIN__) res = stat(mountpoint, &st); if (res == -1) { perror(mountpoint); exit(1); } sshfs.mnt_mode = st.st_mode; -#elif defined(__CYGWIN__) +#else sshfs.mnt_mode = S_IFDIR | 0755; #endif @@ -4158,6 +4064,13 @@ exit(1); } + res = fuse_set_signal_handlers(fuse_get_session(fuse)); + if (res == -1) { + fuse_unmount(mountpoint, ch); + fuse_destroy(fuse); + exit(1); + } + /* * FIXME: trim $PATH so it doesn't contain anything inside the * mountpoint, which would deadlock. @@ -4170,9 +4083,6 @@ } res = fuse_daemonize(foreground); - if (res != -1) - res = fuse_set_signal_handlers(fuse_get_session(fuse)); - if (res == -1) { fuse_unmount(mountpoint, ch); fuse_destroy(fuse); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/sshfs-2.9/sshnodelay.c new/sshfs-2.10/sshnodelay.c --- old/sshfs-2.9/sshnodelay.c 2016-02-11 04:54:13.000000000 +0100 +++ new/sshfs-2.10/sshnodelay.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,59 +0,0 @@ -#define _GNU_SOURCE -#include <dlfcn.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <netinet/tcp.h> - -/* Wrapper around connect(2) to explicitly set TCP_NODELAY. */ -static int nodelay_connect( - int (*real_connect)(int, const struct sockaddr *, socklen_t), - int sock, const struct sockaddr *addr, socklen_t addrlen) -{ - int res = real_connect(sock, addr, addrlen); - if (!res && addr->sa_family == AF_INET) { - int opt = 1; - setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)); - } - return res; -} - -#if __APPLE__ - -/* OS X does not have LD_PRELOAD but has DYLD_INSERT_LIBRARIES. The right - * environment variable is set by sshfs.c when attempting to load the - * sshnodelay workaround. - * - * However, things are not that simple: DYLD_INSERT_LIBRARIES does not - * behave exactly like LD_PRELOAD. Instead, the dyld dynamic linker will - * look for __DATA __interpose sections on the libraries given via the - * DYLD_INSERT_LIBRARIES variable. The contents of this section are pairs - * of replacement functions and functions to be replaced, respectively. - * Prepare such section here. */ - -int custom_connect(int sock, const struct sockaddr *addr, socklen_t addrlen); - -typedef struct interpose_s { - void *new_func; - void *orig_func; -} interpose_t; - -static const interpose_t interposers[] \ - __attribute__ ((section("__DATA, __interpose"))) = { - { (void *)custom_connect, (void *)connect }, -}; - -int custom_connect(int sock, const struct sockaddr *addr, socklen_t addrlen) -{ - return nodelay_connect(connect, sock, addr, addrlen); -} - -#else /* !__APPLE__ */ - -int connect(int sock, const struct sockaddr *addr, socklen_t addrlen) -{ - return nodelay_connect(dlsym(RTLD_NEXT, "connect"), - sock, addr, addrlen); -} - -#endif /* !__APPLE__ */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/sshfs-2.9/test/Makefile.am new/sshfs-2.10/test/Makefile.am --- old/sshfs-2.9/test/Makefile.am 1970-01-01 01:00:00.000000000 +0100 +++ new/sshfs-2.10/test/Makefile.am 2017-08-03 18:00:34.000000000 +0200 @@ -0,0 +1,4 @@ +## Process this file with automake to produce Makefile.in + +EXTRA_DIST = meson.build conftest.py pytest.ini test_sshfs.py \ + util.py wrong_command.c diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/sshfs-2.9/test/conftest.py new/sshfs-2.10/test/conftest.py --- old/sshfs-2.9/test/conftest.py 1970-01-01 01:00:00.000000000 +0100 +++ new/sshfs-2.10/test/conftest.py 2017-07-12 17:10:18.000000000 +0200 @@ -0,0 +1,89 @@ +import sys +import pytest +import time +import re + +# If a test fails, wait a moment before retrieving the captured +# stdout/stderr. When using a server process, this makes sure that we capture +# any potential output of the server that comes *after* a test has failed. For +# example, if a request handler raises an exception, the server first signals an +# error to FUSE (causing the test to fail), and then logs the exception. Without +# the extra delay, the exception will go into nowhere. [email protected] +def pytest_pyfunc_call(pyfuncitem): + outcome = yield + failed = outcome.excinfo is not None + if failed: + time.sleep(1) + [email protected]() +def pass_capfd(request, capfd): + '''Provide capfd object to UnitTest instances''' + request.instance.capfd = capfd + +def check_test_output(capfd): + (stdout, stderr) = capfd.readouterr() + + # Write back what we've read (so that it will still be printed. + sys.stdout.write(stdout) + sys.stderr.write(stderr) + + # Strip out false positives + for (pattern, flags, count) in capfd.false_positives: + cp = re.compile(pattern, flags) + (stdout, cnt) = cp.subn('', stdout, count=count) + if count == 0 or count - cnt > 0: + stderr = cp.sub('', stderr, count=count - cnt) + + patterns = [ r'\b{}\b'.format(x) for x in + ('exception', 'error', 'warning', 'fatal', 'traceback', + 'fault', 'crash(?:ed)?', 'abort(?:ed)', + 'uninitiali[zs]ed') ] + patterns += ['^==[0-9]+== '] + for pattern in patterns: + cp = re.compile(pattern, re.IGNORECASE | re.MULTILINE) + hit = cp.search(stderr) + if hit: + raise AssertionError('Suspicious output to stderr (matched "%s")' % hit.group(0)) + hit = cp.search(stdout) + if hit: + raise AssertionError('Suspicious output to stdout (matched "%s")' % hit.group(0)) + +def register_output(self, pattern, count=1, flags=re.MULTILINE): + '''Register *pattern* as false positive for output checking + + This prevents the test from failing because the output otherwise + appears suspicious. + ''' + + self.false_positives.append((pattern, flags, count)) + +# This is a terrible hack that allows us to access the fixtures from the +# pytest_runtest_call hook. Among a lot of other hidden assumptions, it probably +# relies on tests running sequential (i.e., don't dare to use e.g. the xdist +# plugin) +current_capfd = None [email protected]_fixture(autouse=True) +def save_cap_fixtures(request, capfd): + global current_capfd + capfd.false_positives = [] + + # Monkeypatch in a function to register false positives + type(capfd).register_output = register_output + + if request.config.getoption('capture') == 'no': + capfd = None + current_capfd = capfd + bak = current_capfd + yield + + # Try to catch problems with this hack (e.g. when running tests + # simultaneously) + assert bak is current_capfd + current_capfd = None + [email protected](trylast=True) +def pytest_runtest_call(item): + capfd = current_capfd + if capfd is not None: + check_test_output(capfd) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/sshfs-2.9/test/meson.build new/sshfs-2.10/test/meson.build --- old/sshfs-2.9/test/meson.build 1970-01-01 01:00:00.000000000 +0100 +++ new/sshfs-2.10/test/meson.build 2017-07-12 17:10:18.000000000 +0200 @@ -0,0 +1,11 @@ +test_scripts = [ 'conftest.py', 'pytest.ini', 'test_sshfs.py', + 'util.py' ] +custom_target('test_scripts', input: test_scripts, + output: test_scripts, build_by_default: true, + command: ['cp', '-fPu', '--preserve=mode', + '@INPUT@', meson.current_build_dir() ]) + +# Provide something helpful when running 'ninja test' +wrong_cmd = executable('wrong_command', 'wrong_command.c', + install: false) +test('wrong_cmd', wrong_cmd) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/sshfs-2.9/test/pytest.ini new/sshfs-2.10/test/pytest.ini --- old/sshfs-2.9/test/pytest.ini 1970-01-01 01:00:00.000000000 +0100 +++ new/sshfs-2.10/test/pytest.ini 2017-07-12 17:10:18.000000000 +0200 @@ -0,0 +1,2 @@ +[pytest] +addopts = --verbose --assert=rewrite --tb=native -x -r a diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/sshfs-2.9/test/test_sshfs.py new/sshfs-2.10/test/test_sshfs.py --- old/sshfs-2.9/test/test_sshfs.py 1970-01-01 01:00:00.000000000 +0100 +++ new/sshfs-2.10/test/test_sshfs.py 2017-08-03 17:51:39.000000000 +0200 @@ -0,0 +1,378 @@ +#!/usr/bin/env python3 + +if __name__ == '__main__': + import pytest + import sys + sys.exit(pytest.main([__file__] + sys.argv[1:])) + +import subprocess +import os +import sys +import pytest +import stat +import shutil +import filecmp +import errno +from tempfile import NamedTemporaryFile +from util import (wait_for_mount, umount, cleanup, base_cmdline, + basename, fuse_test_marker, safe_sleep) +from os.path import join as pjoin + +TEST_FILE = __file__ + +pytestmark = fuse_test_marker() + +with open(TEST_FILE, 'rb') as fh: + TEST_DATA = fh.read() + +def name_generator(__ctr=[0]): + __ctr[0] += 1 + return 'testfile_%d' % __ctr[0] + [email protected]("debug", (False, True)) [email protected]("cache_timeout", (0, 1)) +def test_sshfs(tmpdir, debug, cache_timeout, capfd): + + # Avoid false positives from debug messages + #if debug: + # capfd.register_output(r'^ unique: [0-9]+, error: -[0-9]+ .+$', + # count=0) + + # Test if we can ssh into localhost without password + try: + res = subprocess.call(['ssh', '-o', 'KbdInteractiveAuthentication=no', + '-o', 'ChallengeResponseAuthentication=no', + '-o', 'PasswordAuthentication=no', + 'localhost', '--', 'true'], stdin=subprocess.DEVNULL, + timeout=10) + except subprocess.TimeoutExpired: + res = 1 + if res != 0: + pytest.fail('Unable to ssh into localhost without password prompt.') + + mnt_dir = str(tmpdir.mkdir('mnt')) + src_dir = str(tmpdir.mkdir('src')) + + cmdline = base_cmdline + [ pjoin(basename, 'sshfs'), + '-f', 'localhost:' + src_dir, mnt_dir ] + if debug: + cmdline += [ '-o', 'sshfs_debug' ] + + # SSHFS Cache + if cache_timeout == 0: + cmdline += [ '-o', 'cache=no' ] + else: + cmdline += [ '-o', 'cache_timeout=%d' % cache_timeout ] + + # FUSE Cache + cmdline += [ '-o', 'entry_timeout=0', + '-o', 'attr_timeout=0' ] + + + new_env = dict(os.environ) # copy, don't modify + + # Abort on warnings from glib + new_env['G_DEBUG'] = 'fatal-warnings' + + mount_process = subprocess.Popen(cmdline, env=new_env) + try: + wait_for_mount(mount_process, mnt_dir) + + tst_statvfs(mnt_dir) + tst_readdir(src_dir, mnt_dir) + tst_open_read(src_dir, mnt_dir) + tst_open_write(src_dir, mnt_dir) + tst_create(mnt_dir) + tst_passthrough(src_dir, mnt_dir, cache_timeout) + tst_mkdir(mnt_dir) + tst_rmdir(src_dir, mnt_dir, cache_timeout) + tst_unlink(src_dir, mnt_dir, cache_timeout) + tst_symlink(mnt_dir) + if os.getuid() == 0: + tst_chown(mnt_dir) + + # SSHFS only supports one second resolution when setting + # file timestamps. + tst_utimens(mnt_dir, tol=1) + + tst_link(mnt_dir) + tst_truncate_path(mnt_dir) + tst_truncate_fd(mnt_dir) + tst_open_unlink(mnt_dir) + except: + cleanup(mnt_dir) + raise + else: + umount(mount_process, mnt_dir) + +def tst_unlink(src_dir, mnt_dir, cache_timeout): + name = name_generator() + fullname = mnt_dir + "/" + name + with open(pjoin(src_dir, name), 'wb') as fh: + fh.write(b'hello') + if cache_timeout: + safe_sleep(cache_timeout+1) + assert name in os.listdir(mnt_dir) + os.unlink(fullname) + with pytest.raises(OSError) as exc_info: + os.stat(fullname) + assert exc_info.value.errno == errno.ENOENT + assert name not in os.listdir(mnt_dir) + assert name not in os.listdir(src_dir) + +def tst_mkdir(mnt_dir): + dirname = name_generator() + fullname = mnt_dir + "/" + dirname + os.mkdir(fullname) + fstat = os.stat(fullname) + assert stat.S_ISDIR(fstat.st_mode) + assert os.listdir(fullname) == [] + assert fstat.st_nlink in (1,2) + assert dirname in os.listdir(mnt_dir) + +def tst_rmdir(src_dir, mnt_dir, cache_timeout): + name = name_generator() + fullname = mnt_dir + "/" + name + os.mkdir(pjoin(src_dir, name)) + if cache_timeout: + safe_sleep(cache_timeout+1) + assert name in os.listdir(mnt_dir) + os.rmdir(fullname) + with pytest.raises(OSError) as exc_info: + os.stat(fullname) + assert exc_info.value.errno == errno.ENOENT + assert name not in os.listdir(mnt_dir) + assert name not in os.listdir(src_dir) + +def tst_symlink(mnt_dir): + linkname = name_generator() + fullname = mnt_dir + "/" + linkname + os.symlink("/imaginary/dest", fullname) + fstat = os.lstat(fullname) + assert stat.S_ISLNK(fstat.st_mode) + assert os.readlink(fullname) == "/imaginary/dest" + assert fstat.st_nlink == 1 + assert linkname in os.listdir(mnt_dir) + +def tst_create(mnt_dir): + name = name_generator() + fullname = pjoin(mnt_dir, name) + with pytest.raises(OSError) as exc_info: + os.stat(fullname) + assert exc_info.value.errno == errno.ENOENT + assert name not in os.listdir(mnt_dir) + + fd = os.open(fullname, os.O_CREAT | os.O_RDWR) + os.close(fd) + + assert name in os.listdir(mnt_dir) + fstat = os.lstat(fullname) + assert stat.S_ISREG(fstat.st_mode) + assert fstat.st_nlink == 1 + assert fstat.st_size == 0 + +def tst_chown(mnt_dir): + filename = pjoin(mnt_dir, name_generator()) + os.mkdir(filename) + fstat = os.lstat(filename) + uid = fstat.st_uid + gid = fstat.st_gid + + uid_new = uid + 1 + os.chown(filename, uid_new, -1) + fstat = os.lstat(filename) + assert fstat.st_uid == uid_new + assert fstat.st_gid == gid + + gid_new = gid + 1 + os.chown(filename, -1, gid_new) + fstat = os.lstat(filename) + assert fstat.st_uid == uid_new + assert fstat.st_gid == gid_new + +def tst_open_read(src_dir, mnt_dir): + name = name_generator() + with open(pjoin(src_dir, name), 'wb') as fh_out, \ + open(TEST_FILE, 'rb') as fh_in: + shutil.copyfileobj(fh_in, fh_out) + + assert filecmp.cmp(pjoin(mnt_dir, name), TEST_FILE, False) + +def tst_open_write(src_dir, mnt_dir): + name = name_generator() + fd = os.open(pjoin(src_dir, name), + os.O_CREAT | os.O_RDWR) + os.close(fd) + fullname = pjoin(mnt_dir, name) + with open(fullname, 'wb') as fh_out, \ + open(TEST_FILE, 'rb') as fh_in: + shutil.copyfileobj(fh_in, fh_out) + + assert filecmp.cmp(fullname, TEST_FILE, False) + +def tst_open_unlink(mnt_dir): + name = pjoin(mnt_dir, name_generator()) + data1 = b'foo' + data2 = b'bar' + fullname = pjoin(mnt_dir, name) + with open(fullname, 'wb+', buffering=0) as fh: + fh.write(data1) + os.unlink(fullname) + with pytest.raises(OSError) as exc_info: + os.stat(fullname) + assert exc_info.value.errno == errno.ENOENT + assert name not in os.listdir(mnt_dir) + fh.write(data2) + fh.seek(0) + assert fh.read() == data1+data2 + +def tst_statvfs(mnt_dir): + os.statvfs(mnt_dir) + +def tst_link(mnt_dir): + name1 = pjoin(mnt_dir, name_generator()) + name2 = pjoin(mnt_dir, name_generator()) + shutil.copyfile(TEST_FILE, name1) + assert filecmp.cmp(name1, TEST_FILE, False) + + fstat1 = os.lstat(name1) + assert fstat1.st_nlink == 1 + + os.link(name1, name2) + + fstat1 = os.lstat(name1) + fstat2 = os.lstat(name2) + for attr in ('st_mode', 'st_dev', 'st_uid', 'st_gid', + 'st_size', 'st_atime', 'st_mtime', 'st_ctime'): + assert getattr(fstat1, attr) == getattr(fstat2, attr) + assert os.path.basename(name2) in os.listdir(mnt_dir) + assert filecmp.cmp(name1, name2, False) + + os.unlink(name2) + + assert os.path.basename(name2) not in os.listdir(mnt_dir) + with pytest.raises(FileNotFoundError): + os.lstat(name2) + + os.unlink(name1) + +def tst_readdir(src_dir, mnt_dir): + newdir = name_generator() + src_newdir = pjoin(src_dir, newdir) + mnt_newdir = pjoin(mnt_dir, newdir) + file_ = src_newdir + "/" + name_generator() + subdir = src_newdir + "/" + name_generator() + subfile = subdir + "/" + name_generator() + + os.mkdir(src_newdir) + shutil.copyfile(TEST_FILE, file_) + os.mkdir(subdir) + shutil.copyfile(TEST_FILE, subfile) + + listdir_is = os.listdir(mnt_newdir) + listdir_is.sort() + listdir_should = [ os.path.basename(file_), os.path.basename(subdir) ] + listdir_should.sort() + assert listdir_is == listdir_should + + os.unlink(file_) + os.unlink(subfile) + os.rmdir(subdir) + os.rmdir(src_newdir) + +def tst_truncate_path(mnt_dir): + assert len(TEST_DATA) > 1024 + + filename = pjoin(mnt_dir, name_generator()) + with open(filename, 'wb') as fh: + fh.write(TEST_DATA) + + fstat = os.stat(filename) + size = fstat.st_size + assert size == len(TEST_DATA) + + # Add zeros at the end + os.truncate(filename, size + 1024) + assert os.stat(filename).st_size == size + 1024 + with open(filename, 'rb') as fh: + assert fh.read(size) == TEST_DATA + assert fh.read(1025) == b'\0' * 1024 + + # Truncate data + os.truncate(filename, size - 1024) + assert os.stat(filename).st_size == size - 1024 + with open(filename, 'rb') as fh: + assert fh.read(size) == TEST_DATA[:size-1024] + + os.unlink(filename) + +def tst_truncate_fd(mnt_dir): + assert len(TEST_DATA) > 1024 + with NamedTemporaryFile('w+b', 0, dir=mnt_dir) as fh: + fd = fh.fileno() + fh.write(TEST_DATA) + fstat = os.fstat(fd) + size = fstat.st_size + assert size == len(TEST_DATA) + + # Add zeros at the end + os.ftruncate(fd, size + 1024) + assert os.fstat(fd).st_size == size + 1024 + fh.seek(0) + assert fh.read(size) == TEST_DATA + assert fh.read(1025) == b'\0' * 1024 + + # Truncate data + os.ftruncate(fd, size - 1024) + assert os.fstat(fd).st_size == size - 1024 + fh.seek(0) + assert fh.read(size) == TEST_DATA[:size-1024] + +def tst_utimens(mnt_dir, tol=0): + filename = pjoin(mnt_dir, name_generator()) + os.mkdir(filename) + fstat = os.lstat(filename) + + atime = fstat.st_atime + 42.28 + mtime = fstat.st_mtime - 42.23 + if sys.version_info < (3,3): + os.utime(filename, (atime, mtime)) + else: + atime_ns = fstat.st_atime_ns + int(42.28*1e9) + mtime_ns = fstat.st_mtime_ns - int(42.23*1e9) + os.utime(filename, None, ns=(atime_ns, mtime_ns)) + + fstat = os.lstat(filename) + + assert abs(fstat.st_atime - atime) < tol + assert abs(fstat.st_mtime - mtime) < tol + if sys.version_info >= (3,3): + assert abs(fstat.st_atime_ns - atime_ns) < tol*1e9 + assert abs(fstat.st_mtime_ns - mtime_ns) < tol*1e9 + +def tst_passthrough(src_dir, mnt_dir, cache_timeout): + name = name_generator() + src_name = pjoin(src_dir, name) + mnt_name = pjoin(src_dir, name) + assert name not in os.listdir(src_dir) + assert name not in os.listdir(mnt_dir) + with open(src_name, 'w') as fh: + fh.write('Hello, world') + assert name in os.listdir(src_dir) + if cache_timeout: + safe_sleep(cache_timeout+1) + assert name in os.listdir(mnt_dir) + assert os.stat(src_name) == os.stat(mnt_name) + + name = name_generator() + src_name = pjoin(src_dir, name) + mnt_name = pjoin(src_dir, name) + assert name not in os.listdir(src_dir) + assert name not in os.listdir(mnt_dir) + with open(mnt_name, 'w') as fh: + fh.write('Hello, world') + assert name in os.listdir(src_dir) + if cache_timeout: + safe_sleep(cache_timeout+1) + assert name in os.listdir(mnt_dir) + assert os.stat(src_name) == os.stat(mnt_name) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/sshfs-2.9/test/util.py new/sshfs-2.10/test/util.py --- old/sshfs-2.9/test/util.py 1970-01-01 01:00:00.000000000 +0100 +++ new/sshfs-2.10/test/util.py 2017-07-12 17:10:18.000000000 +0200 @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +import subprocess +import pytest +import os +import stat +import time +from os.path import join as pjoin + +basename = pjoin(os.path.dirname(__file__), '..') + +def wait_for_mount(mount_process, mnt_dir, + test_fn=os.path.ismount): + elapsed = 0 + while elapsed < 30: + if test_fn(mnt_dir): + return True + if mount_process.poll() is not None: + pytest.fail('file system process terminated prematurely') + time.sleep(0.1) + elapsed += 0.1 + pytest.fail("mountpoint failed to come up") + +def cleanup(mnt_dir): + subprocess.call(['fusermount', '-z', '-u', mnt_dir], + stdout=subprocess.DEVNULL, + stderr=subprocess.STDOUT) + +def umount(mount_process, mnt_dir): + subprocess.check_call(['fusermount', '-z', '-u', mnt_dir ]) + assert not os.path.ismount(mnt_dir) + + # Give mount process a little while to terminate. Popen.wait(timeout) + # was only added in 3.3... + elapsed = 0 + while elapsed < 30: + code = mount_process.poll() + if code is not None: + if code == 0: + return + pytest.fail('file system process terminated with code %s' % (code,)) + time.sleep(0.1) + elapsed += 0.1 + pytest.fail('mount process did not terminate') + +def safe_sleep(secs): + '''Like time.sleep(), but sleep for at least *secs* + + `time.sleep` may sleep less than the given period if a signal is + received. This function ensures that we sleep for at least the + desired time. + ''' + + now = time.time() + end = now + secs + while now < end: + time.sleep(end - now) + now = time.time() + +def fuse_test_marker(): + '''Return a pytest.marker that indicates FUSE availability + + If system/user/environment does not support FUSE, return + a `pytest.mark.skip` object with more details. If FUSE is + supported, return `pytest.mark.uses_fuse()`. + ''' + + skip = lambda x: pytest.mark.skip(reason=x) + + with subprocess.Popen(['which', 'fusermount'], stdout=subprocess.PIPE, + universal_newlines=True) as which: + fusermount_path = which.communicate()[0].strip() + + if not fusermount_path or which.returncode != 0: + return skip("Can't find fusermount executable") + + if not os.path.exists('/dev/fuse'): + return skip("FUSE kernel module does not seem to be loaded") + + if os.getuid() == 0: + return pytest.mark.uses_fuse() + + mode = os.stat(fusermount_path).st_mode + if mode & stat.S_ISUID == 0: + return skip('fusermount executable not setuid, and we are not root.') + + try: + fd = os.open('/dev/fuse', os.O_RDWR) + except OSError as exc: + return skip('Unable to open /dev/fuse: %s' % exc.strerror) + else: + os.close(fd) + + return pytest.mark.uses_fuse() + +# Use valgrind if requested +if os.environ.get('TEST_WITH_VALGRIND', 'no').lower().strip() \ + not in ('no', 'false', '0'): + base_cmdline = [ 'valgrind', '-q', '--' ] +else: + base_cmdline = [] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' --exclude Makefile.in --exclude configure --exclude config.guess --exclude '*.pot' --exclude mkinstalldirs --exclude aclocal.m4 --exclude config.sub --exclude depcomp --exclude install-sh --exclude ltmain.sh old/sshfs-2.9/test/wrong_command.c new/sshfs-2.10/test/wrong_command.c --- old/sshfs-2.9/test/wrong_command.c 1970-01-01 01:00:00.000000000 +0100 +++ new/sshfs-2.10/test/wrong_command.c 2017-07-12 17:10:18.000000000 +0200 @@ -0,0 +1,9 @@ +#include <stdio.h> + +int main(void) { + fprintf(stderr, "\x1B[31m\e[1m" + "This is not the command you are looking for.\n" + "You probably want to run 'python3 -m pytest test/' instead" + "\e[0m\n"); + return 1; +}
