Compare commits
145 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3ea4c59f63 | |||
| cba87df1d7 | |||
| ef3870a997 | |||
| cf6942a5fe | |||
| 23f6858695 | |||
| a819ec4e22 | |||
| b2d857308e | |||
| 813b75b8c2 | |||
| a115bde93f | |||
| 40e202391e | |||
| 42dfaf9a5b | |||
| e14f6c68f3 | |||
| f30b051ca3 | |||
| d58d73c501 | |||
| 8d9bccb987 | |||
| cc5d6bbbc5 | |||
| 2aced1f7a4 | |||
| 9620ad0f73 | |||
| 2463943abb | |||
| 4828a8a59c | |||
| 7ad9156d4d | |||
| b13abaef12 | |||
| af97a9f4c7 | |||
| 63e65aeaeb | |||
| 70575040b4 | |||
| ca8ba022ef | |||
| bd2ab82ea7 | |||
| 3805f365fa | |||
| f187961c7a | |||
| 5c0dbfe3eb | |||
| eb1b7b1b6f | |||
| 8999210c93 | |||
| 5ed2cf8a8b | |||
| 9f2eb04371 | |||
| 6c301e043b | |||
| 6d7ce1607a | |||
| 5659d0d2ea | |||
| fe3ce10ff8 | |||
| 132dd88755 | |||
| af1f3c6235 | |||
| 8cc069f666 | |||
| d7cb098f1d | |||
| c7d8500634 | |||
| 2896aed825 | |||
| 110163d8d1 | |||
| d93f137c8f | |||
| 1dbda31786 | |||
| 33667f2a56 | |||
| f627055bc7 | |||
| efeed71295 | |||
| 96c6138978 | |||
| 1afaef8f46 | |||
| e5acfce8ed | |||
| e178150a72 | |||
| c63acf3dd7 | |||
| e3cd445a4e | |||
| 1167c11901 | |||
| a175c2876a | |||
| b2fa759358 | |||
| 1756abe6fd | |||
| f51d7b0c6a | |||
| df10d96ac2 | |||
| a2dda962c4 | |||
| 504e26f938 | |||
| 74bfa3850a | |||
| 0f6f33b1b4 | |||
| a9a1cc0046 | |||
| b5983a0ce8 | |||
| adc55c9d2b | |||
| 8589b149fd | |||
| 451f2075f2 | |||
| 391f676705 | |||
| c1e799b4da | |||
| ebfeebd468 | |||
| d1ddbbcae5 | |||
| e812e29d39 | |||
| 80db43d7dc | |||
| 27e7029148 | |||
| 30e90ae080 | |||
| 9b4ca1aade | |||
| a03d3eab39 | |||
| 665bb97ef6 | |||
| b7e8fee81b | |||
| 869aa42e97 | |||
| d8230e72ca | |||
| e0025219b3 | |||
| 19ab22abec | |||
| 3bf3be3bcd | |||
| ddbe3bee44 | |||
| b10d21db31 | |||
| f7c7713a82 | |||
| 3087690aba | |||
| 3a60def57e | |||
| f5b939d40c | |||
| 9b3fb20112 | |||
| d20b25cd2d | |||
| 055649e2fa | |||
| d18349da3b | |||
| cf10c34573 | |||
| 008cf7bcf4 | |||
| e4e14109ad | |||
| 9bcb989454 | |||
| 48122e8043 | |||
| f2b17c299b | |||
| c3d83ee24b | |||
| 1fc97d3f2c | |||
| b42f84218c | |||
| 6b4415ada4 | |||
| 6a2d06e36f | |||
| 334e9e6a27 | |||
| 91c1f2ba9e | |||
| 990f30d767 | |||
| c1d62f2032 | |||
| 61524cd1e8 | |||
| 9f4ba56b52 | |||
| d04fd55128 | |||
| 1458193728 | |||
| ce6753c88f | |||
| 66458931dd | |||
| f4631f0a17 | |||
| 3c44e22ddd | |||
| b389c66b32 | |||
| f64407d865 | |||
| eb60e2d1a2 | |||
| 0d34c7b742 | |||
| ff32332e83 | |||
| c1284b4089 | |||
| 0120e6b461 | |||
| 1ac41d2ae2 | |||
| ea6fa1d7c9 | |||
| 6a954f429c | |||
| 0e87004ab2 | |||
| e6f7fb4e50 | |||
| a6b6b5c820 | |||
| a306a80b6e | |||
| bb3ea51117 | |||
| 20fafed0a4 | |||
| 69b2b3cc0e | |||
| fa9bfc6d08 | |||
| 90619fe249 | |||
| 04a610bad1 | |||
| d8da0c4ed6 | |||
| 163683d9ef | |||
| f2086ccfbc | |||
| b4023a19dd |
@@ -0,0 +1,25 @@
|
||||
((python-mode . ((indent-tabs-mode . nil)))
|
||||
(autoconf-mode . ((indent-tabs-mode . t)))
|
||||
(c-mode . ((c-file-style . "stroustrup")
|
||||
(indent-tabs-mode . t)
|
||||
(tab-width . 8)
|
||||
(c-basic-offset . 8)
|
||||
(c-file-offsets .
|
||||
((block-close . 0)
|
||||
(brace-list-close . 0)
|
||||
(brace-list-entry . 0)
|
||||
(brace-list-intro . +)
|
||||
(case-label . 0)
|
||||
(class-close . 0)
|
||||
(defun-block-intro . +)
|
||||
(defun-close . 0)
|
||||
(defun-open . 0)
|
||||
(else-clause . 0)
|
||||
(inclass . +)
|
||||
(label . 0)
|
||||
(statement . 0)
|
||||
(statement-block-intro . +)
|
||||
(statement-case-intro . +)
|
||||
(statement-cont . +)
|
||||
(substatement . +)
|
||||
(topmost-intro . 0))))))
|
||||
+2
-2
@@ -6,8 +6,6 @@
|
||||
# NOTE! Please use 'git ls-files -i --exclude-standard'
|
||||
# command after changing this file, to see if there are
|
||||
# any tracked files which get ignored after the change.
|
||||
.*
|
||||
!.gitignore
|
||||
*.o
|
||||
*.lo
|
||||
*.la
|
||||
@@ -20,6 +18,7 @@ Makefile
|
||||
*.m4
|
||||
stamp-h*
|
||||
config.*
|
||||
sshfs.1
|
||||
/sshfs
|
||||
/ltmain.sh
|
||||
/configure
|
||||
@@ -34,3 +33,4 @@ config.*
|
||||
/.pc
|
||||
/patches
|
||||
/m4
|
||||
.deps/
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
sudo: required
|
||||
dist: xenial
|
||||
|
||||
language:
|
||||
- c
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- valgrind
|
||||
- clang
|
||||
- gcc
|
||||
- gcc-6
|
||||
- fuse
|
||||
- libfuse2
|
||||
- libfuse-dev
|
||||
- python3-pip
|
||||
- python3-setuptools
|
||||
- ninja-build
|
||||
install: test/travis-install.sh
|
||||
script: test/travis-build.sh
|
||||
|
||||
@@ -1 +1,35 @@
|
||||
Miklos Szeredi <miklos@szeredi.hu>
|
||||
Current Maintainer
|
||||
------------------
|
||||
|
||||
Nikolaus Rath <Nikolaus@rath.org>
|
||||
|
||||
|
||||
Past Maintainers
|
||||
----------------
|
||||
|
||||
Miklos Szeredi <miklos@szeredi.hu> (until 12/2015)
|
||||
|
||||
|
||||
Contributors (autogenerated list)
|
||||
---------------------------------
|
||||
|
||||
Alan Jenkins <alan.christopher.jenkins@gmail.com>
|
||||
Alexander Neumann <alexander@bumpern.de>
|
||||
Benjamin Fleischer <fleiben@gmail.com>
|
||||
Berserker <berserker.troll@yandex.com>
|
||||
Bill Zissimopoulos <billziss@navimatics.com>
|
||||
Chris Wolfe <cwolfe@chromium.org>
|
||||
gala <gala132@users.noreply.github.com>
|
||||
George Vlahavas <vlahavas@gmail.com>
|
||||
Jakub Jelen <jjelen@redhat.com>
|
||||
Julio Merino <jmmv@google.com>
|
||||
Julio Merino <jmmv@meroh.net>
|
||||
Mike Kelly <mike@pair.com>
|
||||
Miklos Szeredi <miklos@szeredi.hu>
|
||||
Miklos Szeredi <mszeredi@suse.cz>
|
||||
Nikolaus Rath <Nikolaus@rath.org>
|
||||
Percy Jahn <email@percyjahn.de>
|
||||
Qais Patankar <qaisjp@gmail.com>
|
||||
Rian Hunter <rian@alum.mit.edu>
|
||||
tpoindessous <thomas@poindessous.com>
|
||||
Zoltan Kuscsik <zoltan.kuscsik@linaro.org>
|
||||
|
||||
@@ -1,573 +0,0 @@
|
||||
2012-03-08 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Released 2.4
|
||||
|
||||
2012-03-08 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Make sure idmap files aren't writable by others otherwise, other
|
||||
local users could change the mapping, and gain access to things
|
||||
they shouldn't. Patch by Mike Kelly
|
||||
|
||||
2012-02-08 Chris Wolfe <cwolfe@chromium.org>
|
||||
|
||||
* Add -o slave. This option routes the sftp communication over stdin
|
||||
and stdout, bypassing SSH and network.
|
||||
|
||||
2011-12-16 Mike Kelly <mike@pair.com>
|
||||
|
||||
* Add -o idmap=file, -o uidmap=FILE, -o gidmap=FILE. These options
|
||||
allow you to create a pair of local files, similar to /etc/passwd or
|
||||
/etc/group files from the remote server, and use those to remap all
|
||||
the given UIDs/GIDs.
|
||||
|
||||
2011-11-25 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Make chown respect the UID mapping policy. Reported and tested
|
||||
by Vivenzio Pagliari
|
||||
|
||||
2011-11-16 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Submit max 32k reads and writes to the sftp server. Also don't
|
||||
limit the kernel to 64k reads and writes, rather split into 32k
|
||||
sized chunks and send them to the server all at once. This is
|
||||
more efficient and less demanding from the server. Reported by
|
||||
Ludovic Courtès. Fix suggested by Niels Möller
|
||||
|
||||
2011-11-14 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix double free if reconnection races with request sending.
|
||||
Patch by E. Kuemmerle
|
||||
|
||||
* Add locking around modifver and connver
|
||||
|
||||
2011-10-21 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Remove "-oPreferredAuthentications" from ssh options if the
|
||||
"password_stdin" option is used. Reported by E. Kuemmerle
|
||||
|
||||
2011-08-24 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add "-oworkaround=fstat" for SFTP servers which don't support
|
||||
the FSTAT message. Patch by: Percy Jahn
|
||||
|
||||
2011-07-01 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Released 2.3
|
||||
|
||||
2011-07-01 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add hard link operation. Works if the server supports the
|
||||
"hardlink@openssh.com" protocol extension.
|
||||
|
||||
2011-03-25 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix possible deadlock on reconnection. Reported by Florian
|
||||
Zumbiehl
|
||||
|
||||
2011-01-25 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix cleanup when ssh connection is terminated. This prevents
|
||||
sshfs hanging when the server is rebooted, for example.
|
||||
|
||||
2010-03-16 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Set FD_CLOEXEC on fuse device. This prevents deadlocks that
|
||||
happen in some circumstances (bugzilla.kernel.org #12864).
|
||||
Reported by Tim Connors
|
||||
|
||||
2009-07-15 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Check mountpoint and fuse options before starting an ssh
|
||||
session (debian bug #535333). This is only supported if compiled
|
||||
against libfuse-2.6 or later. Reported by Greg Kochanski
|
||||
|
||||
* Check that the remote path specified refers to the same type of
|
||||
file as the mountpoint, i.e. both are directories or both are
|
||||
non-directories (debian bug #535343). Reported by Greg Kochanski
|
||||
|
||||
* Allow mounting a single non-directory from the server
|
||||
|
||||
2009-07-15 Sebastian Dransfeld <sebastid@tango.flipp.net>
|
||||
|
||||
* Add option 'delay_connect': This will always create the sshfs mount,
|
||||
even if the connection to the server can't be established.
|
||||
|
||||
2009-01-27 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Print usage information and version to stdout, not stderr.
|
||||
Libfuse also needs to be fixed up. Reported by Kārlis Repsons
|
||||
|
||||
2008-10-20 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Released 2.2
|
||||
|
||||
2008-10-20 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Escape commas in fsname option if libfuse supports it
|
||||
|
||||
2008-10-08 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Handle numerical IPv6 addresses enclosed in square brackets.
|
||||
Reported by Andre-John Mas
|
||||
|
||||
* Fix error if username contains a comma character. Reported by
|
||||
Yang Zhang
|
||||
|
||||
* Fix harmless glib assertations with "cache=no"
|
||||
|
||||
2008-07-11 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Released 2.1
|
||||
|
||||
2008-07-11 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix statvfs extension to match the current protocol in
|
||||
opensshfs
|
||||
|
||||
* Check version numbers of extensions, so such changes wouldn't
|
||||
cause stupid behavior
|
||||
|
||||
2008-06-24 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add '-F' option to specify the ssh config file. Patch by Pat
|
||||
Pascal.
|
||||
|
||||
2008-05-06 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix bug in caching which could cause file corruption for append
|
||||
mode writes. Reported by Jose Alonso
|
||||
|
||||
2008-05-05 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix compile on OS X. Original patch from Michael G Schwern
|
||||
|
||||
* Fix compile on Solaris. Reported by Jean-Jacques Sarton
|
||||
|
||||
2008-04-23 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Released 2.0
|
||||
|
||||
2008-04-23 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add manual page. Written by Bartosz Fenski
|
||||
|
||||
2008-04-22 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add missing ssh options: ControlMaster, ControlPath,
|
||||
KbdInteractiveAuthentication, KbdInteractiveDevices, LocalCommand,
|
||||
RekeyLimit (Debian bug #430225).
|
||||
|
||||
* Allow the '-ossh_command=CMD' command to contain parameters.
|
||||
Escape charater is backslash.
|
||||
|
||||
* Limit write requests to 64kB.
|
||||
|
||||
* Support "statvfs@openssh.com" extension, which will be available
|
||||
in OpenSSH 5.1.
|
||||
|
||||
2008-04-21 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix incorrect disk usage reported by 'du' for files of size 4GB
|
||||
or above. Reported by Christian Boltz.
|
||||
|
||||
2008-04-16 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* If debugging is enabled, print some statistics at exit about the
|
||||
number of bytes transferred, etc..
|
||||
|
||||
2008-03-31 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Support "posix-rename@openssh.com" extension available in
|
||||
OpenSSH 4.9. This allows rename to be atomic even when target
|
||||
file or directory exists. If available, the extension will be
|
||||
used instead of the rename operation in the original protocol and
|
||||
the "-oworkaround=rename" option will be ignored.
|
||||
|
||||
2008-03-28 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add support for password authentication with pam_mount.
|
||||
Original patch and help with testing: John S. Skogtvedt
|
||||
|
||||
2008-03-03 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix ssh debug only appearing if "-d" is used. Reported by
|
||||
Michael Gorbach
|
||||
|
||||
2008-01-03 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix condition for building of sshnodelay.so
|
||||
|
||||
2007-12-17 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Released 1.9
|
||||
|
||||
2007-12-12 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix checking for fuse_opt_parse in configure
|
||||
|
||||
2007-12-11 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fill in st_ctime attribute with st_mtime, instead of leaving it
|
||||
zero
|
||||
|
||||
* Check for OpenSSH version >= 4.4, and if found, don't build the
|
||||
sshnodelay.so workaround
|
||||
|
||||
2007-12-10 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix crash on writing files, caused by missing locking around
|
||||
sshfs_file_get()
|
||||
|
||||
* Don't time requests when not debugging
|
||||
|
||||
* Add subtype support with libfuse >= 2.7.0
|
||||
|
||||
* Abort on allocation failure instead of exit
|
||||
|
||||
* Correctly show default of nodelaysrv workaround in help message
|
||||
|
||||
2007-05-16 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Released 1.8
|
||||
|
||||
2007-05-15 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add needed g_thread_init() to fix rare crashes. Reported by
|
||||
Dimitrios Apostolou
|
||||
|
||||
* Fix memory leak in sshfs_open_common()
|
||||
|
||||
2007-04-18 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix crash within gdb, caused by sem_wait() returning with an
|
||||
error on interrupt. Reported by Dimitrios Apostolou
|
||||
|
||||
* Fix spurious cache entries remaining after renaming a directory
|
||||
|
||||
2007-02-28 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Don't set DISPLAY environment variable to "", because it breaks
|
||||
ssh-askpass. Make nodelaysrv_workaround default to off, because
|
||||
with this change it may have security implications.
|
||||
|
||||
2007-02-19 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* OpenSSH sftp-server can read requests faster, than it processes
|
||||
them, when it's buffer is full it aborts. This can happen on a
|
||||
large upload to a slow server. Work around this by limiting the
|
||||
total size of outstanding reqests. Debian bug #365541. Tracked
|
||||
down by Thue Janus Kristensen
|
||||
|
||||
* Add --disable-sshnodelay configure option. The sshnodelay.so
|
||||
hack shouldnt be needed with OpenSSH client versions >= 4.3
|
||||
|
||||
2006-12-20 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Work around write performace issues due to missing TCP_NODELAY
|
||||
in sshd. Reported by Justin Searle
|
||||
|
||||
2006-11-10 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix bug which ommitted directory entries for symlinks with the
|
||||
-ofollow_symlinks option. Bug reported by Mikael Ståldal
|
||||
|
||||
2006-09-29 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix segfault if there are outstanding writes to the server after
|
||||
release on the file descriptor. This only happened on FreeBSD.
|
||||
Reported by Andriy Gapon
|
||||
|
||||
2006-08-18 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Released 1.7
|
||||
|
||||
2006-08-17 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Cosmetics on bogus statfs numbers
|
||||
|
||||
2006-08-16 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add option 'follow_symlinks' to resolve symlinks on the server.
|
||||
|
||||
* On setups where the login shell prints a banner, the sftp
|
||||
protocol was confused, and bailed out with "reply len too large:
|
||||
...". So iterate over any rubbish read immediately after login
|
||||
until a valid reply is found.
|
||||
|
||||
* Check remote directory for existence before mounting. Closes
|
||||
Ubuntu bug #46633
|
||||
|
||||
2006-03-10 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Use socketpair() instead of pipe() for communication between ssh
|
||||
and sshfs. Patch from Csaba Henk
|
||||
|
||||
* Update to FUSE interface version 26
|
||||
|
||||
2006-03-09 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Released 1.6
|
||||
|
||||
2006-03-09 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix segfault if remote host is down and reconnection is enabled
|
||||
|
||||
2006-03-08 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix bug in symlink transformation. Reported by Ralf Hoffmann
|
||||
|
||||
2006-02-24 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add workaround for broken truncate on old sftp servers. Can be
|
||||
enabled with '-oworkaround=truncate'. Patch from Joseph M Link
|
||||
|
||||
2006-02-23 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Avoid double memcpy on writes. Patch by Joseph M Link
|
||||
|
||||
* More memory copy avoidance
|
||||
|
||||
2006-02-22 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add -fPIC to the comile flags of sshnodelay.so. Reported by
|
||||
Anthony Kolasny
|
||||
|
||||
2006-02-20 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Released 1.5
|
||||
|
||||
2006-02-19 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix compilation with old FUSE versions. Report by Kurt George
|
||||
Gjerde
|
||||
|
||||
2006-02-16 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Block TERM/INT/HUP/QUIT signals in sshfs reading thread, so they
|
||||
will always be received by the FUSE main thread. Fixes the
|
||||
"double ^C" problem seen on FreeBSD.
|
||||
|
||||
* Use 'auto_cache' option if available, so that file cache is not
|
||||
thrown away on each open. This should make repeated reading of a
|
||||
file much much faster.
|
||||
|
||||
2006-01-31 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix problems with nodelay workaround on FreeBSD.
|
||||
|
||||
2006-01-30 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix data consitency bug if readahead is enabled and writes are
|
||||
intermixed with reads. Solution is far from optimal, since it
|
||||
will prevent readahead in the above situation. If used with FUSE
|
||||
>= 2.6.0 with Linux-2.6.X, readahead will be done by the kernel,
|
||||
and hence there will be no performance penalty. Bug reported and
|
||||
test program written by Wolfgang Köbler. Further testing on
|
||||
FreeBSD by Csaba Henk
|
||||
|
||||
2006-01-29 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add '-olarge_read' option for Linux-2.4.*. This should
|
||||
generally improve download performance
|
||||
|
||||
2006-01-27 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add workaround (enabled by default) for ssh clients not setting
|
||||
TCP_NODELAY on the network connection. Currently this is all
|
||||
known versions of openssh. This may improve download speed in
|
||||
some circumstances
|
||||
|
||||
* Make it possible to idividually disable workarounds with a "no"
|
||||
prefix
|
||||
|
||||
* Change '-otransform_symlinks' not to prefix with the mountpoint
|
||||
(which is fragile wrt. moving/binding the mount). Rather convert
|
||||
absolute symlinks to relative if possible. Does not yet work if
|
||||
base path is itself relative
|
||||
|
||||
2006-01-25 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Use TCP_NODELAY socket option for direct connection. This may
|
||||
improve download speed in some circumstances
|
||||
|
||||
2006-01-20 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Update to fuse API 2.6
|
||||
|
||||
* Don't do readahead in sshfs if it's done in kernel
|
||||
|
||||
2006-01-14 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Released 1.4
|
||||
|
||||
2006-01-09 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Added 'transform_symlinks' option to "fix-up" absolute symlinks.
|
||||
Patch by Paul Jarc
|
||||
|
||||
* Add option parsing implementation, so linking with older than
|
||||
2.5.0 libfuse still works.
|
||||
|
||||
2005-12-09 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Use new option parsing interface of FUSE
|
||||
|
||||
2005-11-28 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Set statvfs::f_frsize
|
||||
|
||||
2005-11-23 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix warnings on 64bit systems. Reported by D. R. Evans
|
||||
|
||||
2005-11-16 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Replace EPROTO with the more portable EIO
|
||||
|
||||
* Fix bug in option parsing. Reported by Csaba Henk
|
||||
|
||||
2005-10-29 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Use FUSE version 25 if available
|
||||
|
||||
* Add <sys/socket.h> include. Reported by Csaba Henk
|
||||
|
||||
2005-10-29 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Released 1.3
|
||||
|
||||
2005-10-28 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add atomic create+open and ftruncate operation. This should fix
|
||||
issues with 'cp' and other programs failing with "Permission
|
||||
denied". To be effective, needs FUSE version 2.5 and kernel
|
||||
version 2.6.15 (just a guess, since neither of them is released
|
||||
yet).
|
||||
|
||||
2005-10-27 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add support for SSH protocol version 1. Bug reported by Miklos
|
||||
Bagi Jr.
|
||||
|
||||
2005-10-26 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix typo (ConnectTimeout -> ConnectionTimeout) in ssh options.
|
||||
Bug reported by Miklos Bagi Jr.
|
||||
|
||||
2005-10-19 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* GNOME Nautilus fails to copy file to sshfs filesystem, because
|
||||
FUSE returns zero free space. So instead return huge (999999999
|
||||
kbytes) amount of free space, yet it should be obvious that the
|
||||
number is artificial. Bug report by Peter Kronheimer
|
||||
|
||||
2005-10-18 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add remote uid detection and translation ('idmap=user' option).
|
||||
Idea and implementation details worked out by Jean-Marc Valin
|
||||
|
||||
2005-10-17 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add one more missing lock.
|
||||
|
||||
* Add workaround for failure to rename to an existing file. Based
|
||||
on patch by Michael Best
|
||||
|
||||
2005-10-15 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Protect request ID allocation with mutex. Bug report by Tvrtko
|
||||
Ursulin
|
||||
|
||||
2005-08-17 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Try to calculate approximate disk usage of files from their
|
||||
size, so that 'du' returns meaningful results. Problem reported
|
||||
by Bernd Amend.
|
||||
|
||||
2005-08-15 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Released 1.2
|
||||
|
||||
2005-08-13 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add 'reconnect' option, which tries to reconnect to the server
|
||||
when the connection is broken. If a password is required for
|
||||
connection, it is recommended that you install ssh-askpass, and
|
||||
set the SSH_ASKPASS environment variable (see 'man ssh' for more
|
||||
details).
|
||||
|
||||
2005-05-05 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Work around missing truncate() support in some older sftp
|
||||
servers (only works for zero size truncate). Thanks to Eduard
|
||||
Czimbalmos for the bug report and help with testing
|
||||
|
||||
2005-04-13 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix compilation with gcc-2.95. Reported by David A. Gershman
|
||||
|
||||
2005-03-08 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Make it work on server version 2 (e.g. Sun_SSH_1.0.1). Report
|
||||
and testing by Pieter J. Kersten
|
||||
|
||||
2005-03-04 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Released 1.1
|
||||
|
||||
2005-03-03 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Slightly optimize readahead. Still not clever enough to always
|
||||
keep the pipe filled.
|
||||
|
||||
* Add 'sshfs_debug' option
|
||||
|
||||
2005-02-17 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Parse 'max_read' mount option and if smaller than 65536 forward
|
||||
to FUSE
|
||||
|
||||
2005-02-16 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Added simple readahead (big performance gain in case of
|
||||
sequential read pattern). Can be disabled with '-o no_readahead'
|
||||
|
||||
2005-02-14 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Added asynchronous writeback (big performance gain) and made
|
||||
this the default. Can be disabled with '-o sshfs_sync'
|
||||
|
||||
2005-02-09 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Added option to start arbitary command instead of 'ssh'
|
||||
|
||||
* Re-added '-p PORT' as a convenience option, also '-C' works as
|
||||
in ssh.
|
||||
|
||||
2005-02-08 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add caching of symlinks
|
||||
|
||||
* Add support for many ssh options to be passed to ssh
|
||||
|
||||
* Port number can now actually be specified with "-o port=PORT",
|
||||
bug spotted by Andrew Ukrainec
|
||||
|
||||
2005-02-07 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Separate attribute caching to a separate layer
|
||||
|
||||
* Add caching of directory contents
|
||||
|
||||
2005-02-03 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
Fix PKG_CONFIG_PATH setting in configure.ac (reported by Alpar
|
||||
Juttner)
|
||||
|
||||
2005-01-09 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Released 1.0
|
||||
|
||||
2004-12-04 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Started ChangeLog
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
Unreleased Changes
|
||||
-------------------------
|
||||
|
||||
* Fixed improper handling of sequential spaces spaces in "ssh_command" option
|
||||
|
||||
Release 2.10 (2017-08-03)
|
||||
-------------------------
|
||||
|
||||
* Fixed a crash due to a race condition when listing
|
||||
directory contents.
|
||||
* Added unit tests
|
||||
* Documented limited hardlink support.
|
||||
* Added support for building with Meson.
|
||||
* Added support for more SSH options.
|
||||
* Dropped support for the *nodelay* workaround - the last OpenSSH
|
||||
version for which this was useful was released in 2006.
|
||||
* Dropped 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`
|
||||
* Removed support for `-o workaround=all`. Workarounds should always
|
||||
enabled explicitly and only when needed. There is no point in always
|
||||
enabling a potentially changing set of workarounds.
|
||||
|
||||
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 <Nikolaus@rath.org>)
|
||||
|
||||
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
|
||||
@@ -0,0 +1,263 @@
|
||||
SSHFS FAQ
|
||||
=========
|
||||
|
||||
1. I've found a bug and there's no solution in this FAQ, what
|
||||
should I do?
|
||||
2. Advantage of sshfs over NFS and Samba
|
||||
3. Create the device node
|
||||
4. mv fails with "Operation not permitted"
|
||||
5. cvs fails with "cvs [status aborted]: cannot get working
|
||||
directory: No such file or directory" in a sshfs mounted directory
|
||||
6. Changes on the server are not immediately visible in the
|
||||
mounted directory.
|
||||
7. Configuring the ssh connection
|
||||
8. What are the no_readahead and sshfs_sync options for?
|
||||
9. Why does df return strange values on partitions mounted via
|
||||
sshfs?
|
||||
10. How do I specify the remote mount point (since the example
|
||||
defaults to the home directory)
|
||||
11. sshfs hangs after a while
|
||||
12. Following symlinks on the server side
|
||||
13. Making absolute symlinks work
|
||||
14. Mounting as root
|
||||
15. Exporting via NFS
|
||||
16. Automatical mounting using /etc/fstab
|
||||
17. Why does SVN (etc...) fail with permission denied?
|
||||
18. Why does SVN (etc...) fail to rename files?
|
||||
19. Is there some neat way to do it in reverse?
|
||||
20. Might it be reasonable to disallow loops?
|
||||
21. How to mount through an intermediary ssh server, eg:
|
||||
localhost -> A -> B; mount B from localhost
|
||||
22. Alternative Solution
|
||||
23. I seem to have successfully mounted a remote directory, but
|
||||
performing an `ls -l` on the directory above the mount point shows
|
||||
the mount point's attributes as `? ? ? ? ? ?`. Nothing shows up in
|
||||
the directory either. What am I doing wrong?
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
1. I've found a bug and there's no solution in this FAQ, what should I do?
|
||||
|
||||
Please report the bug in the Github issue tracker:
|
||||
https://github.com/libfuse/sshfs/issues
|
||||
|
||||
Also logs with debugging output can be useful for diagnosing the
|
||||
problem. Try running sshfs with the following options:
|
||||
sshfs -odebug,sshfs_debug,loglevel=debug ...
|
||||
|
||||
Doing strace on the application which fails may also sometimes help:
|
||||
strace -f -o /tmp/strace application args ...
|
||||
|
||||
Note that large messages (over 40k) will be rejected from the mailing
|
||||
list. So try to keep the logs as short as possible.
|
||||
|
||||
|
||||
2. Advantage of sshfs over NFS and Samba
|
||||
|
||||
Users can mount remote resources they already have ssh access to,
|
||||
without requiring the remote machine to export the resource.
|
||||
|
||||
The remote resource can be mounted when it is needed in a location
|
||||
that is convenient for the user at that time, without needing to rely
|
||||
on a central, root-controlled file system table.
|
||||
|
||||
Automatic mounting, if desired, can be added to a shell script such as
|
||||
.bashrc (provided authentication is done using RSA/DSA keys).
|
||||
|
||||
Resources can be mounted over slow and unreliable (distant)
|
||||
connections.
|
||||
|
||||
3. Create the device node
|
||||
|
||||
If you don't use udev, you may get this error message:
|
||||
|
||||
fusermount: failed to open /dev/fuse: No such device or address
|
||||
|
||||
Before loading the fuse kernel module, create the device node
|
||||
manually:
|
||||
|
||||
mknod -m 666 /dev/fuse c 10 229
|
||||
|
||||
4. mv fails with "Operation not permitted".
|
||||
|
||||
Use -o workaround=rename (requires sshfs version >= 1.3).
|
||||
|
||||
5. cvs fails with "cvs status aborted?: cannot get working directory: No such
|
||||
file or directory" in a sshfs mounted directory
|
||||
|
||||
Use the -oreaddir_ino option. Example:
|
||||
sshfs -oreaddir_ino hostname:remote_dir mount_point
|
||||
|
||||
6. Changes on the server are not immediately visible in the mounted directory.
|
||||
|
||||
By default, sshfs caches things for 20 seconds, use -o cache_timeout=N
|
||||
to change the default cache timeout (in seconds) or -o cache=no for
|
||||
disabling the cache.
|
||||
|
||||
You can also control cache timeouts for directory listing etc with
|
||||
-o cache_stat_timeout=N,
|
||||
-o cache_dir_timout=N, and
|
||||
-o cache_link_timout=N.
|
||||
|
||||
7. Configuring the ssh connection
|
||||
|
||||
In addition to flags like -C, -p, and -o SSHOPT...=, you may find it
|
||||
easier to edit your /.ssh/config file. You can add an entry with any
|
||||
customization you want, test it with ssh, and finally use it with
|
||||
sshfs. As a bonus, you get a short mnemonic for your configuration.
|
||||
|
||||
8. What are the no_readahead and sshfs_sync options for?
|
||||
|
||||
These disable read and write optimizations respectively. They don't
|
||||
really make sense unless you're doing something special.
|
||||
|
||||
9. Why does df return strange values on partitions mounted via sshfs?
|
||||
|
||||
Because the SFTP protocol doesn't have a statfs operation this is
|
||||
currently not possible to display proper usage on remote partition.
|
||||
|
||||
10. How do I specfy the remote mount point (since the example defaults to the
|
||||
home directory)
|
||||
|
||||
The example shows:
|
||||
sshfs hostname: mountpoint
|
||||
|
||||
To specify a remote mount point use:
|
||||
sshfs hostname:remotemountpoint mountpoint
|
||||
|
||||
This might be obvious to others, but I ended up looking up the
|
||||
interface to sftp to see if I could learn how to specify the remote
|
||||
mount point, then thought about the way that scp specifies the remote
|
||||
directory, and it worked.
|
||||
|
||||
11. sshfs hangs after a while
|
||||
|
||||
Mounting works fine, I can use the files in Mountpoint as good as any
|
||||
other files on my system, but after bit of time, changing nothing on
|
||||
the remote files sshfs crashes. This means, I can not cd into the
|
||||
Mountpoint (xterm hangs, nautilus hangs... every program trying to
|
||||
access the Mountpoint gets stuck, and won't return).
|
||||
|
||||
Solution: add
|
||||
ServerAliveInterval 15
|
||||
|
||||
in your .ssh/config (or use -o ServerAliveInterval=15 on the sshfs
|
||||
command line but I did not test that solution). This will force the
|
||||
ssh connection to stay alive even if you have no activity.
|
||||
|
||||
12. Following symlinks on the server side
|
||||
|
||||
The -o follow_symlinks option will enable this.
|
||||
|
||||
13. Making absolute symlinks work
|
||||
|
||||
Use the -o transform_symlinks option, which will transform absolute
|
||||
symlinks (ones which point somewhere inside the mount) into relative
|
||||
ones.
|
||||
|
||||
14. Mounting as root
|
||||
|
||||
Generally it's not possible to use an sshfs mount as a "real"
|
||||
filesystem shared between multiple users. Some of this functionality
|
||||
can be enabled with the -o allow_other and -o default_permissions
|
||||
options, but files will not be created with the correct ownership,
|
||||
etc...
|
||||
|
||||
15. Exporting via NFS
|
||||
|
||||
Use the userspace NFS daemon http://sourceforge.net/projects/unfs
|
||||
|
||||
16. Automatical mounting using /etc/fstab
|
||||
|
||||
A line in /etc/fstab has the following format:
|
||||
sshfs#USERNAME@REMOTE_HOST:REMOTE_PATH MOUNT_POINT fuse SSHFS_OPTIONS 0 0
|
||||
|
||||
eg.
|
||||
sshfs#guest@guest.login.com:data /mnt/guest fuse \
|
||||
uid=1003,gid=100,umask=0,allow_other 0 0
|
||||
|
||||
17. Why does SVN (etc...) fail with permission denied?
|
||||
|
||||
This is a bug that happens when an application creates a read-only
|
||||
file opened for writing (e.g. open("foo", O_WRONLY|O_CREAT, 0444))
|
||||
|
||||
It has been fixed in sshfs version 1.3, but also requires FUSE version
|
||||
>=2.5.X and Linux kernel version >=2.6.15.
|
||||
|
||||
18. Why does SVN (etc...) fail to rename files?
|
||||
|
||||
$ svn co svn://anonsvn.kde.org/home/kde/trunk/KDE/kdelibs
|
||||
svn: Can't move 'kdelibs/.svn/tmp/entries' to 'kdelibs/.svn/entries':
|
||||
Operation not permitted
|
||||
|
||||
The reason is that SFTP protocol version 3 (which is implemented by
|
||||
OpenSSH's sftp-server) defines the rename operation slightly
|
||||
differently than POSIX. The difference is that renaming to an existing
|
||||
file or directory will fail instead of atomically replacing the old
|
||||
file.
|
||||
|
||||
The -o workaround=rename option will try to emulate POSIX rename
|
||||
semantics, but it cannot guarantee atomicity. In most of the cases
|
||||
this doesn't matter, and things will work fine with this option.
|
||||
|
||||
19. Is there some neat way to do it in reverse?
|
||||
|
||||
You want to mount a USB thumb drive onto a file server that is rather
|
||||
remote.
|
||||
|
||||
Assuming this is difficult because the laptop with the thumb drive is
|
||||
sitting behind NAT, firewalls, etc. then you need to create a
|
||||
port-forward:
|
||||
client$ ssh -R 2222:localhost:22 server
|
||||
server$ sshfs -p 2222 localhost:/media/usb1 myusb1
|
||||
|
||||
(Now, is there is there a smarter way that does not involve port
|
||||
opening login permissions in an undesireable direction?)
|
||||
|
||||
20. Might it be reasonable to disallow loops?
|
||||
|
||||
sshfs localhost:/mnt /mnt
|
||||
|
||||
This seems to produce undesirable results. --JoshuaRodman
|
||||
|
||||
21. How to mount through an intermediary ssh server, eg: localhost -> A -> B;
|
||||
mount B from localhost
|
||||
|
||||
Start by mounting the folder you need that is on "a" to a folder on
|
||||
"b" then mount the new folder that is on "a" to a folder on localhost.
|
||||
|
||||
IE: (These are NOT real commands, but a sequence of steps.
|
||||
A mounts B:/home/x on /mnt/Bx
|
||||
localhost mounts A:/mnt/Bx on ~/mydir
|
||||
|
||||
22. Alternative Solution:
|
||||
|
||||
1) Create a shell script to wrap the tunneling of one ssh command over
|
||||
another,
|
||||
$ cat >Atunnel <<EOF
|
||||
#!/bin/bash
|
||||
ssh -q A ssh -q "$@"
|
||||
EOF
|
||||
$ chmod u+x Atunnel
|
||||
|
||||
N.B. make sure to put this somewhere on your path. 2) sshfs mount as
|
||||
normal but using this script as the ssh command.
|
||||
$ sshfs -o ssh_command='Atunnel' B: ~/mydir
|
||||
|
||||
23. I seem to have successfully mounted a remote directory, but performing an
|
||||
`ls -l` on the directory above the mount point shows the mount point's
|
||||
attributes as `? ? ? ? ? ?`. Nothing shows up in the directory either. What
|
||||
am I doing wrong?
|
||||
|
||||
You probably specified a remote path with the tilde (~) in it. This
|
||||
doesn't seem to work. Instead, specify an absolute remote path:
|
||||
sshfs username@remote.host:/home/username/whatever my/mount
|
||||
|
||||
What options do i use to make playing media files (music) over sshfs more
|
||||
efficient?
|
||||
|
||||
MacFUSE doesn't seem to let me move files from one directory to another. It
|
||||
first asks for my local user password (i.e. the password on my Macbook Pro)
|
||||
and then produces the error message "The operation cannot be completed
|
||||
because one or more required items cannot be found. (Error code -120).
|
||||
|
||||
+15
-16
@@ -2,30 +2,29 @@
|
||||
|
||||
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
|
||||
endif
|
||||
if DARWIN_COMPAT
|
||||
sshfs_SOURCES += compat/darwin_compat.c compat/darwin_compat.h
|
||||
endif
|
||||
|
||||
sshfs_LDADD = $(SSHFS_LIBS)
|
||||
sshfs_CFLAGS = $(SSHFS_CFLAGS)
|
||||
sshfs_CPPFLAGS = -D_REENTRANT -DFUSE_USE_VERSION=26 -DLIBDIR=\"$(libdir)\"
|
||||
sshfs_CPPFLAGS = -D_REENTRANT -DFUSE_USE_VERSION=26 -DLIBDIR=\"$(libdir)\" \
|
||||
-DIDMAP_DEFAULT="\"$(IDMAP_DEFAULT)\""
|
||||
|
||||
EXTRA_DIST = sshnodelay.c FAQ.txt
|
||||
CLEANFILES = sshnodelay.so
|
||||
EXTRA_DIST = sshfs.1.in meson.build
|
||||
CLEANFILES = sshfs.1 sshfs.1.tmp
|
||||
|
||||
dist_man_MANS = 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
|
||||
sshfs.1: sshfs.1.in
|
||||
$(AM_V_GEN)sed \
|
||||
-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
|
||||
|
||||
@@ -1,112 +0,0 @@
|
||||
What is new in 2.1, 2.2 and 2.3
|
||||
-------------------------------
|
||||
|
||||
* Support hard link creation if server is OpenSSH 5.7 or later
|
||||
|
||||
* Check mount point and options before connecting to ssh server
|
||||
|
||||
* Handle commas in usernames
|
||||
|
||||
* Small improvements and bug fixes
|
||||
|
||||
|
||||
What is new in 2.0
|
||||
------------------
|
||||
|
||||
* 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
|
||||
@@ -1,74 +0,0 @@
|
||||
Abstract
|
||||
========
|
||||
|
||||
This is a filesystem client based on the SSH File Transfer Protocol.
|
||||
Since most SSH servers already support this protocol it is very easy
|
||||
to set up: i.e. on the server side there's nothing to do. On the
|
||||
client side mounting the filesystem is as easy as logging into the
|
||||
server with ssh.
|
||||
|
||||
The idea of sshfs was taken from the SSHFS filesystem distributed with
|
||||
LUFS, which I found very useful. There were some limitations of that
|
||||
codebase, so I rewrote it. Features of this implementation are:
|
||||
|
||||
- Based on FUSE (the best userspace filesystem framework for Linux ;)
|
||||
|
||||
- Multithreading: more than one request can be on it's way to the
|
||||
server
|
||||
|
||||
- Allowing large reads (max 64k)
|
||||
|
||||
- Caching directory contents
|
||||
|
||||
- Reconnect on failure
|
||||
|
||||
Latest version
|
||||
==============
|
||||
|
||||
The latest version and more information can be found on
|
||||
|
||||
http://fuse.sourceforge.net/sshfs.html
|
||||
|
||||
|
||||
How to mount a filesystem
|
||||
=========================
|
||||
|
||||
Once sshfs is installed (see next section) running it is very simple:
|
||||
|
||||
sshfs hostname: mountpoint
|
||||
|
||||
Note, that it's recommended to run it as user, not as root. For this
|
||||
to work the mountpoint must be owned by the user. If the username is
|
||||
different on the host you are connecting to, then use the
|
||||
"username@host:" form. If you need to enter a password sshfs will ask
|
||||
for it (actually it just runs ssh which ask for the password if
|
||||
needed). You can also specify a directory after the ":". The default
|
||||
is the home directory.
|
||||
|
||||
Also many ssh options can be specified (see the manual pages for
|
||||
sftp(1) and ssh_config(5)), including the remote port number
|
||||
('-oport=PORT')
|
||||
|
||||
To unmount the filesystem:
|
||||
|
||||
fusermount -u moutpoint
|
||||
|
||||
|
||||
Installing
|
||||
==========
|
||||
|
||||
First you need to download FUSE 2.2 or later from:
|
||||
|
||||
http://fuse.sourceforge.net
|
||||
|
||||
You also need to install the devel package for glib2.0. After
|
||||
installing FUSE, compile sshfs the usual way:
|
||||
|
||||
./configure
|
||||
make
|
||||
make install (as root)
|
||||
|
||||
And you are ready to go.
|
||||
|
||||
If checking out from CVS for the first time also do 'autoreconf -i'
|
||||
before doing './configure'.
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
SSHFS
|
||||
=====
|
||||
|
||||
|
||||
About
|
||||
-----
|
||||
|
||||
SSHFS allows you to mount a remote filesystem using SFTP. Most SSH
|
||||
servers support and enable this SFTP access by default, so SSHFS is
|
||||
very simple to use - there's nothing to do on the server-side.
|
||||
|
||||
|
||||
Development Status
|
||||
------------------
|
||||
|
||||
SSHFS is shipped by all major Linux distributions and has been in
|
||||
production use across a wide range of systems for many years. However,
|
||||
at present SSHFS does not have any active, regular contributors, and
|
||||
there are a number of known issues (see the bugtracker). The current
|
||||
maintainer continues to apply pull requests and makes regular
|
||||
releases, but unfortunately has no capacity to do any development
|
||||
beyond addressing high-impact issues. When reporting bugs, please
|
||||
understand that unless you are including a pull request or are
|
||||
reporting a critical issue, you will probably not get a response.
|
||||
|
||||
|
||||
How to use
|
||||
----------
|
||||
|
||||
Once sshfs is installed (see next section) running it is very simple::
|
||||
|
||||
sshfs [user@]hostname:[directory] mountpoint
|
||||
|
||||
It is recommended to run SSHFS as regular user (not as root). For
|
||||
this to work the mountpoint must be owned by the user. If username is
|
||||
omitted SSHFS will use the local username. If the directory is
|
||||
omitted, SSHFS will mount the (remote) home directory. If you need to
|
||||
enter a password sshfs will ask for it (actually it just runs ssh
|
||||
which ask for the password if needed).
|
||||
|
||||
Also many ssh options can be specified (see the manual pages for
|
||||
*sftp(1)* and *ssh_config(5)*), including the remote port number
|
||||
(``-oport=PORT``)
|
||||
|
||||
To unmount the filesystem::
|
||||
|
||||
fusermount -u mountpoint
|
||||
|
||||
On BSD and macOS, to unmount the filesystem::
|
||||
|
||||
umount mountpoint
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
First, download the latest SSHFS release from
|
||||
https://github.com/libfuse/sshfs/releases. On Linux and BSD, you will
|
||||
also need to have libfuse_ installed. On macOS, you need macFUSE_
|
||||
instead. Finally, you need the Glib_ development package (which should
|
||||
be available from your operating system's package manager).
|
||||
|
||||
To build and install, we recommend to use Meson_ (version 0.38 or
|
||||
newer) and Ninja_. After extracting the sshfs tarball, create a
|
||||
(temporary) build directory and run Meson::
|
||||
|
||||
$ md build; cd build
|
||||
$ meson ..
|
||||
|
||||
Normally, the default build options will work fine. If you
|
||||
nevertheless want to adjust them, you can do so with the *mesonconf*
|
||||
command::
|
||||
|
||||
$ mesonconf # list options
|
||||
$ mesonconf -D strip=true # set an option
|
||||
|
||||
To build, test and install SSHFS, you then use Ninja (running the
|
||||
tests requires the `py.test`_ Python module)::
|
||||
|
||||
$ ninja
|
||||
$ python3 -m pytest test/ # optional, but recommended
|
||||
$ sudo ninja install
|
||||
|
||||
.. _libfuse: http://github.com/libfuse/libfuse
|
||||
.. _macFUSE: https://macfuse.io/
|
||||
.. _Glib: https://developer.gnome.org/glib/stable/
|
||||
.. _Meson: http://mesonbuild.com/
|
||||
.. _Ninja: https://ninja-build.org/
|
||||
.. _`py.test`: http://www.pytest.org/
|
||||
|
||||
Alternate Installation
|
||||
----------------------
|
||||
|
||||
If you are not able to use Meson and Ninja, please report this to the
|
||||
sshfs mailing list. Until the problem is resolved, you may fall back
|
||||
to an in-source build using autotools::
|
||||
|
||||
$ ./configure
|
||||
$ make
|
||||
$ sudo make install
|
||||
|
||||
Note that support for building with autotools may disappear at some
|
||||
point, so if you depend on using autotools for some reason please let
|
||||
the sshfs developers know!
|
||||
|
||||
|
||||
Caveats
|
||||
-------
|
||||
|
||||
Rename
|
||||
~~~~~~
|
||||
|
||||
Some SSH servers do not support atomically overwriting the destination
|
||||
when renaming a file. In this case you will get an error when you
|
||||
attempt to rename a file and the destination already exists. A
|
||||
workaround is to first remove the destination file, and then do the
|
||||
rename. SSHFS can do this automatically if you call it with `-o
|
||||
workaround=rename`. However, in this case it is still possible that
|
||||
someone (or something) recreates the destination file after SSHFS has
|
||||
removed it, but before SSHFS had the time to rename the old file. In
|
||||
this case, the rename will still fail.
|
||||
|
||||
Hardlinks
|
||||
~~~~~~~~~
|
||||
|
||||
If the SSH server supports the *hardlinks* extension, SSHFS will allow
|
||||
you to create hardlinks. However, hardlinks will always appear as
|
||||
individual files when seen through an SSHFS mount, i.e. they will
|
||||
appear to have different inodes and an *st_nlink* value of 1.
|
||||
|
||||
|
||||
Getting Help
|
||||
------------
|
||||
|
||||
If you need help, please ask on the <fuse-sshfs@lists.sourceforge.net>
|
||||
mailing list (subscribe at
|
||||
https://lists.sourceforge.net/lists/listinfo/fuse-sshfs).
|
||||
|
||||
Please report any bugs on the GitHub issue tracker at
|
||||
https://github.com/libfuse/libfuse/issues.
|
||||
|
||||
Professional Support
|
||||
--------------------
|
||||
|
||||
Professional support is available. Please contact Nikolaus Rath
|
||||
<Nikolaus@rath.org> for details.
|
||||
@@ -14,16 +14,19 @@
|
||||
#include <glib.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#define DEFAULT_CACHE_TIMEOUT 20
|
||||
#define MAX_CACHE_SIZE 10000
|
||||
#define MIN_CACHE_CLEAN_INTERVAL 5
|
||||
#define CACHE_CLEAN_INTERVAL 60
|
||||
#define DEFAULT_CACHE_TIMEOUT_SECS 20
|
||||
#define DEFAULT_MAX_CACHE_SIZE 10000
|
||||
#define DEFAULT_CACHE_CLEAN_INTERVAL_SECS 60
|
||||
#define DEFAULT_MIN_CACHE_CLEAN_INTERVAL_SECS 5
|
||||
|
||||
struct cache {
|
||||
int on;
|
||||
unsigned stat_timeout;
|
||||
unsigned dir_timeout;
|
||||
unsigned link_timeout;
|
||||
unsigned int stat_timeout_secs;
|
||||
unsigned int dir_timeout_secs;
|
||||
unsigned int link_timeout_secs;
|
||||
unsigned int max_size;
|
||||
unsigned int clean_interval_secs;
|
||||
unsigned int min_clean_interval_secs;
|
||||
struct fuse_cache_operations *next_oper;
|
||||
GHashTable *table;
|
||||
pthread_mutex_t lock;
|
||||
@@ -70,9 +73,9 @@ static int cache_clean_entry(void *key_, struct node *node, time_t *now)
|
||||
static void cache_clean(void)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
if (now > cache.last_cleaned + MIN_CACHE_CLEAN_INTERVAL &&
|
||||
(g_hash_table_size(cache.table) > MAX_CACHE_SIZE ||
|
||||
now > cache.last_cleaned + CACHE_CLEAN_INTERVAL)) {
|
||||
if (now > cache.last_cleaned + cache.min_clean_interval_secs &&
|
||||
(g_hash_table_size(cache.table) > cache.max_size ||
|
||||
now > cache.last_cleaned + cache.clean_interval_secs)) {
|
||||
g_hash_table_foreach_remove(cache.table,
|
||||
(GHRFunc) cache_clean_entry, &now);
|
||||
cache.last_cleaned = now;
|
||||
@@ -113,7 +116,7 @@ void cache_invalidate(const char *path)
|
||||
pthread_mutex_unlock(&cache.lock);
|
||||
}
|
||||
|
||||
void cache_invalidate_write(const char *path)
|
||||
static void cache_invalidate_write(const char *path)
|
||||
{
|
||||
pthread_mutex_lock(&cache.lock);
|
||||
cache_purge(path);
|
||||
@@ -172,7 +175,7 @@ void cache_add_attr(const char *path, const struct stat *stbuf, uint64_t wrctr)
|
||||
if (wrctr == cache.write_ctr) {
|
||||
node = cache_get(path);
|
||||
node->stat = *stbuf;
|
||||
node->stat_valid = time(NULL) + cache.stat_timeout;
|
||||
node->stat_valid = time(NULL) + cache.stat_timeout_secs;
|
||||
if (node->stat_valid > node->valid)
|
||||
node->valid = node->stat_valid;
|
||||
cache_clean();
|
||||
@@ -188,7 +191,7 @@ static void cache_add_dir(const char *path, char **dir)
|
||||
node = cache_get(path);
|
||||
g_strfreev(node->dir);
|
||||
node->dir = dir;
|
||||
node->dir_valid = time(NULL) + cache.dir_timeout;
|
||||
node->dir_valid = time(NULL) + cache.dir_timeout_secs;
|
||||
if (node->dir_valid > node->valid)
|
||||
node->valid = node->dir_valid;
|
||||
cache_clean();
|
||||
@@ -210,7 +213,7 @@ static void cache_add_link(const char *path, const char *link, size_t size)
|
||||
node = cache_get(path);
|
||||
g_free(node->link);
|
||||
node->link = g_strndup(link, my_strnlen(link, size-1));
|
||||
node->link_valid = time(NULL) + cache.link_timeout;
|
||||
node->link_valid = time(NULL) + cache.link_timeout_secs;
|
||||
if (node->link_valid > node->valid)
|
||||
node->valid = node->link_valid;
|
||||
cache_clean();
|
||||
@@ -490,6 +493,7 @@ static void cache_unity_fill(struct fuse_cache_operations *oper,
|
||||
cache_oper->init = oper->oper.init;
|
||||
#endif
|
||||
cache_oper->getattr = oper->oper.getattr;
|
||||
cache_oper->access = oper->oper.access;
|
||||
cache_oper->readlink = oper->oper.readlink;
|
||||
cache_oper->getdir = cache_unity_getdir;
|
||||
cache_oper->mknod = oper->oper.mknod;
|
||||
@@ -519,6 +523,10 @@ static void cache_unity_fill(struct fuse_cache_operations *oper,
|
||||
cache_oper->ftruncate = oper->oper.ftruncate;
|
||||
cache_oper->fgetattr = oper->oper.fgetattr;
|
||||
#endif
|
||||
#if FUSE_VERSION >= 29
|
||||
cache_oper->flag_nullpath_ok = oper->oper.flag_nullpath_ok;
|
||||
cache_oper->flag_nopath = oper->oper.flag_nopath;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void cache_fill(struct fuse_cache_operations *oper,
|
||||
@@ -544,6 +552,10 @@ static void cache_fill(struct fuse_cache_operations *oper,
|
||||
cache_oper->ftruncate = oper->oper.ftruncate ? cache_ftruncate : NULL;
|
||||
cache_oper->fgetattr = oper->oper.fgetattr ? cache_fgetattr : NULL;
|
||||
#endif
|
||||
#if FUSE_VERSION >= 29
|
||||
cache_oper->flag_nullpath_ok = 0;
|
||||
cache_oper->flag_nopath = 0;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@@ -569,20 +581,28 @@ struct fuse_operations *cache_init(struct fuse_cache_operations *oper)
|
||||
static const struct fuse_opt cache_opts[] = {
|
||||
{ "cache=yes", offsetof(struct cache, on), 1 },
|
||||
{ "cache=no", offsetof(struct cache, on), 0 },
|
||||
{ "cache_timeout=%u", offsetof(struct cache, stat_timeout), 0 },
|
||||
{ "cache_timeout=%u", offsetof(struct cache, dir_timeout), 0 },
|
||||
{ "cache_timeout=%u", offsetof(struct cache, link_timeout), 0 },
|
||||
{ "cache_stat_timeout=%u", offsetof(struct cache, stat_timeout), 0 },
|
||||
{ "cache_dir_timeout=%u", offsetof(struct cache, dir_timeout), 0 },
|
||||
{ "cache_link_timeout=%u", offsetof(struct cache, link_timeout), 0 },
|
||||
{ "cache_timeout=%u", offsetof(struct cache, stat_timeout_secs), 0 },
|
||||
{ "cache_timeout=%u", offsetof(struct cache, dir_timeout_secs), 0 },
|
||||
{ "cache_timeout=%u", offsetof(struct cache, link_timeout_secs), 0 },
|
||||
{ "cache_stat_timeout=%u", offsetof(struct cache, stat_timeout_secs), 0 },
|
||||
{ "cache_dir_timeout=%u", offsetof(struct cache, dir_timeout_secs), 0 },
|
||||
{ "cache_link_timeout=%u", offsetof(struct cache, link_timeout_secs), 0 },
|
||||
{ "cache_max_size=%u", offsetof(struct cache, max_size), 0 },
|
||||
{ "cache_clean_interval=%u", offsetof(struct cache,
|
||||
clean_interval_secs), 0 },
|
||||
{ "cache_min_clean_interval=%u", offsetof(struct cache,
|
||||
min_clean_interval_secs), 0 },
|
||||
FUSE_OPT_END
|
||||
};
|
||||
|
||||
int cache_parse_options(struct fuse_args *args)
|
||||
{
|
||||
cache.stat_timeout = DEFAULT_CACHE_TIMEOUT;
|
||||
cache.dir_timeout = DEFAULT_CACHE_TIMEOUT;
|
||||
cache.link_timeout = DEFAULT_CACHE_TIMEOUT;
|
||||
cache.stat_timeout_secs = DEFAULT_CACHE_TIMEOUT_SECS;
|
||||
cache.dir_timeout_secs = DEFAULT_CACHE_TIMEOUT_SECS;
|
||||
cache.link_timeout_secs = DEFAULT_CACHE_TIMEOUT_SECS;
|
||||
cache.max_size = DEFAULT_MAX_CACHE_SIZE;
|
||||
cache.clean_interval_secs = DEFAULT_CACHE_CLEAN_INTERVAL_SECS;
|
||||
cache.min_clean_interval_secs = DEFAULT_MIN_CACHE_CLEAN_INTERVAL_SECS;
|
||||
cache.on = 1;
|
||||
|
||||
return fuse_opt_parse(args, &cache, cache_opts, NULL);
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2008 Amit Singh/Google Inc.
|
||||
* Copyright (c) 2012 Anatol Pomozov
|
||||
* Copyright (c) 2011-2013 Benjamin Fleischer
|
||||
*/
|
||||
|
||||
#include "darwin_compat.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
/*
|
||||
* Semaphore implementation based on:
|
||||
*
|
||||
* Copyright (C) 2000,02 Free Software Foundation, Inc.
|
||||
* This file is part of the GNU C Library.
|
||||
* Written by Ga<EB>l Le Mignot <address@hidden>
|
||||
*
|
||||
* The GNU C Library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* The GNU C Library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with the GNU C Library; see the file COPYING.LIB. If not,
|
||||
* write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/* Semaphores */
|
||||
|
||||
#define __SEM_ID_NONE ((int)0x0)
|
||||
#define __SEM_ID_LOCAL ((int)0xcafef00d)
|
||||
|
||||
/* http://www.opengroup.org/onlinepubs/007908799/xsh/sem_init.html */
|
||||
int
|
||||
darwin_sem_init(darwin_sem_t *sem, int pshared, unsigned int value)
|
||||
{
|
||||
if (pshared) {
|
||||
errno = ENOSYS;
|
||||
return -1;
|
||||
}
|
||||
|
||||
sem->id = __SEM_ID_NONE;
|
||||
|
||||
if (pthread_cond_init(&sem->__data.local.count_cond, NULL)) {
|
||||
goto cond_init_fail;
|
||||
}
|
||||
|
||||
if (pthread_mutex_init(&sem->__data.local.count_lock, NULL)) {
|
||||
goto mutex_init_fail;
|
||||
}
|
||||
|
||||
sem->__data.local.count = value;
|
||||
sem->id = __SEM_ID_LOCAL;
|
||||
|
||||
return 0;
|
||||
|
||||
mutex_init_fail:
|
||||
|
||||
pthread_cond_destroy(&sem->__data.local.count_cond);
|
||||
|
||||
cond_init_fail:
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* http://www.opengroup.org/onlinepubs/007908799/xsh/sem_destroy.html */
|
||||
int
|
||||
darwin_sem_destroy(darwin_sem_t *sem)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
pthread_mutex_lock(&sem->__data.local.count_lock);
|
||||
|
||||
sem->id = __SEM_ID_NONE;
|
||||
pthread_cond_broadcast(&sem->__data.local.count_cond);
|
||||
|
||||
if (pthread_cond_destroy(&sem->__data.local.count_cond)) {
|
||||
res = -1;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&sem->__data.local.count_lock);
|
||||
|
||||
if (pthread_mutex_destroy(&sem->__data.local.count_lock)) {
|
||||
res = -1;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int
|
||||
darwin_sem_getvalue(darwin_sem_t *sem, unsigned int *sval)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
pthread_mutex_lock(&sem->__data.local.count_lock);
|
||||
|
||||
if (sem->id != __SEM_ID_LOCAL) {
|
||||
res = -1;
|
||||
errno = EINVAL;
|
||||
} else {
|
||||
*sval = sem->__data.local.count;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&sem->__data.local.count_lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* http://www.opengroup.org/onlinepubs/007908799/xsh/sem_post.html */
|
||||
int
|
||||
darwin_sem_post(darwin_sem_t *sem)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
pthread_mutex_lock(&sem->__data.local.count_lock);
|
||||
|
||||
if (sem->id != __SEM_ID_LOCAL) {
|
||||
res = -1;
|
||||
errno = EINVAL;
|
||||
} else if (sem->__data.local.count < DARWIN_SEM_VALUE_MAX) {
|
||||
sem->__data.local.count++;
|
||||
if (sem->__data.local.count == 1) {
|
||||
pthread_cond_signal(&sem->__data.local.count_cond);
|
||||
}
|
||||
} else {
|
||||
errno = ERANGE;
|
||||
res = -1;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&sem->__data.local.count_lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* http://www.opengroup.org/onlinepubs/009695399/functions/sem_timedwait.html */
|
||||
int
|
||||
darwin_sem_timedwait(darwin_sem_t *sem, const struct timespec *abs_timeout)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
if (abs_timeout &&
|
||||
(abs_timeout->tv_nsec < 0 || abs_timeout->tv_nsec >= 1000000000)) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
pthread_cleanup_push((void(*)(void*))&pthread_mutex_unlock,
|
||||
&sem->__data.local.count_lock);
|
||||
|
||||
pthread_mutex_lock(&sem->__data.local.count_lock);
|
||||
|
||||
if (sem->id != __SEM_ID_LOCAL) {
|
||||
errno = EINVAL;
|
||||
res = -1;
|
||||
} else {
|
||||
if (!sem->__data.local.count) {
|
||||
res = pthread_cond_timedwait(&sem->__data.local.count_cond,
|
||||
&sem->__data.local.count_lock,
|
||||
abs_timeout);
|
||||
}
|
||||
if (res) {
|
||||
assert(res == ETIMEDOUT);
|
||||
res = -1;
|
||||
errno = ETIMEDOUT;
|
||||
} else if (sem->id != __SEM_ID_LOCAL) {
|
||||
res = -1;
|
||||
errno = EINVAL;
|
||||
} else {
|
||||
sem->__data.local.count--;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_cleanup_pop(1);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* http://www.opengroup.org/onlinepubs/007908799/xsh/sem_trywait.html */
|
||||
int
|
||||
darwin_sem_trywait(darwin_sem_t *sem)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
pthread_mutex_lock(&sem->__data.local.count_lock);
|
||||
|
||||
if (sem->id != __SEM_ID_LOCAL) {
|
||||
res = -1;
|
||||
errno = EINVAL;
|
||||
} else if (sem->__data.local.count) {
|
||||
sem->__data.local.count--;
|
||||
} else {
|
||||
res = -1;
|
||||
errno = EAGAIN;
|
||||
}
|
||||
|
||||
pthread_mutex_unlock (&sem->__data.local.count_lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/* http://www.opengroup.org/onlinepubs/007908799/xsh/sem_wait.html */
|
||||
int
|
||||
darwin_sem_wait(darwin_sem_t *sem)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
pthread_cleanup_push((void(*)(void*))&pthread_mutex_unlock,
|
||||
&sem->__data.local.count_lock);
|
||||
|
||||
pthread_mutex_lock(&sem->__data.local.count_lock);
|
||||
|
||||
if (sem->id != __SEM_ID_LOCAL) {
|
||||
errno = EINVAL;
|
||||
res = -1;
|
||||
} else {
|
||||
if (!sem->__data.local.count) {
|
||||
pthread_cond_wait(&sem->__data.local.count_cond,
|
||||
&sem->__data.local.count_lock);
|
||||
if (!sem->__data.local.count) {
|
||||
/* spurious wakeup, assume it is an interruption */
|
||||
res = -1;
|
||||
errno = EINTR;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (sem->id != __SEM_ID_LOCAL) {
|
||||
res = -1;
|
||||
errno = EINVAL;
|
||||
} else {
|
||||
sem->__data.local.count--;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
pthread_cleanup_pop(1);
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2006-2008 Amit Singh/Google Inc.
|
||||
* Copyright (c) 2011-2013 Benjamin Fleischer
|
||||
*/
|
||||
|
||||
#ifndef _DARWIN_COMPAT_
|
||||
#define _DARWIN_COMPAT_
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
/* Semaphores */
|
||||
|
||||
typedef struct darwin_sem {
|
||||
int id;
|
||||
union {
|
||||
struct
|
||||
{
|
||||
unsigned int count;
|
||||
pthread_mutex_t count_lock;
|
||||
pthread_cond_t count_cond;
|
||||
} local;
|
||||
} __data;
|
||||
} darwin_sem_t;
|
||||
|
||||
#define DARWIN_SEM_VALUE_MAX ((int32_t)32767)
|
||||
|
||||
int darwin_sem_init(darwin_sem_t *sem, int pshared, unsigned int value);
|
||||
int darwin_sem_destroy(darwin_sem_t *sem);
|
||||
int darwin_sem_getvalue(darwin_sem_t *sem, unsigned int *value);
|
||||
int darwin_sem_post(darwin_sem_t *sem);
|
||||
int darwin_sem_timedwait(darwin_sem_t *sem, const struct timespec *abs_timeout);
|
||||
int darwin_sem_trywait(darwin_sem_t *sem);
|
||||
int darwin_sem_wait(darwin_sem_t *sem);
|
||||
|
||||
/* Caller must not include <semaphore.h> */
|
||||
|
||||
typedef darwin_sem_t sem_t;
|
||||
|
||||
#define sem_init(s, p, v) darwin_sem_init(s, p, v)
|
||||
#define sem_destroy(s) darwin_sem_destroy(s)
|
||||
#define sem_getvalue(s, v) darwin_sem_getvalue(s, v)
|
||||
#define sem_post(s) darwin_sem_post(s)
|
||||
#define sem_timedwait(s, t) darwin_sem_timedwait(s, t)
|
||||
#define sem_trywait(s) darwin_sem_trywait(s)
|
||||
#define sem_wait(s) darwin_sem_wait(s)
|
||||
|
||||
#define SEM_VALUE_MAX DARWIN_SEM_VALUE_MAX
|
||||
|
||||
#endif /* _DARWIN_COMPAT_ */
|
||||
+24
-32
@@ -1,5 +1,6 @@
|
||||
AC_INIT(sshfs-fuse, 2.4)
|
||||
AM_INIT_AUTOMAKE
|
||||
AC_INIT(sshfs, 2.10)
|
||||
AC_CANONICAL_TARGET
|
||||
AM_INIT_AUTOMAKE([foreign subdir-objects])
|
||||
AM_CONFIG_HEADER(config.h)
|
||||
|
||||
AC_PROG_CC
|
||||
@@ -7,46 +8,37 @@ AM_PROG_CC_C_O
|
||||
CFLAGS="$CFLAGS -Wall -W"
|
||||
LIBS=
|
||||
AC_SEARCH_LIBS(dlsym, [dl])
|
||||
sshnodelay_libs=$LIBS
|
||||
AC_SUBST(sshnodelay_libs)
|
||||
LIBS=
|
||||
|
||||
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")
|
||||
case "$target_os" in
|
||||
*linux*) osname=linux;;
|
||||
*darwin*) osname=darwin;;
|
||||
*) osname=unknown;;
|
||||
esac
|
||||
|
||||
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH
|
||||
PKG_CHECK_MODULES(SSHFS, [fuse >= 2.3 glib-2.0 gthread-2.0])
|
||||
PKG_CHECK_MODULES([SSHFS], [fuse >= 2.3 glib-2.0 gthread-2.0])
|
||||
have_fuse_opt_parse=no
|
||||
oldlibs="$LIBS"
|
||||
LIBS="$LIBS $SSHFS_LIBS"
|
||||
AC_CHECK_FUNC([fuse_opt_parse], [have_fuse_opt_parse=yes])
|
||||
LIBS="$oldlibs"
|
||||
if test "$have_fuse_opt_parse" = no; then
|
||||
CFLAGS="$CFLAGS -Icompat"
|
||||
if test "$have_fuse_opt_parse" = no -o "$osname" = darwin; then
|
||||
CFLAGS="$CFLAGS -I${srcdir}/compat"
|
||||
fi
|
||||
AM_CONDITIONAL(FUSE_OPT_COMPAT, test "$have_fuse_opt_parse" = no)
|
||||
AM_CONDITIONAL(DARWIN_COMPAT, test "$osname" = darwin)
|
||||
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
AC_CHECK_PROG(UNMOUNT_COMMAND, fusermount, fusermount -u, umount)
|
||||
|
||||
# TODO: Figure out why we special-case this in Darwin. Would be nice if
|
||||
# the default setting was consistent across platforms so we wouldn't need
|
||||
# to care about it here.
|
||||
case "$osname" in
|
||||
darwin) IDMAP_DEFAULT=user ;;
|
||||
*) IDMAP_DEFAULT=none ;;
|
||||
esac
|
||||
AC_SUBST(IDMAP_DEFAULT)
|
||||
|
||||
AC_CONFIG_FILES([Makefile test/Makefile])
|
||||
AC_OUTPUT
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
#! /bin/bash
|
||||
|
||||
(
|
||||
echo "See http://fuse.sourceforge.net/wiki/index.php/SshfsFaq for the latest"
|
||||
echo "version of this FAQ"
|
||||
echo "---"
|
||||
echo
|
||||
|
||||
lynx -nolist -dump http://fuse.sourceforge.net/wiki/index.php/SshfsFaq \
|
||||
| sed -e '1,12d' -e '/____/,$d'
|
||||
) > FAQ.txt
|
||||
+64
@@ -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')
|
||||
|
||||
+14
-7
@@ -7,7 +7,7 @@ SSHFS \- filesystem client based on ssh
|
||||
\fBsshfs\fP [\fIuser\fP@]\fBhost\fP:[\fIdir\fP] \fBmountpoint\fP [\fIoptions\fP]
|
||||
.SS unmounting
|
||||
.TP
|
||||
\fBfusermount -u 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)
|
||||
@@ -58,6 +58,9 @@ synchronous writes
|
||||
\fB\-o\fR no_readahead
|
||||
synchronous reads (no speculative readahead)
|
||||
.TP
|
||||
\fB\-o\fR sync_readdir
|
||||
synchronous readdir
|
||||
.TP
|
||||
\fB\-o\fR sshfs_debug
|
||||
print some debugging information
|
||||
.TP
|
||||
@@ -94,14 +97,14 @@ fix buffer fillup bug in server (default: on)
|
||||
.RE
|
||||
.TP
|
||||
\fB\-o\fR idmap=TYPE
|
||||
user/group ID mapping, possible types are:
|
||||
user/group ID mapping (default: @IDMAP_DEFAULT@)
|
||||
.RS 8
|
||||
.TP
|
||||
none
|
||||
no translation of the ID space (default)
|
||||
no translation of the ID space
|
||||
.TP
|
||||
user
|
||||
only translate UID of connecting user
|
||||
only translate UID/GID of connecting user
|
||||
.TP
|
||||
file
|
||||
translate UIDs/GIDs based upon the contents of \fBuidfile \fR and
|
||||
@@ -138,9 +141,15 @@ path to sftp server or subsystem (default: sftp)
|
||||
.TP
|
||||
\fB\-o\fR directport=PORT
|
||||
directly connect to PORT bypassing ssh
|
||||
.TP
|
||||
\fB\-o\fR slave
|
||||
communicate over stdin and stdout bypassing network
|
||||
.TP
|
||||
\fB\-o\fR disable_hardlink
|
||||
link(2) will return with errno set to ENOSYS. Hard links don't currently work
|
||||
perfectly on sshfs, and this confuses some programs. If that happens try
|
||||
disabling hard links with this option.
|
||||
.TP
|
||||
\fB\-o\fR transform_symlinks
|
||||
transform absolute symlinks to relative
|
||||
.TP
|
||||
@@ -256,7 +265,7 @@ perform reads synchronously
|
||||
\fB\-o\fR subdir=DIR
|
||||
prepend this directory to all paths (mandatory)
|
||||
.TP
|
||||
\fB\-o\fR [no]rellinksa
|
||||
\fB\-o\fR [no]rellinks
|
||||
transform absolute symlinks to relative
|
||||
.TP
|
||||
[iconv]
|
||||
@@ -273,5 +282,3 @@ SSHFS has been written by Miklos Szeredi <miklos@szeredi.hu>.
|
||||
.LP
|
||||
This man page was written by Bartosz Fenski <fenio@debian.org> for the
|
||||
Debian GNU/Linux distribution (but it may be used by others).
|
||||
|
||||
|
||||
@@ -11,7 +11,9 @@
|
||||
|
||||
#include <fuse.h>
|
||||
#include <fuse_opt.h>
|
||||
#if !defined(__CYGWIN__)
|
||||
#include <fuse_lowlevel.h>
|
||||
#endif
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -20,7 +22,9 @@
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <semaphore.h>
|
||||
#ifndef __APPLE__
|
||||
# include <semaphore.h>
|
||||
#endif
|
||||
#include <pthread.h>
|
||||
#include <netdb.h>
|
||||
#include <signal.h>
|
||||
@@ -38,6 +42,11 @@
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <limits.h>
|
||||
#ifdef __APPLE__
|
||||
# include <strings.h>
|
||||
# include <libgen.h>
|
||||
# include <darwin_compat.h>
|
||||
#endif
|
||||
|
||||
#include "cache.h"
|
||||
|
||||
@@ -108,6 +117,7 @@
|
||||
#define SFTP_EXT_POSIX_RENAME "posix-rename@openssh.com"
|
||||
#define SFTP_EXT_STATVFS "statvfs@openssh.com"
|
||||
#define SFTP_EXT_HARDLINK "hardlink@openssh.com"
|
||||
#define SFTP_EXT_FSYNC "fsync@openssh.com"
|
||||
|
||||
#define PROTO_VERSION 3
|
||||
|
||||
@@ -119,7 +129,15 @@
|
||||
|
||||
#define SFTP_SERVER_PATH "/usr/lib/sftp-server"
|
||||
|
||||
#define SSHNODELAY_SO "sshnodelay.so"
|
||||
/* Asynchronous readdir parameters */
|
||||
#define READDIR_START 2
|
||||
#define READDIR_MAX 32
|
||||
|
||||
#define MAX_PASSWORD 1024
|
||||
|
||||
#ifdef __APPLE__
|
||||
static char sshfs_program_path[PATH_MAX] = { 0 };
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
struct buffer {
|
||||
uint8_t *p;
|
||||
@@ -139,6 +157,7 @@ struct request {
|
||||
unsigned int want_reply;
|
||||
sem_t ready;
|
||||
uint8_t reply_type;
|
||||
uint32_t id;
|
||||
int replied;
|
||||
int error;
|
||||
struct buffer reply;
|
||||
@@ -192,8 +211,6 @@ struct sshfs {
|
||||
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;
|
||||
@@ -203,6 +220,7 @@ struct sshfs {
|
||||
int detect_uid;
|
||||
int idmap;
|
||||
int nomap;
|
||||
int disable_hardlink;
|
||||
char *uid_file;
|
||||
char *gid_file;
|
||||
GHashTable *uid_map;
|
||||
@@ -214,6 +232,7 @@ struct sshfs {
|
||||
unsigned ssh_ver;
|
||||
int sync_write;
|
||||
int sync_read;
|
||||
int sync_readdir;
|
||||
int debug;
|
||||
int foreground;
|
||||
int reconnect;
|
||||
@@ -234,6 +253,10 @@ struct sshfs {
|
||||
int server_version;
|
||||
unsigned remote_uid;
|
||||
unsigned local_uid;
|
||||
#ifdef __APPLE__
|
||||
unsigned remote_gid;
|
||||
unsigned local_gid;
|
||||
#endif
|
||||
int remote_uid_detected;
|
||||
unsigned blksize;
|
||||
char *progname;
|
||||
@@ -246,7 +269,9 @@ struct sshfs {
|
||||
int ext_posix_rename;
|
||||
int ext_statvfs;
|
||||
int ext_hardlink;
|
||||
int ext_fsync;
|
||||
mode_t mnt_mode;
|
||||
struct fuse_operations *op;
|
||||
|
||||
/* statistics */
|
||||
uint64_t bytes_sent;
|
||||
@@ -265,6 +290,7 @@ static const char *ssh_opts[] = {
|
||||
"AddressFamily",
|
||||
"BatchMode",
|
||||
"BindAddress",
|
||||
"CertificateFile",
|
||||
"ChallengeResponseAuthentication",
|
||||
"CheckHostIP",
|
||||
"Cipher",
|
||||
@@ -275,28 +301,40 @@ static const char *ssh_opts[] = {
|
||||
"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",
|
||||
@@ -304,9 +342,11 @@ static const char *ssh_opts[] = {
|
||||
"SmartcardDevice",
|
||||
"StrictHostKeyChecking",
|
||||
"TCPKeepAlive",
|
||||
"UpdateHostKeys",
|
||||
"UsePrivilegedPort",
|
||||
"UserKnownHostsFile",
|
||||
"VerifyHostKeyDNS",
|
||||
"VisualHostKey",
|
||||
NULL,
|
||||
};
|
||||
|
||||
@@ -350,6 +390,7 @@ static struct fuse_opt sshfs_opts[] = {
|
||||
SSHFS_OPT("nomap=error", nomap, NOMAP_ERROR),
|
||||
SSHFS_OPT("sshfs_sync", sync_write, 1),
|
||||
SSHFS_OPT("no_readahead", sync_read, 1),
|
||||
SSHFS_OPT("sync_readdir", sync_readdir, 1),
|
||||
SSHFS_OPT("sshfs_debug", debug, 1),
|
||||
SSHFS_OPT("reconnect", reconnect, 1),
|
||||
SSHFS_OPT("transform_symlinks", transform_symlinks, 1),
|
||||
@@ -358,6 +399,7 @@ static struct fuse_opt sshfs_opts[] = {
|
||||
SSHFS_OPT("password_stdin", password_stdin, 1),
|
||||
SSHFS_OPT("delay_connect", delay_connect, 1),
|
||||
SSHFS_OPT("slave", slave, 1),
|
||||
SSHFS_OPT("disable_hardlink", disable_hardlink, 1),
|
||||
|
||||
FUSE_OPT_KEY("-p ", KEY_PORT),
|
||||
FUSE_OPT_KEY("-C", KEY_COMPRESS),
|
||||
@@ -374,23 +416,11 @@ static struct fuse_opt sshfs_opts[] = {
|
||||
|
||||
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),
|
||||
@@ -727,8 +757,17 @@ static int buf_get_attrs(struct buffer *buf, struct stat *stbuf, int *flagsp)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
if (sshfs.remote_uid_detected) {
|
||||
if (uid == sshfs.remote_uid)
|
||||
uid = sshfs.local_uid;
|
||||
if (gid == sshfs.remote_gid)
|
||||
gid = sshfs.local_gid;
|
||||
}
|
||||
#else /* !__APPLE__ */
|
||||
if (sshfs.remote_uid_detected && uid == sshfs.remote_uid)
|
||||
uid = sshfs.local_uid;
|
||||
#endif /* __APPLE__ */
|
||||
if (sshfs.idmap == IDMAP_FILE && sshfs.uid_map)
|
||||
if (translate_id(&uid, sshfs.uid_map) == -1)
|
||||
return -EPERM;
|
||||
@@ -834,52 +873,6 @@ static void ssh_add_arg(const char *arg)
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
#ifdef SSH_NODELAY_WORKAROUND
|
||||
static int do_ssh_nodelay_workaround(void)
|
||||
{
|
||||
char *oldpreload = getenv("LD_PRELOAD");
|
||||
char *newpreload;
|
||||
char sopath[PATH_MAX];
|
||||
int res;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
newpreload = g_strdup_printf("%s%s%s",
|
||||
oldpreload ? oldpreload : "",
|
||||
oldpreload ? " " : "",
|
||||
sopath);
|
||||
|
||||
if (!newpreload || setenv("LD_PRELOAD", newpreload, 1) == -1) {
|
||||
fprintf(stderr, "warning: failed set LD_PRELOAD "
|
||||
"for ssh nodelay workaround\n");
|
||||
}
|
||||
g_free(newpreload);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int pty_expect_loop(void)
|
||||
{
|
||||
@@ -1013,29 +1006,6 @@ static int start_ssh(void)
|
||||
} 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) {
|
||||
@@ -1294,9 +1264,10 @@ static void chunk_put_locked(struct read_chunk *chunk)
|
||||
pthread_mutex_unlock(&sshfs.lock);
|
||||
}
|
||||
|
||||
static int clean_req(void *key_, struct request *req)
|
||||
static int clean_req(void *key_, struct request *req, gpointer user_data_)
|
||||
{
|
||||
(void) key_;
|
||||
(void) user_data_;
|
||||
|
||||
req->error = -EIO;
|
||||
if (req->want_reply)
|
||||
@@ -1476,6 +1447,9 @@ static int sftp_init_reply_ok(struct buffer *buf, uint32_t *version)
|
||||
if (strcmp(ext, SFTP_EXT_HARDLINK) == 0 &&
|
||||
strcmp(extdata, "1") == 0)
|
||||
sshfs.ext_hardlink = 1;
|
||||
if (strcmp(ext, SFTP_EXT_FSYNC) == 0 &&
|
||||
strcmp(extdata, "1") == 0)
|
||||
sshfs.ext_fsync = 1;
|
||||
} while (buf2.len < buf2.size);
|
||||
buf_free(&buf2);
|
||||
}
|
||||
@@ -1595,6 +1569,10 @@ static void sftp_detect_uid()
|
||||
|
||||
sshfs.remote_uid = stbuf.st_uid;
|
||||
sshfs.local_uid = getuid();
|
||||
#ifdef __APPLE__
|
||||
sshfs.remote_gid = stbuf.st_gid;
|
||||
sshfs.local_gid = getgid();
|
||||
#endif
|
||||
sshfs.remote_uid_detected = 1;
|
||||
DEBUG("remote_uid = %i\n", sshfs.remote_uid);
|
||||
|
||||
@@ -1620,7 +1598,7 @@ static int sftp_check_root(const char *base_path)
|
||||
buf_init(&buf, 0);
|
||||
buf_add_string(&buf, remote_dir);
|
||||
buf_to_iov(&buf, &iov[0]);
|
||||
if (sftp_send_iov(SSH_FXP_STAT, id, iov, 1) == -1)
|
||||
if (sftp_send_iov(SSH_FXP_LSTAT, id, iov, 1) == -1)
|
||||
goto out;
|
||||
buf_clear(&buf);
|
||||
if (sftp_read(&type, &buf) == -1)
|
||||
@@ -1790,6 +1768,13 @@ static int sftp_request_wait(struct request *req, uint8_t type,
|
||||
err = -EIO;
|
||||
break;
|
||||
|
||||
case SSH_FX_FAILURE:
|
||||
if (type == SSH_FXP_RMDIR)
|
||||
err = -ENOTEMPTY;
|
||||
else
|
||||
err = -EPERM;
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -sftp_error_to_errno(serr);
|
||||
}
|
||||
@@ -1827,6 +1812,7 @@ static int sftp_request_send(uint8_t type, struct iovec *iov, size_t count,
|
||||
if (begin_func)
|
||||
begin_func(req);
|
||||
id = sftp_get_id();
|
||||
req->id = id;
|
||||
err = start_processing_thread();
|
||||
if (err) {
|
||||
pthread_mutex_unlock(&sshfs.lock);
|
||||
@@ -1909,12 +1895,31 @@ static int sshfs_getattr(const char *path, struct stat *stbuf)
|
||||
&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);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sshfs_access(const char *path, int mask)
|
||||
{
|
||||
struct stat stbuf;
|
||||
int err = 0;
|
||||
|
||||
if (mask & X_OK) {
|
||||
err = sshfs.op->getattr(path, &stbuf);
|
||||
if (!err) {
|
||||
if (S_ISREG(stbuf.st_mode) &&
|
||||
!(stbuf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))
|
||||
err = -EACCES;
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int count_components(const char *p)
|
||||
{
|
||||
int ctr;
|
||||
@@ -2014,6 +2019,118 @@ static int sshfs_readlink(const char *path, char *linkbuf, size_t size)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sftp_readdir_send(struct request **req, struct buffer *handle)
|
||||
{
|
||||
struct iovec iov;
|
||||
|
||||
buf_to_iov(handle, &iov);
|
||||
return sftp_request_send(SSH_FXP_READDIR, &iov, 1, NULL, NULL,
|
||||
SSH_FXP_NAME, NULL, req);
|
||||
}
|
||||
|
||||
static int sshfs_req_pending(struct request *req)
|
||||
{
|
||||
if (g_hash_table_lookup(sshfs.reqtab, GUINT_TO_POINTER(req->id)))
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sftp_readdir_async(struct buffer *handle, fuse_cache_dirh_t h,
|
||||
fuse_cache_dirfil_t filler)
|
||||
{
|
||||
int err = 0;
|
||||
int outstanding = 0;
|
||||
int max = READDIR_START;
|
||||
GList *list = NULL;
|
||||
|
||||
int done = 0;
|
||||
|
||||
while (!done || outstanding) {
|
||||
struct request *req;
|
||||
struct buffer name;
|
||||
int tmperr;
|
||||
|
||||
while (!done && outstanding < max) {
|
||||
tmperr = sftp_readdir_send(&req, handle);
|
||||
|
||||
if (tmperr && !done) {
|
||||
err = tmperr;
|
||||
done = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
list = g_list_append(list, req);
|
||||
outstanding++;
|
||||
}
|
||||
|
||||
if (outstanding) {
|
||||
GList *first;
|
||||
/* wait for response to next request */
|
||||
first = g_list_first(list);
|
||||
req = first->data;
|
||||
list = g_list_delete_link(list, first);
|
||||
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 (!want_reply)
|
||||
continue;
|
||||
}
|
||||
|
||||
tmperr = sftp_request_wait(req, SSH_FXP_READDIR,
|
||||
SSH_FXP_NAME, &name);
|
||||
|
||||
if (tmperr && !done) {
|
||||
err = tmperr;
|
||||
if (err == MY_EOF)
|
||||
err = 0;
|
||||
done = 1;
|
||||
}
|
||||
if (!done) {
|
||||
err = buf_get_entries(&name, h, filler);
|
||||
buf_free(&name);
|
||||
|
||||
/* increase number of outstanding requests */
|
||||
if (max < READDIR_MAX)
|
||||
max++;
|
||||
|
||||
if (err)
|
||||
done = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(list == NULL);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sftp_readdir_sync(struct buffer *handle, fuse_cache_dirh_t h,
|
||||
fuse_cache_dirfil_t filler)
|
||||
{
|
||||
int err;
|
||||
do {
|
||||
struct buffer name;
|
||||
err = sftp_request(SSH_FXP_READDIR, handle, SSH_FXP_NAME, &name);
|
||||
if (!err) {
|
||||
err = buf_get_entries(&name, h, filler);
|
||||
buf_free(&name);
|
||||
}
|
||||
} while (!err);
|
||||
if (err == MY_EOF)
|
||||
err = 0;
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int sshfs_getdir(const char *path, fuse_cache_dirh_t h,
|
||||
fuse_cache_dirfil_t filler)
|
||||
{
|
||||
@@ -2026,16 +2143,11 @@ static int sshfs_getdir(const char *path, fuse_cache_dirh_t h,
|
||||
if (!err) {
|
||||
int err2;
|
||||
buf_finish(&handle);
|
||||
do {
|
||||
struct buffer name;
|
||||
err = sftp_request(SSH_FXP_READDIR, &handle, SSH_FXP_NAME, &name);
|
||||
if (!err) {
|
||||
err = buf_get_entries(&name, h, filler);
|
||||
buf_free(&name);
|
||||
}
|
||||
} while (!err);
|
||||
if (err == MY_EOF)
|
||||
err = 0;
|
||||
|
||||
if (sshfs.sync_readdir)
|
||||
err = sftp_readdir_sync(&handle, h, filler);
|
||||
else
|
||||
err = sftp_readdir_async(&handle, h, filler);
|
||||
|
||||
err2 = sftp_request(SSH_FXP_CLOSE, &handle, 0, NULL);
|
||||
if (!err)
|
||||
@@ -2192,7 +2304,7 @@ static int sshfs_link(const char *from, const char *to)
|
||||
{
|
||||
int err = -ENOSYS;
|
||||
|
||||
if (sshfs.ext_hardlink) {
|
||||
if (sshfs.ext_hardlink && !sshfs.disable_hardlink) {
|
||||
struct buffer buf;
|
||||
|
||||
buf_init(&buf, 0);
|
||||
@@ -2226,8 +2338,17 @@ static int sshfs_chown(const char *path, uid_t uid, gid_t gid)
|
||||
int err;
|
||||
struct buffer buf;
|
||||
|
||||
#ifdef __APPLE__
|
||||
if (sshfs.remote_uid_detected) {
|
||||
if (uid == sshfs.local_uid)
|
||||
uid = sshfs.remote_uid;
|
||||
if (gid == sshfs.local_gid)
|
||||
gid = sshfs.remote_gid;
|
||||
}
|
||||
#else /* !__APPLE__ */
|
||||
if (sshfs.remote_uid_detected && uid == sshfs.local_uid)
|
||||
uid = sshfs.remote_uid;
|
||||
#endif /* __APPLE__ */
|
||||
if (sshfs.idmap == IDMAP_FILE && sshfs.r_uid_map)
|
||||
if(translate_id(&uid, sshfs.r_uid_map) == -1)
|
||||
return -EPERM;
|
||||
@@ -2421,8 +2542,26 @@ static int sshfs_flush(const char *path, struct fuse_file_info *fi)
|
||||
static int sshfs_fsync(const char *path, int isdatasync,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
int err;
|
||||
(void) isdatasync;
|
||||
return sshfs_flush(path, fi);
|
||||
|
||||
err = sshfs_flush(path, fi);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!sshfs.ext_fsync)
|
||||
return err;
|
||||
|
||||
{
|
||||
struct buffer buf;
|
||||
struct sshfs_file *sf = get_sshfs_file(fi);
|
||||
buf_init(&buf, 0);
|
||||
buf_add_string(&buf, SFTP_EXT_FSYNC);
|
||||
buf_add_buf(&buf, &sf->handle);
|
||||
err = sftp_request(SSH_FXP_EXTENDED, &buf, SSH_FXP_STATUS, NULL);
|
||||
buf_free(&buf);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
static void sshfs_file_put(struct sshfs_file *sf)
|
||||
@@ -2968,6 +3107,9 @@ static int sshfs_fgetattr(const char *path, struct stat *stbuf,
|
||||
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);
|
||||
@@ -3115,6 +3257,7 @@ static struct fuse_cache_operations sshfs_oper = {
|
||||
.oper = {
|
||||
.init = sshfs_init,
|
||||
.getattr = sshfs_getattr,
|
||||
.access = sshfs_access,
|
||||
.readlink = sshfs_readlink,
|
||||
.mknod = sshfs_mknod,
|
||||
.mkdir = sshfs_mkdir,
|
||||
@@ -3138,6 +3281,10 @@ static struct fuse_cache_operations sshfs_oper = {
|
||||
.create = sshfs_create,
|
||||
.ftruncate = sshfs_ftruncate,
|
||||
.fgetattr = sshfs_fgetattr,
|
||||
#endif
|
||||
#if FUSE_VERSION >= 29
|
||||
.flag_nullpath_ok = 1,
|
||||
.flag_nopath = 1,
|
||||
#endif
|
||||
},
|
||||
.cache_getdir = sshfs_getdir,
|
||||
@@ -3162,23 +3309,27 @@ static void usage(const char *progname)
|
||||
" -o delay_connect delay connection to server\n"
|
||||
" -o sshfs_sync synchronous writes\n"
|
||||
" -o no_readahead synchronous reads (no speculative readahead)\n"
|
||||
" -o sync_readdir synchronous readdir\n"
|
||||
" -o sshfs_debug print some debugging information\n"
|
||||
" -o cache=BOOL enable caching {yes,no} (default: yes)\n"
|
||||
" -o cache_max_size=N sets the maximum size of the cache (default: 10000)\n"
|
||||
" -o cache_timeout=N sets timeout for caches in seconds (default: 20)\n"
|
||||
" -o cache_X_timeout=N sets timeout for {stat,dir,link} cache\n"
|
||||
" -o cache_clean_interval=N\n"
|
||||
" sets the interval for automatic cleaning of the\n"
|
||||
" cache (default: 60)\n"
|
||||
" -o cache_min_clean_interval=N\n"
|
||||
" sets the interval for forced cleaning of the\n"
|
||||
" 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"
|
||||
" -o idmap=TYPE user/group ID mapping, possible types are:\n"
|
||||
" none no translation of the ID space (default)\n"
|
||||
" user only translate UID of connecting user\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"
|
||||
" file translate UIDs/GIDs contained in uidfile/gidfile\n"
|
||||
" -o uidfile=FILE file containing username:remote_uid mappings\n"
|
||||
" -o gidfile=FILE file containing groupname:remote_gid mappings\n"
|
||||
@@ -3190,6 +3341,7 @@ static void usage(const char *progname)
|
||||
" -o sftp_server=SERV path to sftp server or subsystem (default: sftp)\n"
|
||||
" -o directport=PORT directly connect to PORT bypassing ssh\n"
|
||||
" -o slave communicate over stdin and stdout bypassing network\n"
|
||||
" -o disable_hardlink link(2) will return with errno set to ENOSYS\n"
|
||||
" -o transform_symlinks transform absolute symlinks to relative\n"
|
||||
" -o follow_symlinks follow symlinks on the server\n"
|
||||
" -o no_check_root don't check for existence of 'dir' on server\n"
|
||||
@@ -3215,10 +3367,11 @@ static int is_ssh_opt(const char *arg)
|
||||
|
||||
static int sshfs_fuse_main(struct fuse_args *args)
|
||||
{
|
||||
sshfs.op = cache_init(&sshfs_oper);
|
||||
#if FUSE_VERSION >= 26
|
||||
return fuse_main(args->argc, args->argv, cache_init(&sshfs_oper), NULL);
|
||||
return fuse_main(args->argc, args->argv, sshfs.op, NULL);
|
||||
#else
|
||||
return fuse_main(args->argc, args->argv, cache_init(&sshfs_oper));
|
||||
return fuse_main(args->argc, args->argv, sshfs.op);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -3293,10 +3446,14 @@ static int workaround_opt_proc(void *data, const char *arg, int key,
|
||||
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)
|
||||
@@ -3342,7 +3499,7 @@ static void check_large_read(struct fuse_args *args)
|
||||
static int read_password(void)
|
||||
{
|
||||
int size = getpagesize();
|
||||
int max_password = 64;
|
||||
int max_password = MIN(MAX_PASSWORD, size - 1);
|
||||
int n;
|
||||
|
||||
sshfs.password = mmap(NULL, size, PROT_READ | PROT_WRITE,
|
||||
@@ -3352,6 +3509,13 @@ static int read_password(void)
|
||||
perror("Failed to allocate locked page for password");
|
||||
return -1;
|
||||
}
|
||||
if (mlock(sshfs.password, size) != 0) {
|
||||
memset(sshfs.password, 0, size);
|
||||
munmap(sshfs.password, size);
|
||||
sshfs.password = NULL;
|
||||
perror("Failed to allocate locked page for password");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Don't use fgets() because password might stay in memory */
|
||||
for (n = 0; n < max_password; n++) {
|
||||
@@ -3379,40 +3543,60 @@ static int read_password(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Behaves similarly to strtok(), but allows for the ' ' delimiter to be escaped
|
||||
// by '\ '.
|
||||
static char *tokenize_on_space(char *str)
|
||||
{
|
||||
static char *pos = NULL;
|
||||
char *start = NULL;
|
||||
|
||||
if (str)
|
||||
pos = str;
|
||||
|
||||
if (!pos)
|
||||
return NULL;
|
||||
|
||||
// trim any leading spaces
|
||||
while (*pos == ' ')
|
||||
pos++;
|
||||
|
||||
start = pos;
|
||||
|
||||
while (pos && *pos != '\0') {
|
||||
// break on space, but not on '\ '
|
||||
if (*pos == ' ' && *(pos - 1) != '\\') {
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
|
||||
if (*pos == '\0') {
|
||||
pos = NULL;
|
||||
}
|
||||
else {
|
||||
*pos = '\0';
|
||||
pos++;
|
||||
}
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
static void set_ssh_command(void)
|
||||
{
|
||||
char *s;
|
||||
char *d;
|
||||
char *token = NULL;
|
||||
int i = 0;
|
||||
int end = 0;
|
||||
|
||||
d = sshfs.ssh_command;
|
||||
s = sshfs.ssh_command;
|
||||
while (!end) {
|
||||
switch (*s) {
|
||||
case '\0':
|
||||
end = 1;
|
||||
case ' ':
|
||||
*d = '\0';
|
||||
if (i == 0) {
|
||||
replace_arg(&sshfs.ssh_args.argv[0],
|
||||
sshfs.ssh_command);
|
||||
} else {
|
||||
if (fuse_opt_insert_arg(&sshfs.ssh_args, i,
|
||||
sshfs.ssh_command) == -1)
|
||||
_exit(1);
|
||||
}
|
||||
i++;
|
||||
d = sshfs.ssh_command;
|
||||
break;
|
||||
|
||||
case '\\':
|
||||
if (s[1])
|
||||
s++;
|
||||
default:
|
||||
*d++ = *s;
|
||||
token = tokenize_on_space(sshfs.ssh_command);
|
||||
while (token != NULL) {
|
||||
if (i == 0) {
|
||||
replace_arg(&sshfs.ssh_args.argv[0], token);
|
||||
} else {
|
||||
if (fuse_opt_insert_arg(&sshfs.ssh_args, i, token) == -1)
|
||||
_exit(1);
|
||||
}
|
||||
s++;
|
||||
i++;
|
||||
|
||||
token = tokenize_on_space(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3677,7 +3861,11 @@ static inline void load_gid_map(void)
|
||||
read_id_map(sshfs.gid_file, &groupname_to_gid, "gid", &sshfs.gid_map, &sshfs.r_gid_map);
|
||||
}
|
||||
|
||||
#ifdef __APPLE__
|
||||
int main(int argc, char *argv[], __unused char *envp[], char **exec_path)
|
||||
#else
|
||||
int main(int argc, char *argv[])
|
||||
#endif
|
||||
{
|
||||
int res;
|
||||
struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
|
||||
@@ -3686,15 +3874,21 @@ int main(int argc, char *argv[])
|
||||
const char *sftp_server;
|
||||
int libver;
|
||||
|
||||
g_thread_init(NULL);
|
||||
#ifdef __APPLE__
|
||||
if (!realpath(*exec_path, sshfs_program_path)) {
|
||||
memset(sshfs_program_path, 0, PATH_MAX);
|
||||
}
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
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
|
||||
sshfs.rename_workaround = 0;
|
||||
#endif
|
||||
sshfs.truncate_workaround = 0;
|
||||
sshfs.buflimit_workaround = 1;
|
||||
sshfs.ssh_ver = 2;
|
||||
@@ -3706,7 +3900,15 @@ int main(int argc, char *argv[])
|
||||
sshfs.delay_connect = 0;
|
||||
sshfs.slave = 0;
|
||||
sshfs.detect_uid = 0;
|
||||
sshfs.idmap = IDMAP_NONE;
|
||||
if (strcmp(IDMAP_DEFAULT, "none") == 0) {
|
||||
sshfs.idmap = IDMAP_NONE;
|
||||
} else if (strcmp(IDMAP_DEFAULT, "user") == 0) {
|
||||
sshfs.idmap = IDMAP_USER;
|
||||
} else {
|
||||
fprintf(stderr, "bad idmap default value built into sshfs; "
|
||||
"assuming none (bad logic in configure script?)\n");
|
||||
sshfs.idmap = IDMAP_NONE;
|
||||
}
|
||||
sshfs.nomap = NOMAP_ERROR;
|
||||
ssh_add_arg("ssh");
|
||||
ssh_add_arg("-x");
|
||||
@@ -3717,6 +3919,14 @@ int main(int argc, char *argv[])
|
||||
parse_workarounds() == -1)
|
||||
exit(1);
|
||||
|
||||
#if FUSE_VERSION >= 29
|
||||
// These workarounds require the "path" argument.
|
||||
if (sshfs.truncate_workaround || sshfs.fstat_workaround) {
|
||||
sshfs_oper.oper.flag_nullpath_ok = 0;
|
||||
sshfs_oper.oper.flag_nopath = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (sshfs.idmap == IDMAP_USER)
|
||||
sshfs.detect_uid = 1;
|
||||
else if (sshfs.idmap == IDMAP_FILE) {
|
||||
@@ -3829,40 +4039,66 @@ int main(int argc, char *argv[])
|
||||
char *mountpoint;
|
||||
int multithreaded;
|
||||
int foreground;
|
||||
#if !defined(__APPLE__) && !defined(__CYGWIN__)
|
||||
struct stat st;
|
||||
#endif
|
||||
|
||||
res = fuse_parse_cmdline(&args, &mountpoint, &multithreaded,
|
||||
&foreground);
|
||||
if (res == -1)
|
||||
exit(1);
|
||||
|
||||
if (!mountpoint) {
|
||||
fprintf(stderr, "ERROR: No mount point could be parsed "
|
||||
"from the command-line options\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (sshfs.slave) {
|
||||
/* Force sshfs to the foreground when using stdin+stdout */
|
||||
foreground = 1;
|
||||
}
|
||||
|
||||
#if !defined(__APPLE__) && !defined(__CYGWIN__)
|
||||
res = stat(mountpoint, &st);
|
||||
if (res == -1) {
|
||||
perror(mountpoint);
|
||||
exit(1);
|
||||
}
|
||||
sshfs.mnt_mode = st.st_mode;
|
||||
#else
|
||||
sshfs.mnt_mode = S_IFDIR | 0755;
|
||||
#endif
|
||||
|
||||
ch = fuse_mount(mountpoint, &args);
|
||||
if (!ch)
|
||||
exit(1);
|
||||
|
||||
#if !defined(__CYGWIN__)
|
||||
res = fcntl(fuse_chan_fd(ch), F_SETFD, FD_CLOEXEC);
|
||||
if (res == -1)
|
||||
perror("WARNING: failed to set FD_CLOEXEC on fuse device");
|
||||
#endif
|
||||
|
||||
fuse = fuse_new(ch, &args, cache_init(&sshfs_oper),
|
||||
sshfs.op = cache_init(&sshfs_oper);
|
||||
fuse = fuse_new(ch, &args, sshfs.op,
|
||||
sizeof(struct fuse_operations), NULL);
|
||||
if (fuse == NULL) {
|
||||
fuse_unmount(mountpoint, ch);
|
||||
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.
|
||||
*/
|
||||
res = ssh_connect();
|
||||
if (res == -1) {
|
||||
fuse_unmount(mountpoint, ch);
|
||||
@@ -3871,9 +4107,6 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
|
||||
int connect(int sock, const struct sockaddr *addr, socklen_t addrlen)
|
||||
{
|
||||
int (*next_connect)(int, const struct sockaddr *, socklen_t) =
|
||||
dlsym(RTLD_NEXT, "connect");
|
||||
int res = next_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;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
__pycache__/
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@pytest.mark.hookwrapper
|
||||
def pytest_pyfunc_call(pyfuncitem):
|
||||
outcome = yield
|
||||
failed = outcome.excinfo is not None
|
||||
if failed:
|
||||
time.sleep(1)
|
||||
|
||||
@pytest.fixture()
|
||||
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
|
||||
@pytest.yield_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
|
||||
|
||||
@pytest.hookimpl(trylast=True)
|
||||
def pytest_runtest_call(item):
|
||||
capfd = current_capfd
|
||||
if capfd is not None:
|
||||
check_test_output(capfd)
|
||||
@@ -0,0 +1,11 @@
|
||||
# Suppression file for address sanitizer.
|
||||
|
||||
# There are some leaks in command line option parsing. They should be
|
||||
# fixed at some point, but are harmless since the consume just a small,
|
||||
# constant amount of memory and do not grow.
|
||||
leak:fuse_opt_parse
|
||||
|
||||
|
||||
# Leaks in fusermount3 are harmless as well (it's a short-lived
|
||||
# process) - but patches are welcome!
|
||||
leak:fusermount.c
|
||||
@@ -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', '-fPp',
|
||||
'@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)
|
||||
@@ -0,0 +1,2 @@
|
||||
[pytest]
|
||||
addopts = --verbose --assert=rewrite --tb=native -x -r a
|
||||
Executable
+378
@@ -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]
|
||||
|
||||
@pytest.mark.parametrize("debug", (False, True))
|
||||
@pytest.mark.parametrize("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)
|
||||
Executable
+49
@@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
# Disable leak checking for now, there are some issues (or false positives)
|
||||
# that we still need to fix
|
||||
export ASAN_OPTIONS="detect_leaks=0"
|
||||
|
||||
export LSAN_OPTIONS="suppressions=$(pwd)/test/lsan_suppress.txt"
|
||||
export CC
|
||||
|
||||
TEST_CMD="python3 -m pytest --maxfail=99 test/"
|
||||
|
||||
# Standard build with Valgrind
|
||||
for CC in gcc gcc-6 clang; do
|
||||
mkdir build-${CC}; cd build-${CC}
|
||||
if [ ${CC} == 'gcc-6' ]; then
|
||||
build_opts='-D b_lundef=false'
|
||||
else
|
||||
build_opts=''
|
||||
fi
|
||||
meson -D werror=true ${build_opts} ../
|
||||
ninja
|
||||
|
||||
TEST_WITH_VALGRIND=true ${TEST_CMD}
|
||||
cd ..
|
||||
done
|
||||
(cd build-$CC; sudo ninja install)
|
||||
|
||||
# Sanitized build
|
||||
CC=clang
|
||||
for san in undefined address; do
|
||||
mkdir build-${san}; cd build-${san}
|
||||
# b_lundef=false is required to work around clang
|
||||
# bug, cf. https://groups.google.com/forum/#!topic/mesonbuild/tgEdAXIIdC4
|
||||
meson -D b_sanitize=${san} -D b_lundef=false -D werror=true ..
|
||||
ninja
|
||||
${TEST_CMD}
|
||||
sudo ninja install
|
||||
cd ..
|
||||
done
|
||||
|
||||
# Autotools build
|
||||
CC=gcc
|
||||
autoreconf -i
|
||||
./configure
|
||||
make
|
||||
${TEST_CMD}
|
||||
sudo make install
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
# Meson 0.45 requires Python 3.5 or newer
|
||||
sudo python3 -m pip install pytest meson==0.44
|
||||
valgrind --version
|
||||
ninja --version
|
||||
meson --version
|
||||
|
||||
# Setup ssh
|
||||
ssh-keygen -b 1024 -t rsa -f ~/.ssh/id_rsa -P ''
|
||||
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
|
||||
chmod 600 ~/.ssh/authorized_keys
|
||||
ssh -o "StrictHostKeyChecking=no" localhost echo "SSH connection succeeded"
|
||||
|
||||
+100
@@ -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 = []
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user