Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fc63c64602 | |||
| 1e6e067fdf | |||
| 44a56f0cac | |||
| 653de120c8 | |||
| 63300fd564 | |||
| 45710c81d7 | |||
| 161ece55a3 | |||
| db149d1d87 | |||
| 861d308a03 | |||
| 319f0dde18 | |||
| 82766d1093 | |||
| 6f6491cd13 | |||
| 03a693d295 | |||
| 55eb1f00e6 | |||
| 37b6969067 | |||
| ff6194b5c7 | |||
| af31551e18 | |||
| 82e899fb21 | |||
| 34146444ce | |||
| 6cc86fc0bd | |||
| 73e4cf79b0 | |||
| a202e2dcc8 | |||
| c711bf738f | |||
| 493081e8f6 | |||
| 3033dbc22e | |||
| 5f4619bac3 | |||
| b66ecb9c3a | |||
| 9ac5829b89 | |||
| 9cfeaf5120 | |||
| 56d6030f9c | |||
| 152617e53f | |||
| 0908f570dc | |||
| 5bc9ff371d | |||
| 4379773a9e | |||
| 6056149d16 | |||
| 53a14aeaf1 |
+3
-7
@@ -1,5 +1,5 @@
|
||||
sudo: required
|
||||
dist: xenial
|
||||
dist: trusty
|
||||
|
||||
language:
|
||||
- c
|
||||
@@ -12,12 +12,8 @@ addons:
|
||||
- clang
|
||||
- gcc
|
||||
- gcc-6
|
||||
- fuse
|
||||
- libfuse2
|
||||
- libfuse-dev
|
||||
- python3-pip
|
||||
- python3-setuptools
|
||||
- ninja-build
|
||||
before_install:
|
||||
- pip install pip==8.1.1 && pip3 install pip==8.1.1
|
||||
install: test/travis-install.sh
|
||||
script: test/travis-build.sh
|
||||
|
||||
|
||||
+17
-6
@@ -1,14 +1,25 @@
|
||||
Unreleased Changes
|
||||
-------------------------
|
||||
Release 3.1.0 (2017-08-04)
|
||||
--------------------------
|
||||
|
||||
* Fixed improper handling of sequential spaces spaces in "ssh_command" option
|
||||
|
||||
Release 2.10 (2017-08-03)
|
||||
-------------------------
|
||||
* Temporarily disabled the writeback cache feature, since there
|
||||
have been reports of dataloss when appending to files when
|
||||
writeback caching is enabled.
|
||||
|
||||
* 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.
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ 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=31 -DLIBDIR=\"$(libdir)\" \
|
||||
-DIDMAP_DEFAULT="\"$(IDMAP_DEFAULT)\""
|
||||
|
||||
EXTRA_DIST = sshfs.1.in meson.build
|
||||
|
||||
+6
-19
@@ -10,20 +10,6 @@ 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
|
||||
----------
|
||||
|
||||
@@ -46,7 +32,7 @@ To unmount the filesystem::
|
||||
|
||||
fusermount -u mountpoint
|
||||
|
||||
On BSD and macOS, to unmount the filesystem::
|
||||
On BSD and OS-X, to unmount the filesystem::
|
||||
|
||||
umount mountpoint
|
||||
|
||||
@@ -56,9 +42,10 @@ 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).
|
||||
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).
|
||||
|
||||
To build and install, we recommend to use Meson_ (version 0.38 or
|
||||
newer) and Ninja_. After extracting the sshfs tarball, create a
|
||||
@@ -82,7 +69,7 @@ tests requires the `py.test`_ Python module)::
|
||||
$ sudo ninja install
|
||||
|
||||
.. _libfuse: http://github.com/libfuse/libfuse
|
||||
.. _macFUSE: https://macfuse.io/
|
||||
.. _OSXFUSE: https://osxfuse.github.io/
|
||||
.. _Glib: https://developer.gnome.org/glib/stable/
|
||||
.. _Meson: http://mesonbuild.com/
|
||||
.. _Ninja: https://ninja-build.org/
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "cache.h"
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
@@ -27,7 +28,7 @@ struct cache {
|
||||
unsigned int max_size;
|
||||
unsigned int clean_interval_secs;
|
||||
unsigned int min_clean_interval_secs;
|
||||
struct fuse_cache_operations *next_oper;
|
||||
struct fuse_operations *next_oper;
|
||||
GHashTable *table;
|
||||
pthread_mutex_t lock;
|
||||
time_t last_cleaned;
|
||||
@@ -46,14 +47,22 @@ struct node {
|
||||
time_t valid;
|
||||
};
|
||||
|
||||
struct fuse_cache_dirhandle {
|
||||
struct readdir_handle {
|
||||
const char *path;
|
||||
fuse_dirh_t h;
|
||||
fuse_dirfil_t filler;
|
||||
void *buf;
|
||||
fuse_fill_dir_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_;
|
||||
@@ -108,9 +117,6 @@ 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);
|
||||
@@ -168,9 +174,6 @@ 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);
|
||||
@@ -248,12 +251,25 @@ uint64_t cache_get_write_ctr(void)
|
||||
return res;
|
||||
}
|
||||
|
||||
static int cache_getattr(const char *path, struct stat *stbuf)
|
||||
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)
|
||||
{
|
||||
int err = cache_get_attr(path, stbuf);
|
||||
if (err) {
|
||||
uint64_t wrctr = cache_get_write_ctr();
|
||||
err = cache.next_oper->oper.getattr(path, stbuf);
|
||||
err = cache.next_oper->getattr(path, stbuf, fi);
|
||||
if (!err)
|
||||
cache_add_attr(path, stbuf, wrctr);
|
||||
}
|
||||
@@ -277,17 +293,53 @@ static int cache_readlink(const char *path, char *buf, size_t size)
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&cache.lock);
|
||||
err = cache.next_oper->oper.readlink(path, buf, size);
|
||||
err = cache.next_oper->readlink(path, buf, size);
|
||||
if (!err)
|
||||
cache_add_link(path, buf, size);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cache_dirfill(fuse_cache_dirh_t ch, const char *name,
|
||||
const struct stat *stbuf)
|
||||
|
||||
static int cache_opendir(const char *path, struct fuse_file_info *fi)
|
||||
{
|
||||
int err = ch->filler(ch->h, name, 0, 0);
|
||||
(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);
|
||||
if (!err) {
|
||||
g_ptr_array_add(ch->dir, g_strdup(name));
|
||||
if (stbuf->st_mode & S_IFMT) {
|
||||
@@ -302,61 +354,66 @@ static int cache_dirfill(fuse_cache_dirh_t ch, const char *name,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cache_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler)
|
||||
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)
|
||||
{
|
||||
struct fuse_cache_dirhandle ch;
|
||||
struct readdir_handle ch;
|
||||
struct file_handle *cfi;
|
||||
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++)
|
||||
filler(h, *dir, 0, 0);
|
||||
// FIXME: What about st_mode?
|
||||
filler(buf, *dir, NULL, 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.h = h;
|
||||
ch.buf = buf;
|
||||
ch.filler = filler;
|
||||
ch.dir = g_ptr_array_new();
|
||||
ch.wrctr = cache_get_write_ctr();
|
||||
err = cache.next_oper->cache_getdir(path, &ch, cache_dirfill);
|
||||
err = cache.next_oper->readdir(path, &ch, cache_dirfill, offset, fi, flags);
|
||||
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->oper.mknod(path, mode, rdev);
|
||||
int err = cache.next_oper->mknod(path, mode, rdev);
|
||||
if (!err)
|
||||
cache_invalidate_dir(path);
|
||||
return err;
|
||||
@@ -364,7 +421,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->oper.mkdir(path, mode);
|
||||
int err = cache.next_oper->mkdir(path, mode);
|
||||
if (!err)
|
||||
cache_invalidate_dir(path);
|
||||
return err;
|
||||
@@ -372,7 +429,7 @@ static int cache_mkdir(const char *path, mode_t mode)
|
||||
|
||||
static int cache_unlink(const char *path)
|
||||
{
|
||||
int err = cache.next_oper->oper.unlink(path);
|
||||
int err = cache.next_oper->unlink(path);
|
||||
if (!err)
|
||||
cache_invalidate_dir(path);
|
||||
return err;
|
||||
@@ -380,7 +437,7 @@ static int cache_unlink(const char *path)
|
||||
|
||||
static int cache_rmdir(const char *path)
|
||||
{
|
||||
int err = cache.next_oper->oper.rmdir(path);
|
||||
int err = cache.next_oper->rmdir(path);
|
||||
if (!err)
|
||||
cache_invalidate_dir(path);
|
||||
return err;
|
||||
@@ -388,15 +445,15 @@ static int cache_rmdir(const char *path)
|
||||
|
||||
static int cache_symlink(const char *from, const char *to)
|
||||
{
|
||||
int err = cache.next_oper->oper.symlink(from, to);
|
||||
int err = cache.next_oper->symlink(from, to);
|
||||
if (!err)
|
||||
cache_invalidate_dir(to);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cache_rename(const char *from, const char *to)
|
||||
static int cache_rename(const char *from, const char *to, unsigned int flags)
|
||||
{
|
||||
int err = cache.next_oper->oper.rename(from, to);
|
||||
int err = cache.next_oper->rename(from, to, flags);
|
||||
if (!err)
|
||||
cache_do_rename(from, to);
|
||||
return err;
|
||||
@@ -404,7 +461,7 @@ static int cache_rename(const char *from, const char *to)
|
||||
|
||||
static int cache_link(const char *from, const char *to)
|
||||
{
|
||||
int err = cache.next_oper->oper.link(from, to);
|
||||
int err = cache.next_oper->link(from, to);
|
||||
if (!err) {
|
||||
cache_invalidate(from);
|
||||
cache_invalidate_dir(to);
|
||||
@@ -412,33 +469,28 @@ static int cache_link(const char *from, const char *to)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cache_chmod(const char *path, mode_t mode)
|
||||
static int cache_chmod(const char *path, mode_t mode,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
int err = cache.next_oper->oper.chmod(path, mode);
|
||||
int err = cache.next_oper->chmod(path, mode, fi);
|
||||
if (!err)
|
||||
cache_invalidate(path);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cache_chown(const char *path, uid_t uid, gid_t gid)
|
||||
static int cache_chown(const char *path, uid_t uid, gid_t gid,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
int err = cache.next_oper->oper.chown(path, uid, gid);
|
||||
int err = cache.next_oper->chown(path, uid, gid, fi);
|
||||
if (!err)
|
||||
cache_invalidate(path);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cache_truncate(const char *path, off_t size)
|
||||
static int cache_utimens(const char *path, const struct timespec tv[2],
|
||||
struct fuse_file_info *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);
|
||||
int err = cache.next_oper->utimens(path, tv, fi);
|
||||
if (!err)
|
||||
cache_invalidate(path);
|
||||
return err;
|
||||
@@ -447,140 +499,95 @@ static int cache_utime(const char *path, struct utimbuf *buf)
|
||||
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->oper.write(path, buf, size, offset, fi);
|
||||
int res = cache.next_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->oper.create(path, mode, fi);
|
||||
int err = cache.next_oper->create(path, mode, fi);
|
||||
if (!err)
|
||||
cache_invalidate_dir(path);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cache_ftruncate(const char *path, off_t size,
|
||||
struct fuse_file_info *fi)
|
||||
static int cache_truncate(const char *path, off_t size,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
int err = cache.next_oper->oper.ftruncate(path, size, fi);
|
||||
int err = cache.next_oper->truncate(path, size, fi);
|
||||
if (!err)
|
||||
cache_invalidate(path);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cache_fgetattr(const char *path, struct stat *stbuf,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
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
|
||||
}
|
||||
|
||||
static void cache_fill(struct fuse_cache_operations *oper,
|
||||
static void cache_fill(struct fuse_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
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
struct fuse_operations *cache_init(struct fuse_cache_operations *oper)
|
||||
struct fuse_operations *cache_wrap(struct fuse_operations *oper)
|
||||
{
|
||||
static struct fuse_operations cache_oper;
|
||||
cache.next_oper = oper;
|
||||
|
||||
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;
|
||||
}
|
||||
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[] = {
|
||||
{ "cache=yes", offsetof(struct cache, on), 1 },
|
||||
{ "cache=no", offsetof(struct cache, on), 0 },
|
||||
{ "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_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 },
|
||||
@@ -589,9 +596,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
|
||||
};
|
||||
|
||||
@@ -603,7 +610,6 @@ 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);
|
||||
}
|
||||
|
||||
@@ -9,20 +9,7 @@
|
||||
#include <fuse.h>
|
||||
#include <fuse_opt.h>
|
||||
|
||||
#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);
|
||||
struct fuse_operations *cache_wrap(struct fuse_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
@@ -1,4 +1,4 @@
|
||||
AC_INIT(sshfs, 2.10)
|
||||
AC_INIT(sshfs, 3.1.0)
|
||||
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], [fuse >= 2.3 glib-2.0 gthread-2.0])
|
||||
PKG_CHECK_MODULES([SSHFS], [fuse3 >= 3.1 glib-2.0 gthread-2.0])
|
||||
have_fuse_opt_parse=no
|
||||
oldlibs="$LIBS"
|
||||
LIBS="$LIBS $SSHFS_LIBS"
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
project('sshfs', 'c', version: '2.10.0',
|
||||
project('sshfs', 'c', version: '3.1.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('fuse', version: '>= 2.3'),
|
||||
sshfs_deps = [ dependency('fuse3', version: '>= 3.1.0'),
|
||||
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'],
|
||||
c_args: ['-DFUSE_USE_VERSION=31'],
|
||||
install: true,
|
||||
install_dir: get_option('bindir'))
|
||||
|
||||
|
||||
+1
-1
@@ -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', '-fPp',
|
||||
command: ['cp', '-fPu', '--preserve=mode',
|
||||
'@INPUT@', meson.current_build_dir() ])
|
||||
|
||||
# Provide something helpful when running 'ninja test'
|
||||
|
||||
+9
-4
@@ -30,8 +30,9 @@ def name_generator(__ctr=[0]):
|
||||
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):
|
||||
@pytest.mark.parametrize("cache_timeout", (0,1))
|
||||
@pytest.mark.parametrize("sync_rd", (True, False))
|
||||
def test_sshfs(tmpdir, debug, cache_timeout, sync_rd, capfd):
|
||||
|
||||
# Avoid false positives from debug messages
|
||||
#if debug:
|
||||
@@ -58,11 +59,15 @@ def test_sshfs(tmpdir, debug, cache_timeout, capfd):
|
||||
if debug:
|
||||
cmdline += [ '-o', 'sshfs_debug' ]
|
||||
|
||||
if sync_rd:
|
||||
cmdline += [ '-o', 'sync_readdir' ]
|
||||
|
||||
# SSHFS Cache
|
||||
if cache_timeout == 0:
|
||||
cmdline += [ '-o', 'cache=no' ]
|
||||
cmdline += [ '-o', 'dir_cache=no' ]
|
||||
else:
|
||||
cmdline += [ '-o', 'cache_timeout=%d' % cache_timeout ]
|
||||
cmdline += [ '-o', 'dcache_timeout=%d' % cache_timeout,
|
||||
'-o', 'dir_cache=yes' ]
|
||||
|
||||
# FUSE Cache
|
||||
cmdline += [ '-o', 'entry_timeout=0',
|
||||
|
||||
@@ -36,7 +36,6 @@ for san in undefined address; do
|
||||
meson -D b_sanitize=${san} -D b_lundef=false -D werror=true ..
|
||||
ninja
|
||||
${TEST_CMD}
|
||||
sudo ninja install
|
||||
cd ..
|
||||
done
|
||||
|
||||
|
||||
+23
-4
@@ -2,15 +2,34 @@
|
||||
|
||||
set -e
|
||||
|
||||
# Meson 0.45 requires Python 3.5 or newer
|
||||
sudo python3 -m pip install pytest meson==0.44
|
||||
sudo ln -svf $(which python3) /usr/bin/python3
|
||||
sudo python3 -m pip install pytest meson
|
||||
wget https://github.com/ninja-build/ninja/releases/download/v1.7.2/ninja-linux.zip
|
||||
unzip ninja-linux.zip
|
||||
chmod 755 ninja
|
||||
sudo chown root:root ninja
|
||||
sudo mv -fv ninja /usr/local/bin
|
||||
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 1024 -t rsa -f ~/.ssh/id_rsa -P ''
|
||||
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"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user