Switch to libfuse 3.0.

This commit is contained in:
Nikolaus Rath 2017-06-21 15:05:20 -07:00
parent 6cc86fc0bd
commit 34146444ce
11 changed files with 383 additions and 352 deletions

View File

@ -13,9 +13,6 @@ addons:
- clang - clang
- gcc - gcc
- gcc-6 - gcc-6
- fuse
- libfuse2
- libfuse-dev
install: test/travis-install.sh install: test/travis-install.sh
script: test/travis-build.sh script: test/travis-build.sh

View File

@ -1,7 +1,12 @@
Unreleased Changes Unreleased Changes
------------------ ------------------
* sshfs now requires libfuse 3.0.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 * 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. * Documented limited hardlink support.
* Added support for building with Meson. * Added support for building with Meson.
* Added support for more SSH options. * Added support for more SSH options.

View File

@ -12,7 +12,7 @@ endif
sshfs_LDADD = $(SSHFS_LIBS) sshfs_LDADD = $(SSHFS_LIBS)
sshfs_CFLAGS = $(SSHFS_CFLAGS) sshfs_CFLAGS = $(SSHFS_CFLAGS)
sshfs_CPPFLAGS = -D_REENTRANT -DFUSE_USE_VERSION=26 -DLIBDIR=\"$(libdir)\" \ sshfs_CPPFLAGS = -D_REENTRANT -DFUSE_USE_VERSION=30 -DLIBDIR=\"$(libdir)\" \
-DIDMAP_DEFAULT="\"$(IDMAP_DEFAULT)\"" -DIDMAP_DEFAULT="\"$(IDMAP_DEFAULT)\""
EXTRA_DIST = sshfs.1.in meson.build EXTRA_DIST = sshfs.1.in meson.build

View File

@ -42,9 +42,10 @@ Installation
First, download the latest SSHFS release from First, download the latest SSHFS release from
https://github.com/libfuse/sshfs/releases. On Linux and BSD, you will https://github.com/libfuse/sshfs/releases. On Linux and BSD, you will
also need to have libfuse_ installed. On OS-X, you need OSXFUSE_ also need to install libfuse_ 3.0.0 or newer. On OS-X, you need
instead. Finally, you need the Glib_ development package (which should OSXFUSE_ instead. Finally, you need the Glib_ library with development
be available from your operating system's package manager). 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 To build and install, we recommend to use Meson_ (version 0.38 or
newer) and Ninja_. After extracting the sshfs tarball, create a newer) and Ninja_. After extracting the sshfs tarball, create a

107
cache.c
View File

