Compare commits

..

22 Commits

Author SHA1 Message Date
Nikolaus Rath 42dfaf9a5b Released 2.10 2017-08-03 18:00:34 +02:00
Nikolaus Rath e14f6c68f3 Travis: use alternative workaround suggested by support. 2017-08-03 18:00:34 +02:00
Nikolaus Rath f30b051ca3 Travis: disable trusty workaround
According to Travis support, this should be working now. We will see...
2017-08-03 18:00:34 +02:00
Nikolaus Rath d58d73c501 Include test files in "make dist" 2017-08-03 18:00:34 +02:00
Nikolaus Rath 8d9bccb987 Merge remote-tracking branch 'osxfuse/master' into sshfs_2.x 2017-07-13 15:55:51 +02:00
Nikolaus Rath cc5d6bbbc5 sftp_readdir_async(): don't access request when it may have been freed
Fixes: #7
2017-07-12 17:10:18 +02:00
Nikolaus Rath 2aced1f7a4 Abort on glib assertion errors. 2017-07-12 17:10:18 +02:00
Nikolaus Rath 9620ad0f73 Travis: use sudo-enabled environment. 2017-07-12 17:10:18 +02:00
Nikolaus Rath 2463943abb Added information about professional support. 2017-07-12 17:10:18 +02:00
Nikolaus Rath 4828a8a59c Added unit tests and travis integration 2017-07-12 17:10:18 +02:00
Nikolaus Rath 7ad9156d4d Fix Meson manpage install
Without this, Meson is looking for sshfs.1 in the source
directory. However, since that is a generated file it is in the build
directory.
2017-07-12 17:10:18 +02:00
Nikolaus Rath b13abaef12 Compile with -g -O2 by default. 2017-07-12 17:10:18 +02:00
Nikolaus Rath af97a9f4c7 Added .rst suffix to ChangeLog.
That way, programs are more likely to highlight the file correctly.
2017-07-12 17:10:18 +02:00
Nikolaus Rath 63e65aeaeb Document hardlink limitation. 2017-07-12 17:10:17 +02:00
Nikolaus Rath 70575040b4 Converted README from Markdown to Restructured Text. 2017-07-12 17:10:17 +02:00
Nikolaus Rath ca8ba022ef Added .dir-locals.el to setup correct indentation in Emacs 2017-07-12 17:10:17 +02:00
Nikolaus Rath bd2ab82ea7 Updated mailing list information. 2017-07-12 17:10:17 +02:00
Nikolaus Rath 3805f365fa Fixed compiler warnings 2017-07-12 17:10:17 +02:00
Benjamin Fleischer f187961c7a Don't require mount point to exists on macOS
By default volumes are mounted under /Volumes on macOS. Since macOS
10.12 the /Volumes directory is root-owned. In order to allow non-
privileged users to mount FUSE volumes under /Volumes FUSE will create
non-existent mount points automatically.

Fixes osxfuse/sshfs#27
2017-07-07 23:30:17 +02:00
Benjamin Fleischer 5c0dbfe3eb Fall back to global I/O size on macOS
The st_blksize value of struct stat represents the optimal block size
for file I/O operations. FUSE for macOS will use this value when
preforming read or write operations on the file. The smaller st_blksize
is the more context switches are required to complete the operation.

Setting st_blksize to 0 results in FUSE for macOS falling back to the
global I/O size, that can be specified through the "-o iosize=..."
mount-time option.

Fixes osxfuse/osxfuse#389 and osxfuse/sshfs#33
2017-07-07 23:30:14 +02:00
Benjamin Fleischer eb1b7b1b6f Merge tag 'sshfs-2.9'
Tagged release
2017-07-07 20:10:53 +02:00
Benjamin Fleischer 8589b149fd Remove call to g_slice_set_config()
It is unclear why G_SLICE_CONFIG_ALWAYS_MALLOC has been set before but
doing so with the latest version of GLib (2.46.2) results in the
following warning:

GLib-CRITICAL **: g_slice_set_config: assertion 'sys_page_size == 0'
failed