@ -251,12 +251,25 @@ uint64_t cache_get_write_ctr(void)
return res; 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); int err = cache_get_attr(path, stbuf);
if (err) { if (err) {
uint64_t wrctr = cache_get_write_ctr(); uint64_t wrctr = cache_get_write_ctr();
err = cache.next_oper->getattr(path, stbuf); err = cache.next_oper->getattr(path, stbuf, fi);
if (!err) if (!err)
cache_add_attr(path, stbuf, wrctr); cache_add_attr(path, stbuf, wrctr);
} }
@ -319,13 +332,14 @@ static int cache_releasedir(const char *path, struct fuse_file_info *fi)
} }
static int cache_dirfill (void *buf, const char *name, static int cache_dirfill (void *buf, const char *name,
const struct stat *stbuf, off_t off) const struct stat *stbuf, off_t off,
enum fuse_fill_dir_flags flags)
{ {
int err; int err;
struct readdir_handle *ch; struct readdir_handle *ch;
ch = (struct readdir_handle*) buf; ch = (struct readdir_handle*) buf;
err = ch->filler(ch->buf, name, stbuf, off); err = ch->filler(ch->buf, name, stbuf, off, flags);
if (!err) { if (!err) {
g_ptr_array_add(ch->dir, g_strdup(name)); g_ptr_array_add(ch->dir, g_strdup(name));
if (stbuf->st_mode & S_IFMT) { if (stbuf->st_mode & S_IFMT) {
@ -341,7 +355,8 @@ static int cache_dirfill (void *buf, const char *name,
} }
static int cache_readdir(const char *path, void *buf, fuse_fill_dir_t filler, static int cache_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi) off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{ {
struct readdir_handle ch; struct readdir_handle ch;
struct file_handle *cfi; struct file_handle *cfi;
@ -358,7 +373,7 @@ static int cache_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
if (node->dir_valid - now >= 0) { if (node->dir_valid - now >= 0) {
for(dir = node->dir; *dir != NULL; dir++) for(dir = node->dir; *dir != NULL; dir++)
// FIXME: What about st_mode? // FIXME: What about st_mode?
filler(buf, *dir, 0, 0); filler(buf, *dir, NULL, 0, 0);
pthread_mutex_unlock(&cache.lock); pthread_mutex_unlock(&cache.lock);
return 0; return 0;
} }
@ -383,7 +398,7 @@ static int cache_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
ch.filler = filler; ch.filler = filler;
ch.dir = g_ptr_array_new(); ch.dir = g_ptr_array_new();
ch.wrctr = cache_get_write_ctr(); ch.wrctr = cache_get_write_ctr();
err = cache.next_oper->readdir(path, &ch, cache_dirfill, offset, fi); err = cache.next_oper->readdir(path, &ch, cache_dirfill, offset, fi, flags);
g_ptr_array_add(ch.dir, NULL); g_ptr_array_add(ch.dir, NULL);
dir = (char **) ch.dir->pdata; dir = (char **) ch.dir->pdata;
if (!err) { if (!err) {
@ -436,9 +451,9 @@ static int cache_symlink(const char *from, const char *to)
return err; 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->rename(from, to); int err = cache.next_oper->rename(from, to, flags);
if (!err) if (!err)
cache_do_rename(from, to); cache_do_rename(from, to);
return err; return err;
@ -454,33 +469,28 @@ static int cache_link(const char *from, const char *to)
return err; 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->chmod(path, mode); int err = cache.next_oper->chmod(path, mode, fi);
if (!err) if (!err)
cache_invalidate(path); cache_invalidate(path);
return err; 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->chown(path, uid, gid); int err = cache.next_oper->chown(path, uid, gid, fi);
if (!err) if (!err)
cache_invalidate(path); cache_invalidate(path);
return err; 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->truncate(path, size); int err = cache.next_oper->utimens(path, tv, fi);
if (!err)
cache_invalidate(path);
return err;
}
static int cache_utime(const char *path, struct utimbuf *buf)
{
int err = cache.next_oper->utime(path, buf);
if (!err) if (!err)
cache_invalidate(path); cache_invalidate(path);
return err; return err;
@ -504,28 +514,15 @@ static int cache_create(const char *path, mode_t mode,
return err; return err;
} }
static int cache_ftruncate(const char *path, off_t size, static int cache_truncate(const char *path, off_t size,
struct fuse_file_info *fi) struct fuse_file_info *fi)
{ {
int err = cache.next_oper->ftruncate(path, size, fi); int err = cache.next_oper->truncate(path, size, fi);
if (!err) if (!err)
cache_invalidate(path); cache_invalidate(path);
return err; 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->fgetattr(path, stbuf, fi);
if (!err)
cache_add_attr(path, stbuf, wrctr);
}
return err;
}
static void cache_fill(struct fuse_operations *oper, static void cache_fill(struct fuse_operations *oper,
struct fuse_operations *cache_oper) struct fuse_operations *cache_oper)
{ {
@ -533,13 +530,11 @@ static void cache_fill(struct fuse_operations *oper,
cache_oper->chmod = oper->chmod ? cache_chmod : NULL; cache_oper->chmod = oper->chmod ? cache_chmod : NULL;
cache_oper->chown = oper->chown ? cache_chown : NULL; cache_oper->chown = oper->chown ? cache_chown : NULL;
cache_oper->create = oper->create ? cache_create : NULL; cache_oper->create = oper->create ? cache_create : NULL;
cache_oper->fgetattr = oper->fgetattr ? cache_fgetattr : NULL;
cache_oper->flush = oper->flush; cache_oper->flush = oper->flush;
cache_oper->fsync = oper->fsync; cache_oper->fsync = oper->fsync;
cache_oper->ftruncate = oper->ftruncate ? cache_ftruncate : NULL;
cache_oper->getattr = oper->getattr ? cache_getattr : NULL; cache_oper->getattr = oper->getattr ? cache_getattr : NULL;
cache_oper->getxattr = oper->getxattr; cache_oper->getxattr = oper->getxattr;
cache_oper->init = oper->init; cache_oper->init = cache_init;
cache_oper->link = oper->link ? cache_link : NULL; cache_oper->link = oper->link ? cache_link : NULL;
cache_oper->listxattr = oper->listxattr; cache_oper->listxattr = oper->listxattr;
cache_oper->mkdir = oper->mkdir ? cache_mkdir : NULL; cache_oper->mkdir = oper->mkdir ? cache_mkdir : NULL;
@ -559,13 +554,11 @@ static void cache_fill(struct fuse_operations *oper,
cache_oper->symlink = oper->symlink ? cache_symlink : NULL; cache_oper->symlink = oper->symlink ? cache_symlink : NULL;
cache_oper->truncate = oper->truncate ? cache_truncate : NULL; cache_oper->truncate = oper->truncate ? cache_truncate : NULL;
cache_oper->unlink = oper->unlink ? cache_unlink : NULL; cache_oper->unlink = oper->unlink ? cache_unlink : NULL;
cache_oper->utime = oper->utime ? cache_utime : NULL; cache_oper->utimens = oper->utimens ? cache_utimens : NULL;
cache_oper->write = oper->write ? cache_write : NULL; cache_oper->write = oper->write ? cache_write : NULL;
cache_oper->flag_nopath = 0;
cache_oper->flag_nullpath_ok = 0;
} }
struct fuse_operations *cache_init(struct fuse_operations *oper) struct fuse_operations *cache_wrap(struct fuse_operations *oper)
{ {
static struct fuse_operations cache_oper; static struct fuse_operations cache_oper;
cache.next_oper = oper; cache.next_oper = oper;
@ -582,17 +575,17 @@ struct fuse_operations *cache_init(struct fuse_operations *oper)
} }
static const struct fuse_opt cache_opts[] = { static const struct fuse_opt cache_opts[] = {
{ "cache_timeout=%u", offsetof(struct cache, stat_timeout_secs), 0 }, { "dcache_timeout=%u", offsetof(struct cache, stat_timeout_secs), 0 },
{ "cache_timeout=%u", offsetof(struct cache, dir_timeout_secs), 0 }, { "dcache_timeout=%u", offsetof(struct cache, dir_timeout_secs), 0 },
{ "cache_timeout=%u", offsetof(struct cache, link_timeout_secs), 0 }, { "dcache_timeout=%u", offsetof(struct cache, link_timeout_secs), 0 },
{ "cache_stat_timeout=%u", offsetof(struct cache, stat_timeout_secs), 0 }, { "dcache_stat_timeout=%u", offsetof(struct cache, stat_timeout_secs), 0 },
{ "cache_dir_timeout=%u", offsetof(struct cache, dir_timeout_secs), 0 }, { "dcache_dir_timeout=%u", offsetof(struct cache, dir_timeout_secs), 0 },
{ "cache_link_timeout=%u", offsetof(struct cache, link_timeout_secs), 0 }, { "dcache_link_timeout=%u", offsetof(struct cache, link_timeout_secs), 0 },
{ "cache_max_size=%u", offsetof(struct cache, max_size), 0 }, { "dcache_max_size=%u", offsetof(struct cache, max_size), 0 },
{ "cache_clean_interval=%u", offsetof(struct cache, { "dcache_clean_interval=%u", offsetof(struct cache,
clean_interval_secs), 0 }, clean_interval_secs), 0 },
{ "cache_min_clean_interval=%u", offsetof(struct cache, { "dcache_min_clean_interval=%u", offsetof(struct cache,
min_clean_interval_secs), 0 }, min_clean_interval_secs), 0 },
FUSE_OPT_END FUSE_OPT_END
}; };

View File

@ -9,7 +9,7 @@
#include <fuse.h> #include <fuse.h>
#include <fuse_opt.h> #include <fuse_opt.h>
struct fuse_operations *cache_init(struct fuse_operations *oper); struct fuse_operations *cache_wrap(struct fuse_operations *oper);
int cache_parse_options(struct fuse_args *args); int cache_parse_options(struct fuse_args *args);
void cache_add_attr(const char *path, const struct stat *stbuf, uint64_t wrctr); void cache_add_attr(const char *path, const struct stat *stbuf, uint64_t wrctr);
void cache_invalidate(const char *path); void cache_invalidate(const char *path);

View File

@ -17,7 +17,7 @@ case "$target_os" in
esac esac
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH 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 >= 3.0 glib-2.0 gthread-2.0])
have_fuse_opt_parse=no have_fuse_opt_parse=no
oldlibs="$LIBS" oldlibs="$LIBS"
LIBS="$LIBS $SSHFS_LIBS" LIBS="$LIBS $SSHFS_LIBS"

View File

@ -45,14 +45,14 @@ configure_file(input: 'sshfs.1.in',
configure_file(output: 'config.h', configure_file(output: 'config.h',
configuration : cfg) configuration : cfg)
sshfs_deps = [ dependency('fuse', version: '>= 2.9'), sshfs_deps = [ dependency('fuse3', version: '>= 3.0.0'),
dependency('glib-2.0'), dependency('glib-2.0'),
dependency('gthread-2.0') ] dependency('gthread-2.0') ]
executable('sshfs', sshfs_sources, executable('sshfs', sshfs_sources,
include_directories: include_dirs, include_directories: include_dirs,
dependencies: sshfs_deps, dependencies: sshfs_deps,
c_args: ['-DFUSE_USE_VERSION=26'], c_args: ['-DFUSE_USE_VERSION=30'],
install: true, install: true,
install_dir: get_option('bindir')) install_dir: get_option('bindir'))

582
sshfs.c
View File

@ -224,7 +224,12 @@ struct sshfs {
int idmap; int idmap;
int nomap; int nomap;
int disable_hardlink; int disable_hardlink;
int cache; int dir_cache;
int writeback_cache;
int show_version;
int show_help;
int singlethread;
char *mountpoint;
char *uid_file; char *uid_file;
char *gid_file; char *gid_file;
GHashTable *uid_map; GHashTable *uid_map;
@ -357,9 +362,6 @@ static const char *ssh_opts[] = {
enum { enum {
KEY_PORT, KEY_PORT,
KEY_COMPRESS, KEY_COMPRESS,
KEY_HELP,
KEY_VERSION,
KEY_FOREGROUND,
KEY_CONFIGFILE, KEY_CONFIGFILE,
}; };
@ -404,18 +406,22 @@ static struct fuse_opt sshfs_opts[] = {
SSHFS_OPT("delay_connect", delay_connect, 1), SSHFS_OPT("delay_connect", delay_connect, 1),
SSHFS_OPT("slave", slave, 1), SSHFS_OPT("slave", slave, 1),
SSHFS_OPT("disable_hardlink", disable_hardlink, 1), SSHFS_OPT("disable_hardlink", disable_hardlink, 1),
SSHFS_OPT("cache=yes", cache, 1), SSHFS_OPT("dir_cache=yes", dir_cache, 1),
SSHFS_OPT("cache=no", cache, 0), SSHFS_OPT("dir_cache=no", dir_cache, 0),
SSHFS_OPT("writeback_cache=yes", writeback_cache, 1),
SSHFS_OPT("writeback_cache=no", writeback_cache, 0),
SSHFS_OPT("-h", show_help, 1),
SSHFS_OPT("--help", show_help, 1),
SSHFS_OPT("-V", show_version, 1),
SSHFS_OPT("--version", show_version, 1),
SSHFS_OPT("-d", debug, 1),
SSHFS_OPT("debug", debug, 1),
SSHFS_OPT("-f", foreground, 1),
SSHFS_OPT("-s", singlethread, 1),
FUSE_OPT_KEY("-p ", KEY_PORT), FUSE_OPT_KEY("-p ", KEY_PORT),
FUSE_OPT_KEY("-C", KEY_COMPRESS), FUSE_OPT_KEY("-C", KEY_COMPRESS),
FUSE_OPT_KEY("-V", KEY_VERSION),
FUSE_OPT_KEY("--version", KEY_VERSION),
FUSE_OPT_KEY("-h", KEY_HELP),
FUSE_OPT_KEY("--help", KEY_HELP),
FUSE_OPT_KEY("debug", KEY_FOREGROUND),
FUSE_OPT_KEY("-d", KEY_FOREGROUND),
FUSE_OPT_KEY("-f", KEY_FOREGROUND),
FUSE_OPT_KEY("-F ", KEY_CONFIGFILE), FUSE_OPT_KEY("-F ", KEY_CONFIGFILE),
FUSE_OPT_END FUSE_OPT_END
}; };
@ -863,7 +869,7 @@ static int buf_get_entries(struct buffer *buf, void *dbuf,
S_ISLNK(stbuf.st_mode)) { S_ISLNK(stbuf.st_mode)) {
stbuf.st_mode = 0; stbuf.st_mode = 0;
} }
filler(dbuf, name, &stbuf, 0); filler(dbuf, name, &stbuf, 0, 0);
} }
} }
free(name); free(name);
@ -1715,15 +1721,29 @@ static int start_processing_thread(void)
return 0; return 0;
} }
static void *sshfs_init(struct fuse_conn_info *conn) static void *sshfs_init(struct fuse_conn_info *conn,
struct fuse_config *cfg)
{ {
/* Readahead should be done by kernel or sshfs but not both */ /* Readahead should be done by kernel or sshfs but not both */
if (conn->async_read) if (conn->capable & FUSE_CAP_ASYNC_READ)
sshfs.sync_read = 1; sshfs.sync_read = 1;
// These workarounds require the "path" argument.
cfg->nullpath_ok = ~(sshfs.truncate_workaround || sshfs.fstat_workaround);
// Lookup of . and .. is supported
conn->capable |= FUSE_CAP_EXPORT_SUPPORT;
// Enable writeback cache if supported
if (sshfs.writeback_cache && (conn->capable & FUSE_CAP_WRITEBACK_CACHE))
conn->want |= FUSE_CAP_WRITEBACK_CACHE;
if (!sshfs.delay_connect) if (!sshfs.delay_connect)
start_processing_thread(); start_processing_thread();
// SFTP only supports 1-second time resolution
conn->time_gran = 1000000000;
return NULL; return NULL;
} }
@ -1883,30 +1903,13 @@ static int sftp_request(uint8_t type, const struct buffer *buf,
return sftp_request_iov(type, &iov, 1, expect_type, outbuf); return sftp_request_iov(type, &iov, 1, expect_type, outbuf);
} }
static int sshfs_getattr(const char *path, struct stat *stbuf)
{
int err;
struct buffer buf;
struct buffer outbuf;
buf_init(&buf, 0);
buf_add_path(&buf, path);
err = sftp_request(sshfs.follow_symlinks ? SSH_FXP_STAT : SSH_FXP_LSTAT,
&buf, SSH_FXP_ATTRS, &outbuf);
if (!err) {
err = buf_get_attrs(&outbuf, stbuf, NULL);
buf_free(&outbuf);
}
buf_free(&buf);
return err;
}
static int sshfs_access(const char *path, int mask) static int sshfs_access(const char *path, int mask)
{ {
struct stat stbuf; struct stat stbuf;
int err = 0; int err = 0;
if (mask & X_OK) { if (mask & X_OK) {
err = sshfs.op->getattr(path, &stbuf); err = sshfs.op->getattr(path, &stbuf, NULL);
if (!err) { if (!err) {
if (S_ISREG(stbuf.st_mode) && if (S_ISREG(stbuf.st_mode) &&
!(stbuf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))) !(stbuf.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))
@ -2146,9 +2149,10 @@ static int sshfs_opendir(const char *path, struct fuse_file_info *fi)
} }
static int sshfs_readdir(const char *path, void *dbuf, fuse_fill_dir_t filler, static int sshfs_readdir(const char *path, void *dbuf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi) off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags)
{ {
(void) path; (void) path; (void) flags;
int err; int err;
struct buffer *handle; struct buffer *handle;
@ -2291,9 +2295,13 @@ static void random_string(char *str, int length)
*str = '\0'; *str = '\0';
} }
static int sshfs_rename(const char *from, const char *to) static int sshfs_rename(const char *from, const char *to, unsigned int flags)
{ {
int err; int err;
if(flags != 0)
return EINVAL;
if (sshfs.ext_posix_rename) if (sshfs.ext_posix_rename)
err = sshfs_ext_posix_rename(from, to); err = sshfs_ext_posix_rename(from, to);
else else
@ -2337,25 +2345,66 @@ static int sshfs_link(const char *from, const char *to)
return err; return err;
} }
static int sshfs_chmod(const char *path, mode_t mode) static inline int sshfs_file_is_conn(struct sshfs_file *sf)
{ {
int ret;
pthread_mutex_lock(&sshfs.lock);
ret = (sf->connver == sshfs.connver);
pthread_mutex_unlock(&sshfs.lock);
return ret;
}
static inline struct sshfs_file *get_sshfs_file(struct fuse_file_info *fi)
{
return (struct sshfs_file *) (uintptr_t) fi->fh;
}
static int sshfs_chmod(const char *path, mode_t mode,
struct fuse_file_info *fi)
{
(void) fi;
int err; int err;
struct buffer buf; struct buffer buf;
struct sshfs_file *sf = NULL;
if (fi != NULL) {
sf = get_sshfs_file(fi);
if (!sshfs_file_is_conn(sf))
return -EIO;
}
buf_init(&buf, 0); buf_init(&buf, 0);
buf_add_path(&buf, path); if (sf == NULL)
buf_add_path(&buf, path);
else
buf_add_buf(&buf, &sf->handle);
buf_add_uint32(&buf, SSH_FILEXFER_ATTR_PERMISSIONS); buf_add_uint32(&buf, SSH_FILEXFER_ATTR_PERMISSIONS);
buf_add_uint32(&buf, mode); buf_add_uint32(&buf, mode);
/* FIXME: really needs LSETSTAT extension (debian Bug#640038) */ /* FIXME: really needs LSETSTAT extension (debian Bug#640038) */
err = sftp_request(SSH_FXP_SETSTAT, &buf, SSH_FXP_STATUS, NULL); err = sftp_request(sf == NULL ? SSH_FXP_SETSTAT : SSH_FXP_FSETSTAT,
&buf, SSH_FXP_STATUS, NULL);
buf_free(&buf); buf_free(&buf);
return err; return err;
} }
static int sshfs_chown(const char *path, uid_t uid, gid_t gid) static int sshfs_chown(const char *path, uid_t uid, gid_t gid,
struct fuse_file_info *fi)
{ {
(void) fi;
int err; int err;
struct buffer buf; struct buffer buf;
struct sshfs_file *sf = NULL;
if (fi != NULL) {
sf = get_sshfs_file(fi);
if (!sshfs_file_is_conn(sf))
return -EIO;
}
#ifdef __APPLE__ #ifdef __APPLE__
if (sshfs.remote_uid_detected) { if (sshfs.remote_uid_detected) {
if (uid == sshfs.local_uid) if (uid == sshfs.local_uid)
@ -2375,11 +2424,16 @@ static int sshfs_chown(const char *path, uid_t uid, gid_t gid)
return -EPERM; return -EPERM;
buf_init(&buf, 0); buf_init(&buf, 0);
buf_add_path(&buf, path); if (sf == NULL)
buf_add_path(&buf, path);
else
buf_add_buf(&buf, &sf->handle);
buf_add_uint32(&buf, SSH_FILEXFER_ATTR_UIDGID); buf_add_uint32(&buf, SSH_FILEXFER_ATTR_UIDGID);
buf_add_uint32(&buf, uid); buf_add_uint32(&buf, uid);
buf_add_uint32(&buf, gid); buf_add_uint32(&buf, gid);
err = sftp_request(SSH_FXP_SETSTAT, &buf, SSH_FXP_STATUS, NULL);
err = sftp_request(sf == NULL ? SSH_FXP_SETSTAT : SSH_FXP_FSETSTAT,
&buf, SSH_FXP_STATUS, NULL);
buf_free(&buf); buf_free(&buf);
return err; return err;
} }
@ -2394,49 +2448,35 @@ static void sshfs_inc_modifver(void)
pthread_mutex_unlock(&sshfs.lock); pthread_mutex_unlock(&sshfs.lock);
} }
static int sshfs_truncate(const char *path, off_t size) static int sshfs_utimens(const char *path, const struct timespec tv[2],
struct fuse_file_info *fi)
{ {
(void) fi;
int err; int err;
struct buffer buf; struct buffer buf;
struct sshfs_file *sf = NULL;
sshfs_inc_modifver(); if (fi != NULL) {
if (size == 0 || sshfs.truncate_workaround) sf = get_sshfs_file(fi);
return sshfs_truncate_workaround(path, size, NULL); if (!sshfs_file_is_conn(sf))
return -EIO;
}
buf_init(&buf, 0); buf_init(&buf, 0);
buf_add_path(&buf, path); if (sf == NULL)
buf_add_uint32(&buf, SSH_FILEXFER_ATTR_SIZE); buf_add_path(&buf, path);
buf_add_uint64(&buf, size); else
err = sftp_request(SSH_FXP_SETSTAT, &buf, SSH_FXP_STATUS, NULL); buf_add_buf(&buf, &sf->handle);
buf_free(&buf);
return err;
}
static int sshfs_utime(const char *path, struct utimbuf *ubuf)
{
int err;
struct buffer buf;
buf_init(&buf, 0);
buf_add_path(&buf, path);
buf_add_uint32(&buf, SSH_FILEXFER_ATTR_ACMODTIME); buf_add_uint32(&buf, SSH_FILEXFER_ATTR_ACMODTIME);
buf_add_uint32(&buf, ubuf->actime); buf_add_uint32(&buf, tv[0].tv_sec);
buf_add_uint32(&buf, ubuf->modtime); buf_add_uint32(&buf, tv[1].tv_sec);
err = sftp_request(SSH_FXP_SETSTAT, &buf, SSH_FXP_STATUS, NULL);
err = sftp_request(sf == NULL ? SSH_FXP_SETSTAT : SSH_FXP_FSETSTAT,
&buf, SSH_FXP_STATUS, NULL);
buf_free(&buf); buf_free(&buf);
return err; return err;
} }
static inline int sshfs_file_is_conn(struct sshfs_file *sf)
{
int ret;
pthread_mutex_lock(&sshfs.lock);
ret = (sf->connver == sshfs.connver);
pthread_mutex_unlock(&sshfs.lock);
return ret;
}
static int sshfs_open_common(const char *path, mode_t mode, static int sshfs_open_common(const char *path, mode_t mode,
struct fuse_file_info *fi) struct fuse_file_info *fi)
{ {
@ -2450,9 +2490,9 @@ static int sshfs_open_common(const char *path, mode_t mode,
uint32_t pflags = 0; uint32_t pflags = 0;
struct iovec iov; struct iovec iov;
uint8_t type; uint8_t type;
uint64_t wrctr = 0; uint64_t wrctr;
if (sshfs.cache) if (sshfs.dir_cache)
wrctr = cache_get_write_ctr(); wrctr = cache_get_write_ctr();
if ((fi->flags & O_ACCMODE) == O_RDONLY) if ((fi->flags & O_ACCMODE) == O_RDONLY)
@ -2510,12 +2550,12 @@ static int sshfs_open_common(const char *path, mode_t mode,
} }
if (!err) { if (!err) {
if (sshfs.cache) if (sshfs.dir_cache)
cache_add_attr(path, &stbuf, wrctr); cache_add_attr(path, &stbuf, wrctr);
buf_finish(&sf->handle); buf_finish(&sf->handle);
fi->fh = (unsigned long) sf; fi->fh = (unsigned long) sf;
} else { } else {
if (sshfs.cache) if (sshfs.dir_cache)
cache_invalidate(path); cache_invalidate(path);
g_free(sf); g_free(sf);
} }
@ -2528,11 +2568,6 @@ static int sshfs_open(const char *path, struct fuse_file_info *fi)
return sshfs_open_common(path, 0, fi); return sshfs_open_common(path, 0, fi);
} }
static inline struct sshfs_file *get_sshfs_file(struct fuse_file_info *fi)
{
return (struct sshfs_file *) (uintptr_t) fi->fh;
}
static int sshfs_flush(const char *path, struct fuse_file_info *fi) static int sshfs_flush(const char *path, struct fuse_file_info *fi)
{ {
int err; int err;
@ -2575,16 +2610,14 @@ static int sshfs_fsync(const char *path, int isdatasync,
if (!sshfs.ext_fsync) if (!sshfs.ext_fsync)
return err; return err;
{ struct buffer buf;
struct buffer buf; struct sshfs_file *sf = get_sshfs_file(fi);
struct sshfs_file *sf = get_sshfs_file(fi); buf_init(&buf, 0);
buf_init(&buf, 0); buf_add_string(&buf, SFTP_EXT_FSYNC);
buf_add_string(&buf, SFTP_EXT_FSYNC); buf_add_buf(&buf, &sf->handle);
buf_add_buf(&buf, &sf->handle); err = sftp_request(SSH_FXP_EXTENDED, &buf, SSH_FXP_STATUS, NULL);
err = sftp_request(SSH_FXP_EXTENDED, &buf, SSH_FXP_STATUS, NULL); buf_free(&buf);
buf_free(&buf); return err;
return err;
}
} }
static void sshfs_file_put(struct sshfs_file *sf) static void sshfs_file_put(struct sshfs_file *sf)
@ -3052,51 +3085,64 @@ static int sshfs_create(const char *path, mode_t mode,
return sshfs_open_common(path, mode, fi); return sshfs_open_common(path, mode, fi);
} }
static int sshfs_ftruncate(const char *path, off_t size, static int sshfs_truncate(const char *path, off_t size,
struct fuse_file_info *fi) struct fuse_file_info *fi)
{ {
int err; int err;
struct buffer buf; struct buffer buf;
struct sshfs_file *sf = get_sshfs_file(fi); struct sshfs_file *sf = NULL;
(void) path;
if (!sshfs_file_is_conn(sf))
return -EIO;
if (fi != NULL) {
sf = get_sshfs_file(fi);
if (!sshfs_file_is_conn(sf))
return -EIO;
}
sshfs_inc_modifver(); sshfs_inc_modifver();
if (sshfs.truncate_workaround) if (sshfs.truncate_workaround)
return sshfs_truncate_workaround(path, size, fi); return sshfs_truncate_workaround(path, size, fi);
buf_init(&buf, 0); buf_init(&buf, 0);
buf_add_buf(&buf, &sf->handle);
if (sf != NULL)
buf_add_buf(&buf, &sf->handle);
else
buf_add_path(&buf, path);
buf_add_uint32(&buf, SSH_FILEXFER_ATTR_SIZE); buf_add_uint32(&buf, SSH_FILEXFER_ATTR_SIZE);
buf_add_uint64(&buf, size); buf_add_uint64(&buf, size);
err = sftp_request(SSH_FXP_FSETSTAT, &buf, SSH_FXP_STATUS, NULL); err = sftp_request(sf == NULL ? SSH_FXP_SETSTAT : SSH_FXP_FSETSTAT,
&buf, SSH_FXP_STATUS, NULL);
buf_free(&buf); buf_free(&buf);
return err; return err;
} }
static int sshfs_fgetattr(const char *path, struct stat *stbuf, static int sshfs_getattr(const char *path, struct stat *stbuf,
struct fuse_file_info *fi) struct fuse_file_info *fi)
{ {
int err; int err;
struct buffer buf; struct buffer buf;
struct buffer outbuf; struct buffer outbuf;
struct sshfs_file *sf = get_sshfs_file(fi); struct sshfs_file *sf = NULL;
(void) path; if (fi != NULL || sshfs.fstat_workaround) {
sf = get_sshfs_file(fi);
if (!sshfs_file_is_conn(sf)) if (!sshfs_file_is_conn(sf))
return -EIO; return -EIO;
}
if (sshfs.fstat_workaround)
return sshfs_getattr(path, stbuf);
buf_init(&buf, 0); buf_init(&buf, 0);
buf_add_buf(&buf, &sf->handle); if(sf == NULL) {
err = sftp_request(SSH_FXP_FSTAT, &buf, SSH_FXP_ATTRS, &outbuf); buf_add_path(&buf, path);
err = sftp_request(sshfs.follow_symlinks ? SSH_FXP_STAT : SSH_FXP_LSTAT,
&buf, SSH_FXP_ATTRS, &outbuf);
}
else {
buf_add_buf(&buf, &sf->handle);
err = sftp_request(SSH_FXP_FSTAT, &buf, SSH_FXP_ATTRS, &outbuf);
}
if (!err) { if (!err) {
err = buf_get_attrs(&outbuf, stbuf, NULL); err = buf_get_attrs(&outbuf, stbuf, NULL);
buf_free(&outbuf); buf_free(&outbuf);
@ -3212,10 +3258,7 @@ static int sshfs_truncate_workaround(const char *path, off_t size,
else { else {
struct stat stbuf; struct stat stbuf;
int err; int err;
if (fi) err = sshfs_getattr(path, &stbuf, fi);
err = sshfs_fgetattr(path, &stbuf, fi);
else
err = sshfs_getattr(path, &stbuf);
if (err) if (err)
return err; return err;
if (stbuf.st_size == size) if (stbuf.st_size == size)
@ -3260,7 +3303,7 @@ static struct fuse_operations sshfs_oper = {
.chmod = sshfs_chmod, .chmod = sshfs_chmod,
.chown = sshfs_chown, .chown = sshfs_chown,
.truncate = sshfs_truncate, .truncate = sshfs_truncate,
.utime = sshfs_utime, .utimens = sshfs_utimens,
.open = sshfs_open, .open = sshfs_open,
.flush = sshfs_flush, .flush = sshfs_flush,
.fsync = sshfs_fsync, .fsync = sshfs_fsync,
@ -3269,10 +3312,6 @@ static struct fuse_operations sshfs_oper = {
.write = sshfs_write, .write = sshfs_write,
.statfs = sshfs_statfs, .statfs = sshfs_statfs,
.create = sshfs_create, .create = sshfs_create,
.ftruncate = sshfs_ftruncate,
.fgetattr = sshfs_fgetattr,
.flag_nullpath_ok = 1,
.flag_nopath = 1,
}; };
static void usage(const char *progname) static void usage(const char *progname)
@ -3280,30 +3319,32 @@ static void usage(const char *progname)
printf( printf(
"usage: %s [user@]host:[dir] mountpoint [options]\n" "usage: %s [user@]host:[dir] mountpoint [options]\n"
"\n" "\n"
"general options:\n"
" -o opt,[opt...] mount options\n"
" -h --help print help\n" " -h --help print help\n"
" -V --version print version\n" " -V --version print version\n"
"\n" " -f foreground operation\n"
"SSHFS options:\n" " -s disable multi-threaded operation\n"
" -p PORT equivalent to '-o port=PORT'\n" " -p PORT equivalent to '-o port=PORT'\n"
" -C equivalent to '-o compression=yes'\n" " -C equivalent to '-o compression=yes'\n"
" -F ssh_configfile specifies alternative ssh configuration file\n" " -F ssh_configfile specifies alternative ssh configuration file\n"
" -1 equivalent to '-o ssh_protocol=1'\n" " -1 equivalent to '-o ssh_protocol=1'\n"
" -o opt,[opt...] mount options\n"
" -o reconnect reconnect to server\n" " -o reconnect reconnect to server\n"
" -o delay_connect delay connection to server\n" " -o delay_connect delay connection to server\n"
" -o sshfs_sync synchronous writes\n" " -o sshfs_sync synchronous writes\n"
" -o no_readahead synchronous reads (no speculative readahead)\n" " -o no_readahead synchronous reads (no speculative readahead)\n"
" -o sync_readdir synchronous readdir\n" " -o sync_readdir synchronous readdir\n"
" -o sshfs_debug print some debugging information\n" " -d, --debug print some debugging information (implies -f)\n"
" -o cache=BOOL enable caching {yes,no} (default: yes)\n" " -o writeback_cache=BOOL enable writeback cache {yes,no} (default: yes)\n"
" -o cache_max_size=N sets the maximum size of the cache (default: 10000)\n" " -o dir_cache=BOOL enable caching of directory contents (names,\n"
" -o cache_timeout=N sets timeout for caches in seconds (default: 20)\n" " attributes, symlink targets) {yes,no} (default: yes)\n"
" -o cache_X_timeout=N sets timeout for {stat,dir,link} cache\n" " -o dcache_max_size=N sets the maximum size of the directory cache (default: 10000)\n"
" -o cache_clean_interval=N\n" " -o dcache_timeout=N sets timeout for directory cache in seconds (default: 20)\n"
" -o dcache_{stat,link,dir}_timeout=N\n"
" sets separate timeout for {attributes, symlinks, names}\n"
" -o dcache_clean_interval=N\n"
" sets the interval for automatic cleaning of the\n" " sets the interval for automatic cleaning of the\n"
" cache (default: 60)\n" " cache (default: 60)\n"
" -o cache_min_clean_interval=N\n" " -o dcache_min_clean_interval=N\n"
" sets the interval for forced cleaning of the\n" " sets the interval for forced cleaning of the\n"
" cache if full (default: 5)\n" " cache if full (default: 5)\n"
" -o workaround=LIST colon separated list of workarounds\n" " -o workaround=LIST colon separated list of workarounds\n"
@ -3311,7 +3352,7 @@ static void usage(const char *progname)
" [no]rename fix renaming to existing file (default: off)\n" " [no]rename fix renaming to existing file (default: off)\n"
" [no]truncate fix truncate for old servers (default: off)\n" " [no]truncate fix truncate for old servers (default: off)\n"
" [no]buflimit fix buffer fillup bug in server (default: on)\n" " [no]buflimit fix buffer fillup bug in server (default: on)\n"
" [no]fstat fix fstat for old servers (default: off)\n" " [no]fstat always use stat() instead of fstat() (default: off)\n"
" -o idmap=TYPE user/group ID mapping (default: " IDMAP_DEFAULT ")\n" " -o idmap=TYPE user/group ID mapping (default: " IDMAP_DEFAULT ")\n"
" none no translation of the ID space\n" " none no translation of the ID space\n"
" user only translate UID/GID of connecting user\n" " user only translate UID/GID of connecting user\n"
@ -3332,7 +3373,9 @@ static void usage(const char *progname)
" -o no_check_root don't check for existence of 'dir' on server\n" " -o no_check_root don't check for existence of 'dir' on server\n"
" -o password_stdin read password from stdin (only for pam_mount!)\n" " -o password_stdin read password from stdin (only for pam_mount!)\n"
" -o SSHOPT=VAL ssh options (see man ssh_config)\n" " -o SSHOPT=VAL ssh options (see man ssh_config)\n"
"\n", progname); "\n"
"FUSE Options:\n",
progname);
} }
static int is_ssh_opt(const char *arg) static int is_ssh_opt(const char *arg)
@ -3350,17 +3393,11 @@ static int is_ssh_opt(const char *arg)
return 0; return 0;
} }
static int sshfs_fuse_main(struct fuse_args *args)
{
sshfs.op = cache_init(&sshfs_oper);
return fuse_main(args->argc, args->argv, sshfs.op, NULL);
}
static int sshfs_opt_proc(void *data, const char *arg, int key, static int sshfs_opt_proc(void *data, const char *arg, int key,
struct fuse_args *outargs) struct fuse_args *outargs)
{ {
(void) outargs; (void) data;
char *tmp; char *tmp;
(void) data;
switch (key) { switch (key) {
case FUSE_OPT_KEY_OPT: case FUSE_OPT_KEY_OPT:
@ -3370,6 +3407,7 @@ static int sshfs_opt_proc(void *data, const char *arg, int key,
g_free(tmp); g_free(tmp);
return 0; return 0;
} }
/* Pass through */
return 1; return 1;
case FUSE_OPT_KEY_NONOPT: case FUSE_OPT_KEY_NONOPT:
@ -3377,7 +3415,18 @@ static int sshfs_opt_proc(void *data, const char *arg, int key,
sshfs.host = strdup(arg); sshfs.host = strdup(arg);
return 0; return 0;
} }
return 1; else if (!sshfs.mountpoint) {
sshfs.mountpoint = realpath(arg, NULL);
if (!sshfs.mountpoint) {
fprintf(stderr, "sshfs: bad mount point `%s': %s\n",
arg, strerror(errno));
return -1;
}
return 0;
}
fprintf(stderr, "sshfs: invalid argument `%s'\n", arg);
return -1;
case KEY_PORT: case KEY_PORT:
tmp = g_strdup_printf("-oPort=%s", arg + 2); tmp = g_strdup_printf("-oPort=%s", arg + 2);
@ -3395,22 +3444,6 @@ static int sshfs_opt_proc(void *data, const char *arg, int key,
g_free(tmp); g_free(tmp);
return 0; return 0;
case KEY_HELP:
usage(outargs->argv[0]);
fuse_opt_add_arg(outargs, "-ho");
sshfs_fuse_main(outargs);
exit(1);
case KEY_VERSION:
printf("SSHFS version %s\n", PACKAGE_VERSION);
fuse_opt_add_arg(outargs, "--version");
sshfs_fuse_main(outargs);
exit(0);
case KEY_FOREGROUND:
sshfs.foreground = 1;
return 1;
default: default:
fprintf(stderr, "internal error\n"); fprintf(stderr, "internal error\n");
abort(); abort();
@ -3448,15 +3481,6 @@ static int parse_workarounds(void)
return res; return res;
} }
static void check_large_read(struct fuse_args *args)
{
struct utsname buf;
int err = uname(&buf);
if (!err && strcmp(buf.sysname, "Linux") == 0 &&
strncmp(buf.release, "2.4.", 4) == 0)
fuse_opt_insert_arg(args, 1, "-olarge_read");
}
static int read_password(void) static int read_password(void)
{ {
int size = getpagesize(); int size = getpagesize();
@ -3794,6 +3818,11 @@ int main(int argc, char *argv[])
char *tmp; char *tmp;
char *fsname; char *fsname;
const char *sftp_server; const char *sftp_server;
struct fuse *fuse;
struct fuse_session *se;
#if !defined(__CYGWIN__)
struct stat st;
#endif
#ifdef __APPLE__ #ifdef __APPLE__
if (!realpath(*exec_path, sshfs_program_path)) { if (!realpath(*exec_path, sshfs_program_path)) {
@ -3817,7 +3846,12 @@ int main(int argc, char *argv[])
sshfs.rfd = -1; sshfs.rfd = -1;
sshfs.wfd = -1; sshfs.wfd = -1;
sshfs.ptyfd = -1; sshfs.ptyfd = -1;
sshfs.cache = 1; sshfs.dir_cache = 1;
sshfs.writeback_cache = 1;
sshfs.show_help = 0;
sshfs.show_version = 0;
sshfs.singlethread = 0;
sshfs.foreground = 0;
sshfs.ptyslavefd = -1; sshfs.ptyslavefd = -1;
sshfs.delay_connect = 0; sshfs.delay_connect = 0;
sshfs.slave = 0; sshfs.slave = 0;
@ -3841,12 +3875,31 @@ int main(int argc, char *argv[])
parse_workarounds() == -1) parse_workarounds() == -1)
exit(1); exit(1);
// These workarounds require the "path" argument. if (sshfs.show_version) {
if (sshfs.truncate_workaround || sshfs.fstat_workaround) { printf("SSHFS version %s\n", PACKAGE_VERSION);
sshfs_oper.flag_nullpath_ok = 0; printf("FUSE library version %s\n", fuse_pkgversion());
sshfs_oper.flag_nopath = 0; fuse_lowlevel_version();
exit(0);
} }
if (sshfs.show_help) {
usage(args.argv[0]);
/* Re-add --help */
if (fuse_opt_add_arg(&args, "--help") == -1)
exit(1);
/* Print FUSE help text */
assert(fuse_new(&args, NULL, 0, NULL) == NULL);
exit(0);
} else if (!sshfs.host) {
fprintf(stderr, "missing host\n");
fprintf(stderr, "see `%s -h' for usage\n", argv[0]);
exit(1);
} else if (!sshfs.mountpoint) {
fprintf(stderr, "error: no mountpoint specified\n");
fprintf(stderr, "see `%s -h' for usage\n", argv[0]);
exit(1);
}
if (sshfs.idmap == IDMAP_USER) if (sshfs.idmap == IDMAP_USER)
sshfs.detect_uid = 1; sshfs.detect_uid = 1;
else if (sshfs.idmap == IDMAP_FILE) { else if (sshfs.idmap == IDMAP_FILE) {
@ -3868,10 +3921,10 @@ int main(int argc, char *argv[])
DEBUG("SSHFS version %s\n", PACKAGE_VERSION); DEBUG("SSHFS version %s\n", PACKAGE_VERSION);
if (sshfs.slave) { /* Force sshfs to the foreground when using stdin+stdout */
/* Force sshfs to the foreground when using stdin+stdout */ if (sshfs.slave)
sshfs.foreground = 1; sshfs.foreground = 1;
}
if (sshfs.slave && sshfs.password_stdin) { if (sshfs.slave && sshfs.password_stdin) {
fprintf(stderr, "the password_stdin and slave options cannot both be specified\n"); fprintf(stderr, "the password_stdin and slave options cannot both be specified\n");
@ -3884,6 +3937,9 @@ int main(int argc, char *argv[])
exit(1); exit(1);
} }
if (sshfs.debug)
sshfs.foreground = 1;
if (sshfs.buflimit_workaround) if (sshfs.buflimit_workaround)
/* Work around buggy sftp-server in OpenSSH. Without this on /* Work around buggy sftp-server in OpenSSH. Without this on
a slow server a 10Mbyte buffer would fill up and the server a slow server a 10Mbyte buffer would fill up and the server
@ -3892,12 +3948,6 @@ int main(int argc, char *argv[])
else else
sshfs.max_outstanding_len = ~0; sshfs.max_outstanding_len = ~0;
if (!sshfs.host) {
fprintf(stderr, "missing host\n");
fprintf(stderr, "see `%s -h' for usage\n", argv[0]);
exit(1);
}
fsname = g_strdup(sshfs.host); fsname = g_strdup(sshfs.host);
sshfs.base_path = g_strdup(find_base_path()); sshfs.base_path = g_strdup(find_base_path());
@ -3921,7 +3971,6 @@ int main(int argc, char *argv[])
ssh_add_arg(sftp_server); ssh_add_arg(sftp_server);
free(sshfs.sftp_server); free(sshfs.sftp_server);
res = cache_parse_options(&args); res = cache_parse_options(&args);
if (res == -1) if (res == -1)
exit(1); exit(1);
@ -3933,107 +3982,82 @@ int main(int argc, char *argv[])
if (sshfs.max_write > 65536) if (sshfs.max_write > 65536)
sshfs.max_write = 65536; sshfs.max_write = 65536;
if (fuse_is_lib_option("ac_attr_timeout="))
fuse_opt_insert_arg(&args, 1, "-oauto_cache,ac_attr_timeout=0");
fsname = fsname_escape_commas(fsname); fsname = fsname_escape_commas(fsname);
tmp = g_strdup_printf("-osubtype=sshfs,fsname=%s", fsname); tmp = g_strdup_printf("-osubtype=sshfs,fsname=%s", fsname);
fuse_opt_insert_arg(&args, 1, tmp); fuse_opt_insert_arg(&args, 1, tmp);
g_free(tmp); g_free(tmp);
g_free(fsname); g_free(fsname);
check_large_read(&args);
{
struct fuse *fuse;
struct fuse_chan *ch;
char *mountpoint;
int multithreaded;
int foreground;
#if !defined(__CYGWIN__)
struct stat st;
#endif
res = fuse_parse_cmdline(&args, &mountpoint, &multithreaded,
&foreground);
if (res == -1)
exit(1);
if (sshfs.slave) {
/* Force sshfs to the foreground when using stdin+stdout */
foreground = 1;
}
#if !defined(__CYGWIN__) #if !defined(__CYGWIN__)
res = stat(mountpoint, &st); res = stat(sshfs.mountpoint, &st);
if (res == -1) { if (res == -1) {
perror(mountpoint); perror(sshfs.mountpoint);
exit(1); exit(1);
}
sshfs.mnt_mode = st.st_mode;
#elif defined(__CYGWIN__)
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
if(sshfs.cache)
sshfs.op = cache_init(&sshfs_oper);
else
sshfs.op = &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);
fuse_destroy(fuse);
exit(1);
}
res = fuse_daemonize(foreground);
if (res == -1) {
fuse_unmount(mountpoint, ch);
fuse_destroy(fuse);
exit(1);
}
if (multithreaded)
res = fuse_loop_mt(fuse);
else
res = fuse_loop(fuse);
if (res == -1)
res = 1;
else
res = 0;
fuse_remove_signal_handlers(fuse_get_session(fuse));
fuse_unmount(mountpoint, ch);
fuse_destroy(fuse);
free(mountpoint);
} }
sshfs.mnt_mode = st.st_mode;
#elif defined(__CYGWIN__)
sshfs.mnt_mode = S_IFDIR | 0755;
#endif
if(sshfs.dir_cache)
sshfs.op = cache_wrap(&sshfs_oper);
else
sshfs.op = &sshfs_oper;
fuse = fuse_new(&args, sshfs.op,
sizeof(struct fuse_operations), NULL);
if(fuse == NULL)
exit(1);
se = fuse_get_session(fuse);
res = fuse_set_signal_handlers(se);
if (res != 0) {
fuse_destroy(fuse);
exit(1);
}
res = fuse_mount(fuse, sshfs.mountpoint);
if (res != 0) {
fuse_destroy(fuse);
exit(1);
}
#if !defined(__CYGWIN__)
res = fcntl(fuse_session_fd(se), F_SETFD, FD_CLOEXEC);
if (res == -1)
perror("WARNING: failed to set FD_CLOEXEC on fuse device");
#endif
/*
* FIXME: trim $PATH so it doesn't contain anything inside the
* mountpoint, which would deadlock.
*/
res = ssh_connect();
if (res == -1) {
fuse_unmount(fuse);
fuse_destroy(fuse);
exit(1);
}
res = fuse_daemonize(sshfs.foreground);
if (res == -1) {
fuse_unmount(fuse);
fuse_destroy(fuse);
exit(1);
}
if (sshfs.singlethread)
res = fuse_loop(fuse);
else
res = fuse_loop_mt(fuse, 0);
if (res != 0)
res = 1;
else
res = 0;
fuse_remove_signal_handlers(se);
fuse_unmount(fuse);
fuse_destroy(fuse);
if (sshfs.debug) { if (sshfs.debug) {
unsigned int avg_rtt = 0; unsigned int avg_rtt = 0;

View File

@ -30,7 +30,7 @@ def name_generator(__ctr=[0]):
return 'testfile_%d' % __ctr[0] return 'testfile_%d' % __ctr[0]
@pytest.mark.parametrize("debug", (False, True)) @pytest.mark.parametrize("debug", (False, True))
@pytest.mark.parametrize("cache_timeout", (0, 1)) @pytest.mark.parametrize("cache_timeout", (0,1))
@pytest.mark.parametrize("sync_rd", (True, False)) @pytest.mark.parametrize("sync_rd", (True, False))
def test_sshfs(tmpdir, debug, cache_timeout, sync_rd, capfd): def test_sshfs(tmpdir, debug, cache_timeout, sync_rd, capfd):
@ -64,10 +64,10 @@ def test_sshfs(tmpdir, debug, cache_timeout, sync_rd, capfd):
# SSHFS Cache # SSHFS Cache
if cache_timeout == 0: if cache_timeout == 0:
cmdline += [ '-o', 'cache=no' ] cmdline += [ '-o', 'dir_cache=no' ]
else: else:
cmdline += [ '-o', 'cache_timeout=%d' % cache_timeout, cmdline += [ '-o', 'dcache_timeout=%d' % cache_timeout,
'-o', 'cache=yes' ] '-o', 'dir_cache=yes' ]
# FUSE Cache # FUSE Cache
cmdline += [ '-o', 'entry_timeout=0', cmdline += [ '-o', 'entry_timeout=0',

View File

@ -13,9 +13,20 @@ valgrind --version
ninja --version ninja --version
meson --version meson --version
# Install fuse
wget https://github.com/libfuse/libfuse/releases/download/fuse-3.0.2/fuse-3.0.2.tar.gz
tar xzf fuse-3.0.2.tar.gz
cd fuse-3.0.2
mkdir build
cd build
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/
# Setup ssh # Setup ssh
ssh-keygen -b 768 -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 cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys chmod 600 ~/.ssh/authorized_keys
ssh -o "StrictHostKeyChecking=no" localhost echo "SSH connection succeeded" ssh -o "StrictHostKeyChecking=no" localhost echo "SSH connection succeeded"