This fixes issue osxfuse/sshfs#17
2016-02-18 21:18:02 +01:00
15 changed files with 734 additions and 720 deletions
+3
View File
@@ -12,6 +12,9 @@ addons:
- clang
- gcc
- gcc-6
- fuse
- libfuse2
- libfuse-dev
before_install:
- pip install pip==8.1.1 && pip3 install pip==8.1.1
install: test/travis-install.sh
-1
View File
@@ -21,7 +21,6 @@ Bill Zissimopoulos <billziss@navimatics.com>
Chris Wolfe <cwolfe@chromium.org>
gala <gala132@users.noreply.github.com>
George Vlahavas <vlahavas@gmail.com>
harrim4n <git@harrim4n.com>
Jakub Jelen <jjelen@redhat.com>
Julio Merino <jmmv@google.com>
Julio Merino <jmmv@meroh.net>
+2 -24
View File
@@ -1,31 +1,9 @@
Release 3.2.0 (2017-08-06)
--------------------------
* Re-enabled writeback cache.
* SSHFS now supports O_APPEND.
Release 3.1.0 (2017-08-04)
--------------------------
* Temporarily disabled the writeback cache feature, since there
have been reports of dataloss when appending to files when
writeback caching is enabled.
Release 2.10 (2017-08-03)
-------------------------
* Fixed a crash due to a race condition when listing
directory contents.
* For improved backwards compatibility, SSHFS now also silently
accepts the old ``-o cache_*`` options.
Release 3.0.0 (2017-07-08)
--------------------------
* sshfs now requires libfuse 3.1.0 or newer.
* When supported by the kernel, sshfs now uses writeback caching.
* The `cache` option has been renamed to `dir_cache` for clarity.
* Added unit tests
* --debug now behaves like -o debug_sshfs, i.e. it enables sshfs
debugging messages rather than libfuse debugging messages.
* Documented limited hardlink support.
* Added support for building with Meson.
* Added support for more SSH options.
+74 -53
View File
@@ -6,26 +6,30 @@ SSHFS FAQ
2. Advantage of sshfs over NFS and Samba
3. Create the device node
4. mv fails with "Operation not permitted"
5. Configuring the ssh connection
6. What are the no_readahead and sshfs_sync options for?
7. Why does df return strange values on partitions mounted via
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?
8. How do I specify the remote mount point (since the example
10. How do I specify the remote mount point (since the example
defaults to the home directory)
9. sshfs hangs after a while
10. Following symlinks on the server side
11. Making absolute symlinks work
12. Mounting as root
13. Exporting via NFS
14. Automatical mounting using /etc/fstab
15. Why does SVN (etc...) fail with permission denied?
16. Why does SVN (etc...) fail to rename files?
17. Is there some neat way to do it in reverse?
18. Might it be reasonable to disallow loops?
19. How to mount through an intermediary ssh server, eg:
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
20. Alternative Solution
21. I seem to have successfully mounted a remote directory, but
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?
@@ -78,63 +82,80 @@ SSHFS FAQ
Use -o workaround=rename (requires sshfs version >= 1.3).
5. Configuring the ssh connection
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.
6. What are the no_readahead and sshfs_sync options for?
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.
7. Why does df return strange values on partitions mounted via sshfs?
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.
8. How do I specfy the remote mount point (since the example defaults to the
home directory)
10. How do I specfy the remote mount point (since the example defaults to the
home directory)
The example shows:
sshfs hostname: mountpoint
The example shows:
sshfs hostname: mountpoint
To specify a remote mount point use:
sshfs hostname:remotemountpoint 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.
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.
9. sshfs hangs after a while
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).
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
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.
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.
10. Following symlinks on the server side
12. Following symlinks on the server side
The -o follow_symlinks option will enable this.
11. Making absolute symlinks work
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.
12. Mounting as root
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
@@ -142,11 +163,11 @@ SSHFS FAQ
options, but files will not be created with the correct ownership,
etc...
13. Exporting via NFS
15. Exporting via NFS
Use the userspace NFS daemon http://sourceforge.net/projects/unfs
14. Automatical mounting using /etc/fstab
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
@@ -155,7 +176,7 @@ SSHFS FAQ
sshfs#guest@guest.login.com:data /mnt/guest fuse \
uid=1003,gid=100,umask=0,allow_other 0 0
15. Why does SVN (etc...) fail with permission denied?
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))
@@ -163,7 +184,7 @@ SSHFS FAQ
It has been fixed in sshfs version 1.3, but also requires FUSE version
>=2.5.X and Linux kernel version >=2.6.15.
16. Why does SVN (etc...) fail to rename files?
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':
@@ -179,7 +200,7 @@ SSHFS FAQ
semantics, but it cannot guarantee atomicity. In most of the cases
this doesn't matter, and things will work fine with this option.
17. Is there some neat way to do it in reverse?
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.
@@ -193,13 +214,13 @@ SSHFS FAQ
(Now, is there is there a smarter way that does not involve port
opening login permissions in an undesireable direction?)
18. Might it be reasonable to disallow loops?
20. Might it be reasonable to disallow loops?
sshfs localhost:/mnt /mnt
This seems to produce undesirable results. --JoshuaRodman
19. How to mount through an intermediary ssh server, eg: localhost -> A -> B;
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
@@ -209,7 +230,7 @@ SSHFS FAQ
A mounts B:/home/x on /mnt/Bx
localhost mounts A:/mnt/Bx on ~/mydir
20. Alternative Solution:
22. Alternative Solution:
1) Create a shell script to wrap the tunneling of one ssh command over
another,
@@ -223,7 +244,7 @@ SSHFS FAQ
normal but using this script as the ssh command.
$ sshfs -o ssh_command='Atunnel' B: ~/mydir
21. I seem to have successfully mounted a remote directory, but performing an
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?
+2 -2
View File
@@ -14,10 +14,10 @@ endif
sshfs_LDADD = $(SSHFS_LIBS)
sshfs_CFLAGS = $(SSHFS_CFLAGS)
sshfs_CPPFLAGS = -D_REENTRANT -DFUSE_USE_VERSION=31 -DLIBDIR=\"$(libdir)\" \
sshfs_CPPFLAGS = -D_REENTRANT -DFUSE_USE_VERSION=26 -DLIBDIR=\"$(libdir)\" \
-DIDMAP_DEFAULT="\"$(IDMAP_DEFAULT)\""
EXTRA_DIST = sshfs.1.in meson.build ChangeLog.rst
EXTRA_DIST = sshfs.1.in meson.build
CLEANFILES = sshfs.1 sshfs.1.tmp
dist_man_MANS = sshfs.1
+3 -14
View File
@@ -42,10 +42,9 @@ Installation
First, download the latest SSHFS release from
https://github.com/libfuse/sshfs/releases. On Linux and BSD, you will
also need to install libfuse_ 3.1.0 or newer. On OS-X, you need
OSXFUSE_ instead. Finally, you need the Glib_ library with development
headers (which should be available from your operating system's
package manager).
also need to have libfuse_ installed. On OS-X, you need OSXFUSE_
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
@@ -116,16 +115,6 @@ individual files when seen through an SSHFS mount, i.e. they will
appear to have different inodes and an *st_nlink* value of 1.
O_APPEND
~~~~~~~~
When writeback caching is enabled, SSHFS cannot reliably support the
``O_APPEND`` open flag and thus signals an error on open. To enable
support for unreliable ``O_APPEND`` (which may overwrite data if the
file changes on the server at a bad time), mount the file system with
``-o unreliable_append``.
Getting Help
------------
+168 -174
View File
@@ -8,7 +8,6 @@
#include "cache.h"
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
@@ -28,7 +27,7 @@ struct cache {
unsigned int max_size;
unsigned int clean_interval_secs;
unsigned int min_clean_interval_secs;
struct fuse_operations *next_oper;
struct fuse_cache_operations *next_oper;
GHashTable *table;
pthread_mutex_t lock;
time_t last_cleaned;
@@ -47,22 +46,14 @@ struct node {
time_t valid;
};
struct readdir_handle {
struct fuse_cache_dirhandle {
const char *path;
void *buf;
fuse_fill_dir_t filler;
fuse_dirh_t h;
fuse_dirfil_t filler;
GPtrArray *dir;
uint64_t wrctr;
};
struct file_handle {
/* Did we send an open request to the underlying fs? */
int is_open;
/* If so, this will hold its handle */
unsigned long fs_fh;
};
static void free_node(gpointer node_)
{
struct node *node = (struct node *) node_;
@@ -117,12 +108,15 @@ static void cache_purge_parent(const char *path)
void cache_invalidate(const char *path)
{
if (!cache.on)
return;
pthread_mutex_lock(&cache.lock);
cache_purge(path);
pthread_mutex_unlock(&cache.lock);
}
static void cache_invalidate_write(const char *path)
void cache_invalidate_write(const char *path)
{
pthread_mutex_lock(&cache.lock);
cache_purge(path);
@@ -174,6 +168,9 @@ void cache_add_attr(const char *path, const struct stat *stbuf, uint64_t wrctr)
{
struct node *node;
if (!cache.on)
return;
pthread_mutex_lock(&cache.lock);
if (wrctr == cache.write_ctr) {
node = cache_get(path);
@@ -251,25 +248,12 @@ uint64_t cache_get_write_ctr(void)
return res;
}
static void *cache_init(struct fuse_conn_info *conn,
struct fuse_config *cfg)
{
void *res;
res = cache.next_oper->init(conn, cfg);
// Cache requires a path for each request
cfg->nullpath_ok = 0;
return res;
}
static int cache_getattr(const char *path, struct stat *stbuf,
struct fuse_file_info *fi)
static int cache_getattr(const char *path, struct stat *stbuf)
{
int err = cache_get_attr(path, stbuf);
if (err) {
uint64_t wrctr = cache_get_write_ctr();
err = cache.next_oper->getattr(path, stbuf, fi);
err = cache.next_oper->oper.getattr(path, stbuf);
if (!err)
cache_add_attr(path, stbuf, wrctr);
}
@@ -293,53 +277,17 @@ static int cache_readlink(const char *path, char *buf, size_t size)
}
}
pthread_mutex_unlock(&cache.lock);
err = cache.next_oper->readlink(path, buf, size);
err = cache.next_oper->oper.readlink(path, buf, size);
if (!err)
cache_add_link(path, buf, size);
return err;
}
static int cache_opendir(const char *path, struct fuse_file_info *fi)
static int cache_dirfill(fuse_cache_dirh_t ch, const char *name,
const struct stat *stbuf)
{
(void) path;
struct file_handle *cfi;
cfi = malloc(sizeof(struct file_handle));
if(cfi == NULL)
return -ENOMEM;
cfi->is_open = 0;
fi->fh = (unsigned long) cfi;
return 0;
}
static int cache_releasedir(const char *path, struct fuse_file_info *fi)
{
int err;
struct file_handle *cfi;
cfi = (struct file_handle*) fi->fh;
if(cfi->is_open) {
fi->fh = cfi->fs_fh;
err = cache.next_oper->releasedir(path, fi);
} else
err = 0;
free(cfi);
return err;
}
static int cache_dirfill (void *buf, const char *name,
const struct stat *stbuf, off_t off,
enum fuse_fill_dir_flags flags)
{
int err;
struct readdir_handle *ch;
ch = (struct readdir_handle*) buf;
err = ch->filler(ch->buf, name, stbuf, off, flags);
int err = ch->filler(ch->h, name, 0, 0);
if (!err) {
g_ptr_array_add(ch->dir, g_strdup(name));
if (stbuf->st_mode & S_IFMT) {
@@ -354,66 +302,61 @@ static int cache_dirfill (void *buf, const char *name,
return err;
}
static int cache_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
static int cache_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler)
{
struct readdir_handle ch;
struct file_handle *cfi;
struct fuse_cache_dirhandle ch;
int err;
char **dir;
struct node *node;
assert(offset == 0);
pthread_mutex_lock(&cache.lock);
node = cache_lookup(path);
if (node != NULL && node->dir != NULL) {
time_t now = time(NULL);
if (node->dir_valid - now >= 0) {
for(dir = node->dir; *dir != NULL; dir++)
// FIXME: What about st_mode?
filler(buf, *dir, NULL, 0, 0);
filler(h, *dir, 0, 0);
pthread_mutex_unlock(&cache.lock);
return 0;
}
}
pthread_mutex_unlock(&cache.lock);
cfi = (struct file_handle*) fi->fh;
if(cfi->is_open)
fi->fh = cfi->fs_fh;
else {
if(cache.next_oper->opendir) {
err = cache.next_oper->opendir(path, fi);
if(err)
return err;
}
cfi->is_open = 1;
cfi->fs_fh = fi->fh;
}
ch.path = path;
ch.buf = buf;
ch.h = h;
ch.filler = filler;
ch.dir = g_ptr_array_new();
ch.wrctr = cache_get_write_ctr();
err = cache.next_oper->readdir(path, &ch, cache_dirfill, offset, fi, flags);
err = cache.next_oper->cache_getdir(path, &ch, cache_dirfill);
g_ptr_array_add(ch.dir, NULL);
dir = (char **) ch.dir->pdata;
if (!err) {
if (!err)
cache_add_dir(path, dir);
} else {
else
g_strfreev(dir);
}
g_ptr_array_free(ch.dir, FALSE);
return err;
}
static int cache_unity_dirfill(fuse_cache_dirh_t ch, const char *name,
const struct stat *stbuf)
{
(void) stbuf;
return ch->filler(ch->h, name, 0, 0);
}
static int cache_unity_getdir(const char *path, fuse_dirh_t h,
fuse_dirfil_t filler)
{
struct fuse_cache_dirhandle ch;
ch.h = h;
ch.filler = filler;
return cache.next_oper->cache_getdir(path, &ch, cache_unity_dirfill);
}
static int cache_mknod(const char *path, mode_t mode, dev_t rdev)
{
int err = cache.next_oper->mknod(path, mode, rdev);
int err = cache.next_oper->oper.mknod(path, mode, rdev);
if (!err)
cache_invalidate_dir(path);
return err;
@@ -421,7 +364,7 @@ static int cache_mknod(const char *path, mode_t mode, dev_t rdev)
static int cache_mkdir(const char *path, mode_t mode)
{
int err = cache.next_oper->mkdir(path, mode);
int err = cache.next_oper->oper.mkdir(path, mode);
if (!err)
cache_invalidate_dir(path);
return err;
@@ -429,7 +372,7 @@ static int cache_mkdir(const char *path, mode_t mode)
static int cache_unlink(const char *path)
{
int err = cache.next_oper->unlink(path);
int err = cache.next_oper->oper.unlink(path);
if (!err)
cache_invalidate_dir(path);
return err;
@@ -437,7 +380,7 @@ static int cache_unlink(const char *path)
static int cache_rmdir(const char *path)
{
int err = cache.next_oper->rmdir(path);
int err = cache.next_oper->oper.rmdir(path);
if (!err)
cache_invalidate_dir(path);
return err;
@@ -445,15 +388,15 @@ static int cache_rmdir(const char *path)
static int cache_symlink(const char *from, const char *to)
{
int err = cache.next_oper->symlink(from, to);
int err = cache.next_oper->oper.symlink(from, to);
if (!err)
cache_invalidate_dir(to);
return err;
}
static int cache_rename(const char *from, const char *to, unsigned int flags)
static int cache_rename(const char *from, const char *to)
{
int err = cache.next_oper->rename(from, to, flags);
int err = cache.next_oper->oper.rename(from, to);
if (!err)
cache_do_rename(from, to);
return err;
@@ -461,7 +404,7 @@ static int cache_rename(const char *from, const char *to, unsigned int flags)
static int cache_link(const char *from, const char *to)
{
int err = cache.next_oper->link(from, to);
int err = cache.next_oper->oper.link(from, to);
if (!err) {
cache_invalidate(from);
cache_invalidate_dir(to);
@@ -469,28 +412,33 @@ static int cache_link(const char *from, const char *to)
return err;
}
static int cache_chmod(const char *path, mode_t mode,
struct fuse_file_info *fi)
static int cache_chmod(const char *path, mode_t mode)
{
int err = cache.next_oper->chmod(path, mode, fi);
int err = cache.next_oper->oper.chmod(path, mode);
if (!err)
cache_invalidate(path);
return err;
}
static int cache_chown(const char *path, uid_t uid, gid_t gid,
struct fuse_file_info *fi)
static int cache_chown(const char *path, uid_t uid, gid_t gid)
{
int err = cache.next_oper->chown(path, uid, gid, fi);
int err = cache.next_oper->oper.chown(path, uid, gid);
if (!err)
cache_invalidate(path);
return err;
}
static int cache_utimens(const char *path, const struct timespec tv[2],
struct fuse_file_info *fi)
static int cache_truncate(const char *path, off_t size)
{
int err = cache.next_oper->utimens(path, tv, fi);
int err = cache.next_oper->oper.truncate(path, size);
if (!err)
cache_invalidate(path);
return err;
}
static int cache_utime(const char *path, struct utimbuf *buf)
{
int err = cache.next_oper->oper.utime(path, buf);
if (!err)
cache_invalidate(path);
return err;
@@ -499,95 +447,140 @@ static int cache_utimens(const char *path, const struct timespec tv[2],
static int cache_write(const char *path, const char *buf, size_t size,
off_t offset, struct fuse_file_info *fi)
{
int res = cache.next_oper->write(path, buf, size, offset, fi);
int res = cache.next_oper->oper.write(path, buf, size, offset, fi);
if (res >= 0)
cache_invalidate_write(path);
return res;
}
#if FUSE_VERSION >= 25
static int cache_create(const char *path, mode_t mode,
struct fuse_file_info *fi)
{
int err = cache.next_oper->create(path, mode, fi);
int err = cache.next_oper->oper.create(path, mode, fi);
if (!err)
cache_invalidate_dir(path);
return err;
}
static int cache_truncate(const char *path, off_t size,
struct fuse_file_info *fi)
static int cache_ftruncate(const char *path, off_t size,
struct fuse_file_info *fi)
{
int err = cache.next_oper->truncate(path, size, fi);
int err = cache.next_oper->oper.ftruncate(path, size, fi);
if (!err)
cache_invalidate(path);
return err;
}
static void cache_fill(struct fuse_operations *oper,
struct fuse_operations *cache_oper)
static int cache_fgetattr(const char *path, struct stat *stbuf,
struct fuse_file_info *fi)
{
cache_oper->access = oper->access;
cache_oper->chmod = oper->chmod ? cache_chmod : NULL;
cache_oper->chown = oper->chown ? cache_chown : NULL;
cache_oper->create = oper->create ? cache_create : NULL;
cache_oper->flush = oper->flush;
cache_oper->fsync = oper->fsync;
cache_oper->getattr = oper->getattr ? cache_getattr : NULL;
cache_oper->getxattr = oper->getxattr;
cache_oper->init = cache_init;
cache_oper->link = oper->link ? cache_link : NULL;
cache_oper->listxattr = oper->listxattr;
cache_oper->mkdir = oper->mkdir ? cache_mkdir : NULL;
cache_oper->mknod = oper->mknod ? cache_mknod : NULL;
cache_oper->open = oper->open;
cache_oper->opendir = cache_opendir;
cache_oper->read = oper->read;
cache_oper->readdir = oper->readdir ? cache_readdir : NULL;
cache_oper->readlink = oper->readlink ? cache_readlink : NULL;
cache_oper->release = oper->release;
cache_oper->releasedir = cache_releasedir;
cache_oper->removexattr = oper->removexattr;
cache_oper->rename = oper->rename ? cache_rename : NULL;
cache_oper->rmdir = oper->rmdir ? cache_rmdir : NULL;
cache_oper->setxattr = oper->setxattr;
cache_oper->statfs = oper->statfs;
cache_oper->symlink = oper->symlink ? cache_symlink : NULL;
cache_oper->truncate = oper->truncate ? cache_truncate : NULL;
cache_oper->unlink = oper->unlink ? cache_unlink : NULL;
cache_oper->utimens = oper->utimens ? cache_utimens : NULL;
cache_oper->write = oper->write ? cache_write : NULL;
int err = cache_get_attr(path, stbuf);
if (err) {
uint64_t wrctr = cache_get_write_ctr();
err = cache.next_oper->oper.fgetattr(path, stbuf, fi);
if (!err)
cache_add_attr(path, stbuf, wrctr);
}
return err;
}
#endif
static void cache_unity_fill(struct fuse_cache_operations *oper,
struct fuse_operations *cache_oper)
{
#if FUSE_VERSION >= 23
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;
cache_oper->mkdir = oper->oper.mkdir;
cache_oper->symlink = oper->oper.symlink;
cache_oper->unlink = oper->oper.unlink;
cache_oper->rmdir = oper->oper.rmdir;
cache_oper->rename = oper->oper.rename;
cache_oper->link = oper->oper.link;
cache_oper->chmod = oper->oper.chmod;
cache_oper->chown = oper->oper.chown;
cache_oper->truncate = oper->oper.truncate;
cache_oper->utime = oper->oper.utime;
cache_oper->open = oper->oper.open;
cache_oper->read = oper->oper.read;
cache_oper->write = oper->oper.write;
cache_oper->flush = oper->oper.flush;
cache_oper->release = oper->oper.release;
cache_oper->fsync = oper->oper.fsync;
cache_oper->statfs = oper->oper.statfs;
cache_oper->setxattr = oper->oper.setxattr;
cache_oper->getxattr = oper->oper.getxattr;
cache_oper->listxattr = oper->oper.listxattr;
cache_oper->removexattr = oper->oper.removexattr;
#if FUSE_VERSION >= 25
cache_oper->create = oper->oper.create;
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
}
struct fuse_operations *cache_wrap(struct fuse_operations *oper)
static void cache_fill(struct fuse_cache_operations *oper,
struct fuse_operations *cache_oper)
{
cache_oper->getattr = oper->oper.getattr ? cache_getattr : NULL;
cache_oper->readlink = oper->oper.readlink ? cache_readlink : NULL;
cache_oper->getdir = oper->cache_getdir ? cache_getdir : NULL;
cache_oper->mknod = oper->oper.mknod ? cache_mknod : NULL;
cache_oper->mkdir = oper->oper.mkdir ? cache_mkdir : NULL;
cache_oper->symlink = oper->oper.symlink ? cache_symlink : NULL;
cache_oper->unlink = oper->oper.unlink ? cache_unlink : NULL;
cache_oper->rmdir = oper->oper.rmdir ? cache_rmdir : NULL;
cache_oper->rename = oper->oper.rename ? cache_rename : NULL;
cache_oper->link = oper->oper.link ? cache_link : NULL;
cache_oper->chmod = oper->oper.chmod ? cache_chmod : NULL;
cache_oper->chown = oper->oper.chown ? cache_chown : NULL;
cache_oper->truncate = oper->oper.truncate ? cache_truncate : NULL;
cache_oper->utime = oper->oper.utime ? cache_utime : NULL;
cache_oper->write = oper->oper.write ? cache_write : NULL;
#if FUSE_VERSION >= 25
cache_oper->create = oper->oper.create ? cache_create : NULL;
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
}
struct fuse_operations *cache_init(struct fuse_cache_operations *oper)
{
static struct fuse_operations cache_oper;
cache.next_oper = oper;
cache_fill(oper, &cache_oper);
pthread_mutex_init(&cache.lock, NULL);
cache.table = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, free_node);
if (cache.table == NULL) {
fprintf(stderr, "failed to create cache\n");
return NULL;
cache_unity_fill(oper, &cache_oper);
if (cache.on) {
cache_fill(oper, &cache_oper);
pthread_mutex_init(&cache.lock, NULL);
cache.table = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, free_node);
if (cache.table == NULL) {
fprintf(stderr, "failed to create cache\n");
return NULL;
}
}
return &cache_oper;
}
static const struct fuse_opt cache_opts[] = {
{ "dcache_timeout=%u", offsetof(struct cache, stat_timeout_secs), 0 },
{ "dcache_timeout=%u", offsetof(struct cache, dir_timeout_secs), 0 },
{ "dcache_timeout=%u", offsetof(struct cache, link_timeout_secs), 0 },
{ "dcache_stat_timeout=%u", offsetof(struct cache, stat_timeout_secs), 0 },
{ "dcache_dir_timeout=%u", offsetof(struct cache, dir_timeout_secs), 0 },
{ "dcache_link_timeout=%u", offsetof(struct cache, link_timeout_secs), 0 },
{ "dcache_max_size=%u", offsetof(struct cache, max_size), 0 },
{ "dcache_clean_interval=%u", offsetof(struct cache,
clean_interval_secs), 0 },
{ "dcache_min_clean_interval=%u", offsetof(struct cache,
min_clean_interval_secs), 0 },
/* For backwards compatibility */
{ "cache=yes", offsetof(struct cache, on), 1 },
{ "cache=no", offsetof(struct cache, on), 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 },
@@ -596,9 +589,9 @@ static const struct fuse_opt cache_opts[] = {
{ "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 },
clean_interval_secs), 0 },
{ "cache_min_clean_interval=%u", offsetof(struct cache,
min_clean_interval_secs), 0 },
min_clean_interval_secs), 0 },
FUSE_OPT_END
};
@@ -610,6 +603,7 @@ int cache_parse_options(struct fuse_args *args)
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);
}
+14 -1
View File
@@ -9,7 +9,20 @@
#include <fuse.h>
#include <fuse_opt.h>
struct fuse_operations *cache_wrap(struct fuse_operations *oper);
#ifndef FUSE_VERSION
#define FUSE_VERSION (FUSE_MAJOR_VERSION * 10 + FUSE_MINOR_VERSION)
#endif
typedef struct fuse_cache_dirhandle *fuse_cache_dirh_t;
typedef int (*fuse_cache_dirfil_t) (fuse_cache_dirh_t h, const char *name,
const struct stat *stbuf);
struct fuse_cache_operations {
struct fuse_operations oper;
int (*cache_getdir) (const char *, fuse_cache_dirh_t, fuse_cache_dirfil_t);
};
struct fuse_operations *cache_init(struct fuse_cache_operations *oper);
int cache_parse_options(struct fuse_args *args);
void cache_add_attr(const char *path, const struct stat *stbuf, uint64_t wrctr);
void cache_invalidate(const char *path);
+2 -2
View File
@@ -1,4 +1,4 @@
AC_INIT(sshfs, 3.2.0)
AC_INIT(sshfs, 2.10)
AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE([foreign subdir-objects])
AM_CONFIG_HEADER(config.h)
@@ -17,7 +17,7 @@ case "$target_os" in
esac
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH
PKG_CHECK_MODULES([SSHFS], [fuse3 >= 3.1 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"
+3 -3
View File
@@ -1,4 +1,4 @@
project('sshfs', 'c', version: '3.1.0',
project('sshfs', 'c', version: '2.10.0',
meson_version: '>= 0.38',
default_options: [ 'buildtype=plain' ])
@@ -45,14 +45,14 @@ configure_file(input: 'sshfs.1.in',
configure_file(output: 'config.h',
configuration : cfg)
sshfs_deps = [ dependency('fuse3', version: '>= 3.1.0'),
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=31'],
c_args: ['-DFUSE_USE_VERSION=26'],
install: true,
install_dir: get_option('bindir'))
+45
View File
@@ -64,6 +64,15 @@ synchronous readdir
\fB\-o\fR sshfs_debug
print some debugging information
.TP
\fB\-o\fR cache=BOOL
enable caching {yes,no} (default: yes)
.TP
\fB\-o\fR cache_timeout=N
sets timeout for caches in seconds (default: 20)
.TP
\fB\-o\fR cache_X_timeout=N
sets timeout for {stat,dir,link} cache
.TP
\fB\-o\fR workaround=LIST
colon separated list of workarounds
.RS 8
@@ -71,6 +80,9 @@ colon separated list of workarounds
none
no workarounds enabled
.TP
all
all workarounds enabled
.TP
[no]rename
fix renaming to existing file (default: off)
.TP
@@ -168,6 +180,9 @@ allow access to other users
.TP
\fB\-o\fR allow_root
allow access to root
.TP
\fB\-o\fR nonempty
allow mounts over non\-empty file/dir
.HP
\fB\-o\fR default_permissions
enable permission checking by kernel
@@ -178,9 +193,24 @@ set filesystem name
\fB\-o\fR subtype=NAME
set filesystem type
.TP
\fB\-o\fR large_read
issue large read requests (2.4 only)
.TP
\fB\-o\fR max_read=N
set maximum size of read requests
.TP
\fB\-o\fR hard_remove
immediate removal (don't hide files)
.TP
\fB\-o\fR use_ino
let filesystem set inode numbers
.TP
\fB\-o\fR readdir_ino
try to fill in d_ino in readdir
.TP
\fB\-o\fR direct_io
use direct I/O
.TP
\fB\-o\fR kernel_cache
cache files in kernel
.TP
@@ -208,11 +238,26 @@ cache timeout for attributes (1.0s)
\fB\-o\fR ac_attr_timeout=T
auto cache timeout for attributes (attr_timeout)
.TP
\fB\-o\fR intr
allow requests to be interrupted
.TP
\fB\-o\fR intr_signal=NUM
signal to send on interrupt (10)
.TP
\fB\-o\fR modules=M1[:M2...]
names of modules to push onto filesystem stack
.TP
\fB\-o\fR max_write=N
set maximum size of write requests
.TP
\fB\-o\fR max_readahead=N
set maximum readahead
.TP
\fB\-o\fR async_read
perform reads asynchronously (default)
.TP
\fB\-o\fR sync_read
perform reads synchronously
.SS "Module options:"
.TP
[subdir]
+412 -373
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -2,7 +2,7 @@ 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', '-fP', '--preserve=mode',
command: ['cp', '-fPu', '--preserve=mode',
'@INPUT@', meson.current_build_dir() ])
# Provide something helpful when running 'ninja test'
+4 -57
View File
@@ -13,7 +13,6 @@ import stat
import shutil
import filecmp
import errno
from contextlib import contextmanager
from tempfile import NamedTemporaryFile
from util import (wait_for_mount, umount, cleanup, base_cmdline,
basename, fuse_test_marker, safe_sleep)
@@ -31,11 +30,8 @@ def name_generator(__ctr=[0]):
return 'testfile_%d' % __ctr[0]
@pytest.mark.parametrize("debug", (False, True))
@pytest.mark.parametrize("cache_timeout", (0,1))
@pytest.mark.parametrize("sync_rd", (True, False))
@pytest.mark.parametrize("writeback", (False, True))
def test_sshfs(tmpdir, debug, cache_timeout, sync_rd,
writeback, capfd):
@pytest.mark.parametrize("cache_timeout", (0, 1))
def test_sshfs(tmpdir, debug, cache_timeout, capfd):
# Avoid false positives from debug messages
#if debug:
@@ -62,21 +58,11 @@ def test_sshfs(tmpdir, debug, cache_timeout, sync_rd,
if debug:
cmdline += [ '-o', 'sshfs_debug' ]
if sync_rd:
cmdline += [ '-o', 'sync_readdir' ]
if writeback:
cmdline += [ '-o', 'writeback_cache=yes',
'-o', 'unreliable_append' ]
else:
cmdline += [ '-o', 'writeback_cache=no' ]
# SSHFS Cache
if cache_timeout == 0:
cmdline += [ '-o', 'dir_cache=no' ]
cmdline += [ '-o', 'cache=no' ]
else:
cmdline += [ '-o', 'dcache_timeout=%d' % cache_timeout,
'-o', 'dir_cache=yes' ]
cmdline += [ '-o', 'cache_timeout=%d' % cache_timeout ]
# FUSE Cache
cmdline += [ '-o', 'entry_timeout=0',
@@ -96,8 +82,6 @@ def test_sshfs(tmpdir, debug, cache_timeout, sync_rd,
tst_readdir(src_dir, mnt_dir)
tst_open_read(src_dir, mnt_dir)
tst_open_write(src_dir, mnt_dir)
tst_append(src_dir, mnt_dir)
tst_seek(src_dir, mnt_dir)
tst_create(mnt_dir)
tst_passthrough(src_dir, mnt_dir, cache_timeout)
tst_mkdir(mnt_dir)
@@ -121,17 +105,6 @@ def test_sshfs(tmpdir, debug, cache_timeout, sync_rd,
else:
umount(mount_process, mnt_dir)
@contextmanager
def os_open(name, flags):
fd = os.open(name, flags)
try:
yield fd
finally:
os.close(fd)
def os_create(name):
os.close(os.open(name, os.O_CREAT | os.O_RDWR))
def tst_unlink(src_dir, mnt_dir, cache_timeout):
name = name_generator()
fullname = mnt_dir + "/" + name
@@ -237,32 +210,6 @@ def tst_open_write(src_dir, mnt_dir):
assert filecmp.cmp(fullname, TEST_FILE, False)
def tst_append(src_dir, mnt_dir):
name = name_generator()
os_create(pjoin(src_dir, name))
fullname = pjoin(mnt_dir, name)
with os_open(fullname, os.O_WRONLY) as fd:
os.write(fd, b'foo\n')
with os_open(fullname, os.O_WRONLY|os.O_APPEND) as fd:
os.write(fd, b'bar\n')
with open(fullname, 'rb') as fh:
assert fh.read() == b'foo\nbar\n'
def tst_seek(src_dir, mnt_dir):
name = name_generator()
os_create(pjoin(src_dir, name))
fullname = pjoin(mnt_dir, name)
with os_open(fullname, os.O_WRONLY) as fd:
os.lseek(fd, 1, os.SEEK_SET)
os.write(fd, b'foobar\n')
with os_open(fullname, os.O_WRONLY) as fd:
os.lseek(fd, 4, os.SEEK_SET)
os.write(fd, b'com')
with open(fullname, 'rb') as fh:
assert fh.read() == b'\0foocom\n'
def tst_open_unlink(mnt_dir):
name = pjoin(mnt_dir, name_generator())
data1 = b'foo'
+1 -15
View File
@@ -13,23 +13,9 @@ valgrind --version
ninja --version
meson --version
# Install fuse
wget https://github.com/libfuse/libfuse/archive/master.zip
unzip master.zip
cd libfuse-master
mkdir build
cd build
export CC=gcc-6
meson ..
ninja
sudo ninja install
test -e /usr/local/lib/pkgconfig || sudo mkdir /usr/local/lib/pkgconfig
sudo mv /usr/local/lib/*/pkgconfig/* /usr/local/lib/pkgconfig/
ls -d1 /usr/local/lib/*-linux-gnu | sudo tee /etc/ld.so.conf.d/usrlocal.conf
sudo ldconfig
# Setup ssh
ssh-keygen -b 768 -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"