Compare commits

...

16 Commits

Author SHA1 Message Date
Miklos Szeredi 3cfb3a6dab new release 2006-02-20 12:11:23 +00:00
Miklos Szeredi 790bb8dd36 fix 2006-02-20 11:44:36 +00:00
Miklos Szeredi accc218cdb revert making rename workaround on by default 2006-02-20 11:32:45 +00:00
Miklos Szeredi 8468900705 fix 2006-02-16 17:02:25 +00:00
Miklos Szeredi b60a970602 fix 2006-02-16 11:51:27 +00:00
Miklos Szeredi 27c1b7b09d fix 2006-02-02 12:07:14 +00:00
Miklos Szeredi 6f0233e11e fix 2006-02-01 15:50:44 +00:00
Miklos Szeredi 89da93d07e fix 2006-01-31 18:34:38 +00:00
Miklos Szeredi cb4239036a fix 2006-01-30 11:05:40 +00:00
Miklos Szeredi 40393a4b0b fix 2006-01-30 09:47:02 +00:00
Miklos Szeredi a6e0d2ddd9 fix 2006-01-27 16:30:55 +00:00
Miklos Szeredi 1cb9aba6ea fix 2006-01-27 12:49:14 +00:00
Miklos Szeredi 3883b4eb0c fix 2006-01-25 14:47:07 +00:00
Miklos Szeredi f1f738d5b8 fix 2006-01-23 17:03:25 +00:00
Miklos Szeredi d2a3ed327e fix 2006-01-20 16:29:23 +00:00
Miklos Szeredi cd6a924cdc fix 2006-01-18 14:05:48 +00:00
10 changed files with 578 additions and 156 deletions
+64
View File
@@ -1,3 +1,67 @@
2006-02-20 Miklos Szeredi <miklos@szeredi.hu>
* Released 1.5
2006-02-19 Miklos Szeredi <miklos@szeredi.hu>
* Fix compilation with old FUSE versions. Report by Kurt George
Gjerde
2006-02-16 Miklos Szeredi <miklos@szeredi.hu>
* Block TERM/INT/HUP/QUIT signals in sshfs reading thread, so they
will always be received by the FUSE main thread. Fixes the
"double ^C" problem seen on FreeBSD.
* Use 'auto_cache' option if available, so that file cache is not
thrown away on each open. This should make repeated reading of a
file much much faster.
2006-01-31 Miklos Szeredi <miklos@szeredi.hu>
* Fix problems with nodelay workaround on FreeBSD.
2006-01-30 Miklos Szeredi <miklos@szeredi.hu>
* Fix data consitency bug if readahead is enabled and writes are
intermixed with reads. Solution is far from optimal, since it
will prevent readahead in the above situation. If used with FUSE
>= 2.6.0 with Linux-2.6.X, readahead will be done by the kernel,
and hence there will be no performance penalty. Bug reported and
test program written by Wolfgang Köbler. Further testing on
FreeBSD by Csaba Henk
2006-01-29 Miklos Szeredi <miklos@szeredi.hu>
* Add '-olarge_read' option for Linux-2.4.*. This should
generally improve download performance
2006-01-27 Miklos Szeredi <miklos@szeredi.hu>
* Add workaround (enabled by default) for ssh clients not setting
TCP_NODELAY on the network connection. Currently this is all
known versions of openssh. This may improve download speed in
some circumstances
* Make it possible to idividually disable workarounds with a "no"
prefix
* Change '-otransform_symlinks' not to prefix with the mountpoint
(which is fragile wrt. moving/binding the mount). Rather convert
absolute symlinks to relative if possible. Does not yet work if
base path is itself relative
2006-01-25 Miklos Szeredi <miklos@szeredi.hu>
* Use TCP_NODELAY socket option for direct connection. This may
improve download speed in some circumstances
2006-01-20 Miklos Szeredi <miklos@szeredi.hu>
* Update to fuse API 2.6
* Don't do readahead in sshfs if it's done in kernel
2006-01-14 Miklos Szeredi <miklos@szeredi.hu>
* Released 1.4
+20 -3
View File
@@ -2,9 +2,26 @@
bin_PROGRAMS = sshfs
sshfs_SOURCES = sshfs.c cache.c cache.h
if FUSE_OPT_COMPAT
compat_sources = compat/fuse_opt.c compat/fuse_opt.h
sshfs_SOURCES += compat/fuse_opt.c compat/fuse_opt.h
endif
sshfs_SOURCES = sshfs.c cache.c cache.h $(compat_sources)
sshfs_CPPFLAGS = -DFUSE_USE_VERSION=25
sshfs_LDADD = $(SSHFS_LIBS)
sshfs_CFLAGS = $(SSHFS_CFLAGS)
sshfs_CPPFLAGS = -D_REENTRANT -DFUSE_USE_VERSION=26 -DLIBDIR=\"$(libdir)\"
EXTRA_DIST = sshnodelay.c
CLEANFILES = sshnodelay.so
all-local: sshnodelay.so
install-exec-local: sshnodelay.so
test -z "$(libdir)" || $(mkdir_p) "$(DESTDIR)$(libdir)"
$(INSTALL) -m 755 sshnodelay.so "$(DESTDIR)$(libdir)/sshnodelay.so"
uninstall-local:
rm -f "$(DESTDIR)$(libdir)/sshnodelay.so"
sshnodelay.so:
$(CC) -Wall -W -s --shared $(sshnodelay_libs) sshnodelay.c -o sshnodelay.so
+12
View File
@@ -1,3 +1,15 @@
What is new in 1.5
------------------
* Improvements to read performance. Now both read and write
throughput should be very close to 'scp'
* If used with FUSE 2.6.0 or later, then perform better data caching.
This should show dramatic speed improvements when a file is opened
more than once
* Bug fixes
What is new in 1.4
------------------
+2 -2
View File
@@ -101,7 +101,7 @@ static void cache_purge_parent(const char *path)
}
}
static void cache_invalidate(const char *path)
void cache_invalidate(const char *path)
{
pthread_mutex_lock(&cache.lock);
cache_purge(path);
@@ -137,7 +137,7 @@ static struct node *cache_get(const char *path)
return node;
}
static void cache_add_attr(const char *path, const struct stat *stbuf)
void cache_add_attr(const char *path, const struct stat *stbuf)
{
struct node *node;
time_t now;
+2
View File
@@ -24,3 +24,5 @@ struct fuse_cache_operations {
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);
void cache_invalidate(const char *path);
+30 -25
View File
@@ -62,6 +62,21 @@ int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
return 0;
}
int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg)
{
assert(pos <= args->argc);
if (fuse_opt_add_arg(args, arg) == -1)
return -1;
if (pos != args->argc - 1) {
char *newarg = args->argv[args->argc - 1];
memmove(&args->argv[pos + 1], &args->argv[pos],
sizeof(char *) * (args->argc - pos - 1));
args->argv[pos] = newarg;
}
return 0;
}
static int next_arg(struct fuse_opt_context *ctx, const char *opt)
{
if (ctx->argctr + 1 >= ctx->argc) {
@@ -102,25 +117,13 @@ static int add_opt(struct fuse_opt_context *ctx, const char *opt)
return fuse_opt_add_opt(&ctx->opts, opt);
}
static int insert_arg(struct fuse_opt_context *ctx, int pos, const char *arg)
{
assert(pos <= ctx->outargs.argc);
if (add_arg(ctx, arg) == -1)
return -1;
if (pos != ctx->outargs.argc - 1) {
char *newarg = ctx->outargs.argv[ctx->outargs.argc - 1];
memmove(&ctx->outargs.argv[pos + 1], &ctx->outargs.argv[pos],
sizeof(char *) * (ctx->outargs.argc - pos - 1));
ctx->outargs.argv[pos] = newarg;
}
return 0;
}
static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key,
int iso)
{
if (ctx->proc) {
if (key == FUSE_OPT_KEY_DISCARD)
return 0;
if (key != FUSE_OPT_KEY_KEEP && ctx->proc) {
int res = ctx->proc(ctx->data, arg, key, &ctx->outargs);
if (res == -1 || !res)
return res;
@@ -155,8 +158,8 @@ static int match_template(const char *t, const char *arg, unsigned *sepp)
static const struct fuse_opt *find_opt(const struct fuse_opt *opt,
const char *arg, unsigned *sepp)
{
for (; opt && opt->template; opt++)
if (match_template(opt->template, arg, sepp))
for (; opt && opt->templ; opt++)
if (match_template(opt->templ, arg, sepp))
return opt;
return NULL;
}
@@ -195,11 +198,11 @@ static int process_opt(struct fuse_opt_context *ctx,
return -1;
} else {
void *var = ctx->data + opt->offset;
if (sep && opt->template[sep + 1]) {
if (sep && opt->templ[sep + 1]) {
const char *param = arg + sep;
if (opt->template[sep] == '=')
if (opt->templ[sep] == '=')
param ++;
if (process_opt_param(var, opt->template + sep + 1,
if (process_opt_param(var, opt->templ + sep + 1,
param, arg) == -1)
return -1;
} else
@@ -239,7 +242,7 @@ static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso)
if (opt) {
for (; opt; opt = find_opt(opt + 1, arg, &sep)) {
int res;
if (sep && opt->template[sep] == ' ' && !arg[sep])
if (sep && opt->templ[sep] == ' ' && !arg[sep])
res = process_opt_sep_arg(ctx, opt, sep, arg, iso);
else
res = process_opt(ctx, opt, sep, arg, iso);
@@ -321,12 +324,14 @@ static int opt_parse(struct fuse_opt_context *ctx)
return -1;
if (ctx->opts) {
if (insert_arg(ctx, 1, "-o") == -1 ||
insert_arg(ctx, 2, ctx->opts) == -1)
if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 ||
fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1)
return -1;
}
if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc)
if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc) {
free(ctx->outargs.argv[ctx->outargs.argc - 1]);
ctx->outargs.argv[--ctx->outargs.argc] = NULL;
}
return 0;
}
+36 -5
View File
@@ -72,7 +72,7 @@ extern "C" {
*/
struct fuse_opt {
/** Matching template and optional parameter formatting */
const char *template;
const char *templ;
/**
* Offset of variable within 'data' parameter of fuse_opt_parse()
@@ -82,7 +82,7 @@ struct fuse_opt {
/**
* Value to set the variable to, or to be passed as 'key' to the
* processing function. Ignored if template a format
* processing function. Ignored if template has a format
*/
int value;
};
@@ -91,13 +91,13 @@ struct fuse_opt {
* Key option. In case of a match, the processing function will be
* called with the specified key.
*/
#define FUSE_OPT_KEY(template, key) { template, -1U, key }
#define FUSE_OPT_KEY(templ, key) { templ, -1U, key }
/**
* Last option. An array of 'struct fuse_opt' must end with a NULL
* template value
*/
#define FUSE_OPT_END { .template = NULL }
#define FUSE_OPT_END { .templ = NULL }
/**
* Argument list
@@ -120,7 +120,7 @@ struct fuse_args {
/**
* Key value passed to the processing function if an option did not
* match any templated
* match any template
*/
#define FUSE_OPT_KEY_OPT -1
@@ -132,6 +132,22 @@ struct fuse_args {
*/
#define FUSE_OPT_KEY_NONOPT -2
/**
* Special key value for options to keep
*
* Argument is not passed to processing function, but behave as if the
* processing function returned 1
*/
#define FUSE_OPT_KEY_KEEP -3
/**
* Special key value for options to discard
*
* Argument is not passed to processing function, but behave as if the
* processing function returned zero
*/
#define FUSE_OPT_KEY_DISCARD -4
/**
* Processing function
*
@@ -201,6 +217,21 @@ int fuse_opt_add_opt(char **opts, const char *opt);
*/
int fuse_opt_add_arg(struct fuse_args *args, const char *arg);
/**
* Add an argument at the specified position in a NULL terminated
* argument vector
*
* Adds the argument to the N-th position. This is useful for adding
* options at the beggining of the array which must not come after the
* special '--' option.
*
* @param args is the structure containing the current argument list
* @param pos is the position at which to add the argument
* @param arg is the new argument to add
* @return -1 on allocation error, 0 on success
*/
int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg);
/**
* Free the contents of argument list
*
+8 -3
View File
@@ -1,12 +1,17 @@
AC_INIT(sshfs-fuse, 1.4)
AC_INIT(sshfs-fuse, 1.5)
AM_INIT_AUTOMAKE
AM_CONFIG_HEADER(config.h)
AC_PROG_CC
CFLAGS="$CFLAGS -Wall -W"
LIBS=
AC_SEARCH_LIBS(dlsym, [dl])
sshnodelay_libs=$LIBS
AC_SUBST(sshnodelay_libs)
LIBS=
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH
PKG_CHECK_MODULES(SSHFS, [fuse >= 2.2 glib-2.0])
CFLAGS="$CFLAGS -Wall -W -D_REENTRANT $SSHFS_CFLAGS"
LIBS="$SSHFS_LIBS"
have_fuse_opt_parse=no
AC_CHECK_FUNC([fuse_opt_parse], [have_fuse_opt_parse=yes])
if test "$have_fuse_opt_parse" = no; then
+386 -118
View File
@@ -22,10 +22,13 @@
#include <pthread.h>
#include <netdb.h>
#include <signal.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/utsname.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <glib.h>
#include "cache.h"
@@ -95,6 +98,8 @@
#define SFTP_SERVER_PATH "/usr/lib/sftp-server"
#define SSHNODELAY_SO "sshnodelay.so"
struct buffer {
uint8_t *p;
size_t len;
@@ -129,6 +134,7 @@ struct read_chunk {
struct buffer data;
int refs;
int res;
long modifver;
};
struct sshfs_file {
@@ -140,15 +146,17 @@ struct sshfs_file {
off_t next_pos;
int is_seq;
int connver;
int modifver;
};
struct sshfs {
char *directport;
char *ssh_command;
char *sftp_server;
char *mountpoint;
struct fuse_args ssh_args;
char *workarounds;
int rename_workaround;
int nodelay_workaround;
int transform_symlinks;
int detect_uid;
unsigned max_read;
@@ -161,6 +169,7 @@ struct sshfs {
char *base_path;
GHashTable *reqtab;
pthread_mutex_t lock;
pthread_mutex_t lock_write;
int processing_thread_started;
unsigned int randseed;
int infd;
@@ -171,8 +180,8 @@ struct sshfs {
unsigned local_uid;
int remote_uid_detected;
unsigned blksize;
size_t symlink_prefix_len;
char symlink_prefix[PATH_MAX+1];
char *progname;
long modifver;
};
static struct sshfs sshfs;
@@ -236,9 +245,7 @@ static struct fuse_opt sshfs_opts[] = {
SSHFS_OPT("max_read=%u", max_read, 0),
SSHFS_OPT("ssh_protocol=%u", ssh_ver, 0),
SSHFS_OPT("-1", ssh_ver, 1),
SSHFS_OPT("workaround=none", rename_workaround, 0),
SSHFS_OPT("workaround=rename", rename_workaround, 1),
SSHFS_OPT("workaround=all", rename_workaround, 1),
SSHFS_OPT("workaround=%s", workarounds, 0),
SSHFS_OPT("idmap=none", detect_uid, 0),
SSHFS_OPT("idmap=user", detect_uid, 1),
SSHFS_OPT("sshfs_sync", sync_write, 1),
@@ -256,6 +263,18 @@ static struct fuse_opt sshfs_opts[] = {
FUSE_OPT_END
};
static struct fuse_opt workaround_opts[] = {
SSHFS_OPT("none", rename_workaround, 0),
SSHFS_OPT("none", nodelay_workaround, 0),
SSHFS_OPT("all", rename_workaround, 1),
SSHFS_OPT("all", nodelay_workaround, 1),
SSHFS_OPT("rename", rename_workaround, 1),
SSHFS_OPT("norename", rename_workaround, 0),
SSHFS_OPT("nodelay", nodelay_workaround, 1),
SSHFS_OPT("nonodelay", nodelay_workaround, 0),
FUSE_OPT_END
};
#define DEBUG(format, args...) \
do { if (sshfs.debug) fprintf(stderr, format, args); } while(0)
@@ -599,6 +618,48 @@ static void ssh_add_arg(const char *arg)
_exit(1);
}
static int do_ssh_nodelay_workaround(void)
{
char *oldpreload = getenv("LD_PRELOAD");
char *newpreload;
char sopath[PATH_MAX];
int res;
snprintf(sopath, sizeof(sopath), "%s/%s", LIBDIR, SSHNODELAY_SO);
res = access(sopath, R_OK);
if (res == -1) {
char *s;
if (!realpath(sshfs.progname, sopath))
return -1;
s = strrchr(sopath, '/');
if (!s)
s = sopath;
else
s++;
if (s + strlen(SSHNODELAY_SO) >= sopath + sizeof(sopath))
return -1;
strcpy(s, SSHNODELAY_SO);
res = access(sopath, R_OK);
if (res == -1) {
fprintf(stderr, "sshfs: cannot find %s\n", SSHNODELAY_SO);
return -1;
}
}
newpreload = g_strdup_printf("%s%s%s",
oldpreload ? oldpreload : "",
oldpreload ? " " : "",
sopath);
if (!newpreload || setenv("LD_PRELOAD", newpreload, 1) == -1)
fprintf(stderr, "warning: failed set LD_PRELOAD for ssh nodelay workaround\n");
g_free(newpreload);
return 0;
}
static int start_ssh(void)
{
int inpipe[2];
@@ -619,6 +680,9 @@ static int start_ssh(void)
} else if (pid == 0) {
int devnull;
if (sshfs.nodelay_workaround && do_ssh_nodelay_workaround() == -1)
fprintf(stderr, "warning: ssh nodelay workaround disabled\n");
devnull = open("/dev/null", O_WRONLY);
if (dup2(outpipe[0], 0) == -1 || dup2(inpipe[1], 1) == -1) {
@@ -659,6 +723,7 @@ static int connect_to(char *host, char *port)
{
int err;
int sock;
int opt;
struct addrinfo *ai;
struct addrinfo hint;
@@ -681,6 +746,11 @@ static int connect_to(char *host, char *port)
perror("failed to connect");
return -1;
}
opt = 1;
err = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
if (err == -1)
perror("warning: failed to set TCP_NODELAY");
freeaddrinfo(ai);
sshfs.infd = sock;
@@ -688,13 +758,11 @@ static int connect_to(char *host, char *port)
return 0;
}
static int do_write(struct buffer *buf)
static int do_write(struct iovec *iov, size_t count)
{
uint8_t *p = buf->p;
size_t size = buf->len;
int res;
while (size) {
res = write(sshfs.outfd, p, size);
while (count) {
res = writev(sshfs.outfd, iov, count);
if (res == -1) {
perror("write");
return -1;
@@ -702,8 +770,17 @@ static int do_write(struct buffer *buf)
fprintf(stderr, "zero write\n");
return -1;
}
size -= res;
p += res;
do {
if ((unsigned) res < iov->iov_len) {
iov->iov_len -= res;
iov->iov_base += res;
break;
} else {
res -= iov->iov_len;
count --;
iov ++;
}
} while(count);
}
return 0;
}
@@ -714,16 +791,25 @@ static uint32_t sftp_get_id(void)
return idctr++;
}
static void buf_to_iov(struct buffer *buf, struct iovec *iov)
{
iov->iov_base = buf->p;
iov->iov_len = buf->len;
}
static int sftp_send(uint8_t type, struct buffer *buf)
{
int res;
struct buffer buf2;
struct iovec iov[2];
buf_init(&buf2, 5);
buf_add_uint32(&buf2, buf->len + 1);
buf_add_uint8(&buf2, type);
res = do_write(&buf2);
if (res != -1)
res = do_write(buf);
buf_to_iov(&buf2, &iov[0]);
buf_to_iov(buf, &iov[1]);
pthread_mutex_lock(&sshfs.lock_write);
res = do_write(iov, 2);
pthread_mutex_unlock(&sshfs.lock_write);
buf_free(&buf2);
return res;
}
@@ -986,6 +1072,9 @@ static int start_processing_thread(void)
{
int err;
pthread_t thread_id;
sigset_t oldset;
sigset_t newset;
if (sshfs.processing_thread_started)
return 0;
@@ -995,19 +1084,36 @@ static int start_processing_thread(void)
return -EIO;
}
sigemptyset(&newset);
sigaddset(&newset, SIGTERM);
sigaddset(&newset, SIGINT);
sigaddset(&newset, SIGHUP);
sigaddset(&newset, SIGQUIT);
pthread_sigmask(SIG_BLOCK, &newset, &oldset);
err = pthread_create(&thread_id, NULL, process_requests, NULL);
if (err) {
fprintf(stderr, "failed to create thread: %s\n", strerror(err));
return -EIO;
}
pthread_detach(thread_id);
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
sshfs.processing_thread_started = 1;
return 0;
}
#ifdef SSHFS_USE_INIT
#if FUSE_VERSION >= 26
static void *sshfs_init(struct fuse_conn_info *conn)
#else
static void *sshfs_init(void)
#endif
{
#if FUSE_VERSION >= 26
/* Readahead should be done by kernel or sshfs but not both */
if (conn->async_read)
sshfs.sync_read = 1;
#endif
if (sshfs.detect_uid)
sftp_detect_uid();
@@ -1016,50 +1122,15 @@ static void *sshfs_init(void)
}
#endif
static int sftp_request_common(uint8_t type, const struct buffer *buf,
uint8_t expect_type, struct buffer *outbuf,
request_func begin_func, request_func end_func,
void *data)
static int sftp_request_wait(struct request *req, uint8_t type,
uint8_t expect_type, struct buffer *outbuf)
{
int err;
struct buffer buf2;
uint32_t id;
struct request *req = g_new0(struct request, 1);
req->want_reply = expect_type != 0 ? 1 : 0;
req->end_func = end_func;
req->data = data;
sem_init(&req->ready, 0, 0);
buf_init(&req->reply, 0);
buf_init(&buf2, buf->len + 4);
pthread_mutex_lock(&sshfs.lock);
if (begin_func)
begin_func(req);
id = sftp_get_id();
buf_add_uint32(&buf2, id);
buf_add_mem(&buf2, buf->p, buf->len);
err = start_processing_thread();
if (err) {
pthread_mutex_unlock(&sshfs.lock);
if (req->error) {
err = req->error;
goto out;
}
g_hash_table_insert(sshfs.reqtab, GUINT_TO_POINTER(id), req);
gettimeofday(&req->start, NULL);
DEBUG("[%05i] %s\n", id, type_name(type));
err = -EIO;
if (sftp_send(type, &buf2) == -1) {
g_hash_table_remove(sshfs.reqtab, GUINT_TO_POINTER(id));
pthread_mutex_unlock(&sshfs.lock);
goto out;
}
pthread_mutex_unlock(&sshfs.lock);
if (expect_type == 0) {
buf_free(&buf2);
return 0;
}
sem_wait(&req->ready);
if (req->error) {
err = req->error;
@@ -1103,16 +1174,85 @@ static int sftp_request_common(uint8_t type, const struct buffer *buf,
}
out:
if (end_func) {
if (req->end_func) {
pthread_mutex_lock(&sshfs.lock);
end_func(req);
req->end_func(req);
pthread_mutex_unlock(&sshfs.lock);
}
buf_free(&buf2);
request_free(req);
return err;
}
static int sftp_request_send(uint8_t type, const struct buffer *buf,
request_func begin_func, request_func end_func,
int want_reply, void *data,
struct request **reqp)
{
int err;
struct buffer buf2;
uint32_t id;
struct request *req = g_new0(struct request, 1);
req->want_reply = want_reply;
req->end_func = end_func;
req->data = data;
sem_init(&req->ready, 0, 0);
buf_init(&req->reply, 0);
buf_init(&buf2, buf->len + 4);
pthread_mutex_lock(&sshfs.lock);
if (begin_func)
begin_func(req);
id = sftp_get_id();
buf_add_uint32(&buf2, id);
buf_add_mem(&buf2, buf->p, buf->len);
err = start_processing_thread();
if (err) {
pthread_mutex_unlock(&sshfs.lock);
goto out;
}
g_hash_table_insert(sshfs.reqtab, GUINT_TO_POINTER(id), req);
gettimeofday(&req->start, NULL);
DEBUG("[%05i] %s\n", id, type_name(type));
pthread_mutex_unlock(&sshfs.lock);
err = -EIO;
if (sftp_send(type, &buf2) == -1) {
pthread_mutex_lock(&sshfs.lock);
g_hash_table_remove(sshfs.reqtab, GUINT_TO_POINTER(id));
pthread_mutex_unlock(&sshfs.lock);
goto out;
}
buf_free(&buf2);
if (want_reply)
*reqp = req;
return 0;
out:
buf_free(&buf2);
req->error = err;
if (!want_reply)
sftp_request_wait(req, type, 0, NULL);
return err;
}
static int sftp_request_common(uint8_t type, const struct buffer *buf,
uint8_t expect_type, struct buffer *outbuf,
request_func begin_func, request_func end_func,
void *data)
{
struct request *req;
sftp_request_send(type, buf, begin_func, end_func, expect_type, data,
&req);
if (expect_type == 0)
return 0;
return sftp_request_wait(req, type, expect_type, outbuf);
}
static int sftp_request(uint8_t type, const struct buffer *buf,
uint8_t expect_type, struct buffer *outbuf)
{
@@ -1124,7 +1264,7 @@ static int sftp_request_async(uint8_t type, const struct buffer *buf,
request_func begin_func, request_func end_func,
void *data)
{
return sftp_request_common(type, buf, 0, NULL, begin_func, end_func, data);
return sftp_request_send(type, buf, begin_func, end_func, 0, data, NULL);
}
static int sshfs_getattr(const char *path, struct stat *stbuf)
@@ -1144,6 +1284,72 @@ static int sshfs_getattr(const char *path, struct stat *stbuf)
return err;
}
static int count_components(const char *p)
{
int ctr;
for (; *p == '/'; p++);
for (ctr = 0; *p; ctr++) {
for (; *p && *p != '/'; p++);
for (; *p == '/'; p++);
}
return ctr;
}
static void strip_common(const char **sp, const char **tp)
{
const char *s = *sp;
const char *t = *tp;
do {
for (; *s == '/'; s++);
for (; *t == '/'; t++);
*tp = t;
*sp = s;
for (; *s == *t && *s && *s != '/'; s++, t++);
} while ((*s == *t && *s) || (!*s && *t == '/') || (*s == '/' && !*t));
}
static void transform_symlink(const char *path, char **linkp)
{
const char *l = *linkp;
const char *b = sshfs.base_path;
char *newlink;
char *s;
int dotdots;
int i;
if (l[0] != '/' || b[0] != '/')
return;
strip_common(&l, &b);
if (*b)
return;
strip_common(&l, &path);
dotdots = count_components(path);
if (!dotdots)
return;
dotdots--;
newlink = malloc(dotdots * 3 + l ? strlen(l) : 1 + 10);
if (!newlink) {
fprintf(stderr, "sshfs: memory allocation failed\n");
exit(1);
}
for (s = newlink, i = 0; i < dotdots; i++, s += 3)
strcpy(s, "../");
if (l[0])
strcpy(s, l);
else if (!dotdots)
strcpy(s, ".");
else
s[0] = '\0';
free(*linkp);
*linkp = newlink;
}
static int sshfs_readlink(const char *path, char *linkbuf, size_t size)
{
int err;
@@ -1164,14 +1370,8 @@ static int sshfs_readlink(const char *path, char *linkbuf, size_t size)
err = -EIO;
if(buf_get_uint32(&name, &count) != -1 && count == 1 &&
buf_get_string(&name, &link) != -1) {
if (link[0] == '/' && sshfs.symlink_prefix_len) {
size_t len = sshfs.symlink_prefix_len;
if (len > size - 1)
len = size - 1;
memcpy(linkbuf, sshfs.symlink_prefix, len);
linkbuf += len;
size -= len;
}
if (sshfs.transform_symlinks)
transform_symlink(path, &link);
strncpy(linkbuf, link, size - 1);
linkbuf[size - 1] = '\0';
free(link);
@@ -1372,6 +1572,8 @@ static int sshfs_truncate(const char *path, off_t size)
{
int err;
struct buffer buf;
sshfs.modifver ++;
buf_init(&buf, 0);
buf_add_path(&buf, path);
if (size == 0) {
@@ -1421,8 +1623,12 @@ static int sshfs_open_common(const char *path, mode_t mode,
struct fuse_file_info *fi)
{
int err;
int err2;
struct buffer buf;
struct buffer outbuf;
struct stat stbuf;
struct sshfs_file *sf;
struct request *open_req;
uint32_t pflags = 0;
if ((fi->flags & O_ACCMODE) == O_RDONLY)
pflags = SSH_FXF_READ;
@@ -1448,18 +1654,36 @@ static int sshfs_open_common(const char *path, mode_t mode,
/* Assume random read after open */
sf->is_seq = 0;
sf->next_pos = 0;
sf->modifver= sshfs.modifver;
sf->connver = sshfs.connver;
buf_init(&buf, 0);
buf_add_path(&buf, path);
buf_add_uint32(&buf, pflags);
buf_add_uint32(&buf, SSH_FILEXFER_ATTR_PERMISSIONS);
buf_add_uint32(&buf, mode);
err = sftp_request(SSH_FXP_OPEN, &buf, SSH_FXP_HANDLE, &sf->handle);
sftp_request_send(SSH_FXP_OPEN, &buf, NULL, NULL, 1, NULL, &open_req);
buf_clear(&buf);
buf_add_path(&buf, path);
err2 = sftp_request(SSH_FXP_LSTAT, &buf, SSH_FXP_ATTRS, &outbuf);
if (!err2 && buf_get_attrs(&outbuf, &stbuf, NULL) == -1)
err2 = -EIO;
err = sftp_request_wait(open_req, SSH_FXP_OPEN, SSH_FXP_HANDLE,
&sf->handle);
if (!err && err2) {
buf_finish(&sf->handle);
sftp_request(SSH_FXP_CLOSE, &sf->handle, 0, NULL);
buf_free(&sf->handle);
err = err2;
}
if (!err) {
cache_add_attr(path, &stbuf);
buf_finish(&sf->handle);
fi->fh = (unsigned long) sf;
} else
} else {
cache_invalidate(path);
g_free(sf);
}
buf_free(&buf);
return err;
}
@@ -1594,26 +1818,23 @@ static void sshfs_read_begin(struct request *req)
chunk->refs++;
}
static int sshfs_send_async_read(struct sshfs_file *sf,
struct read_chunk *chunk)
static void sshfs_send_async_read(struct sshfs_file *sf,
struct read_chunk *chunk)
{
int err;
struct buffer buf;
struct buffer *handle = &sf->handle;
buf_init(&buf, 0);
buf_add_buf(&buf, handle);
buf_add_uint64(&buf, chunk->offset);
buf_add_uint32(&buf, chunk->size);
err = sftp_request_async(SSH_FXP_READ, &buf, sshfs_read_begin,
sshfs_read_end, chunk);
sftp_request_async(SSH_FXP_READ, &buf, sshfs_read_begin, sshfs_read_end,
chunk);
buf_free(&buf);
return err;
}
static int submit_read(struct sshfs_file *sf, size_t size, off_t offset,
struct read_chunk **chunkp)
static void submit_read(struct sshfs_file *sf, size_t size, off_t offset,
struct read_chunk **chunkp)
{
int err;
struct read_chunk *chunk = g_new0(struct read_chunk, 1);
sem_init(&chunk->ready, 0, 0);
@@ -1621,16 +1842,12 @@ static int submit_read(struct sshfs_file *sf, size_t size, off_t offset,
chunk->offset = offset;
chunk->size = size;
chunk->refs = 1;
err = sshfs_send_async_read(sf, chunk);
if (!err) {
pthread_mutex_lock(&sshfs.lock);
chunk_put(*chunkp);
*chunkp = chunk;
pthread_mutex_unlock(&sshfs.lock);
} else
chunk_put(chunk);
return err;
chunk->modifver = sshfs.modifver;
sshfs_send_async_read(sf, chunk);
pthread_mutex_lock(&sshfs.lock);
chunk_put(*chunkp);
*chunkp = chunk;
pthread_mutex_unlock(&sshfs.lock);
}
static int wait_chunk(struct read_chunk *chunk, char *buf, size_t size)
@@ -1654,7 +1871,7 @@ static int wait_chunk(struct read_chunk *chunk, char *buf, size_t size)
static struct read_chunk *search_read_chunk(struct sshfs_file *sf, off_t offset)
{
struct read_chunk *ch = sf->readahead;
if (ch && ch->offset == offset) {
if (ch && ch->offset == offset && ch->modifver == sshfs.modifver) {
ch->refs++;
return ch;
} else
@@ -1673,8 +1890,9 @@ static int sshfs_async_read(struct sshfs_file *sf, char *rbuf, size_t size,
pthread_mutex_lock(&sshfs.lock);
curr_is_seq = sf->is_seq;
sf->is_seq = (sf->next_pos == offset);
sf->is_seq = (sf->next_pos == offset && sf->modifver == sshfs.modifver);
sf->next_pos = offset + size;
sf->modifver = sshfs.modifver;
chunk = search_read_chunk(sf, offset);
pthread_mutex_unlock(&sshfs.lock);
@@ -1686,7 +1904,7 @@ static int sshfs_async_read(struct sshfs_file *sf, char *rbuf, size_t size,
}
if (!chunk)
res = submit_read(sf, size, offset, &chunk);
submit_read(sf, size, offset, &chunk);
if (curr_is_seq && chunk && chunk->size <= size)
submit_read(sf, origsize, offset + size, &sf->readahead);
@@ -1762,6 +1980,7 @@ static int sshfs_write(const char *path, const char *wbuf, size_t size,
if (!sshfs_file_is_conn(sf))
return -EIO;
sshfs.modifver ++;
data.p = (uint8_t *) wbuf;
data.len = size;
buf_init(&buf, 0);
@@ -1827,6 +2046,7 @@ static int sshfs_ftruncate(const char *path, off_t size,
if (!sshfs_file_is_conn(sf))
return -EIO;
sshfs.modifver ++;
buf_init(&buf, 0);
buf_add_buf(&buf, &sf->handle);
buf_add_uint32(&buf, SSH_FILEXFER_ATTR_SIZE);
@@ -1866,6 +2086,7 @@ static int sshfs_fgetattr(const char *path, struct stat *stbuf,
static int processing_init(void)
{
pthread_mutex_init(&sshfs.lock, NULL);
pthread_mutex_init(&sshfs.lock_write, NULL);
sshfs.reqtab = g_hash_table_new(NULL, NULL);
if (!sshfs.reqtab) {
fprintf(stderr, "failed to create hash table\n");
@@ -1910,7 +2131,7 @@ static struct fuse_cache_operations sshfs_oper = {
static void usage(const char *progname)
{
fprintf(stderr,
"usage: %s [user@]host:[dir]] mountpoint [options]\n"
"usage: %s [user@]host:[dir] mountpoint [options]\n"
"\n"
"general options:\n"
" -o opt,[opt...] mount options\n"
@@ -1929,9 +2150,10 @@ static void usage(const char *progname)
" -o cache_timeout=N sets timeout for caches in seconds (default: 20)\n"
" -o cache_X_timeout=N sets timeout for {stat,dir,link} cache\n"
" -o workaround=LIST colon separated list of workarounds\n"
" none no workarounds enabled (default)\n"
" none no workarounds enabled\n"
" all all workarounds enabled\n"
" rename work around problem renaming to existing file\n"
" [no]rename fix renaming to existing file (default: off)\n"
" [no]nodelay set nodelay tcp flag in ssh (default: on)\n"
" -o idmap=TYPE user/group ID mapping, possible types are:\n"
" none no translation of the ID space (default)\n"
" user only translate UID of connecting user\n"
@@ -1939,7 +2161,7 @@ static void usage(const char *progname)
" -o ssh_protocol=N ssh protocol to use (default: 2)\n"
" -o sftp_server=SERV path to sftp server or subsystem (default: sftp)\n"
" -o directport=PORT directly connect to PORT bypassing ssh\n"
" -o transform_symlinks prepend mountpoint to absolute symlink targets\n"
" -o transform_symlinks transform absolute symlinks to relative\n"
" -o SSHOPT=VAL ssh options (see man ssh_config)\n"
"\n", progname);
}
@@ -1979,8 +2201,7 @@ static int sshfs_opt_proc(void *data, const char *arg, int key,
if (!sshfs.host && strchr(arg, ':')) {
sshfs.host = strdup(arg);
return 0;
} else if (!sshfs.mountpoint)
sshfs.mountpoint = strdup(arg);
}
return 1;
case KEY_PORT:
@@ -2013,6 +2234,59 @@ static int sshfs_opt_proc(void *data, const char *arg, int key,
}
}
static int workaround_opt_proc(void *data, const char *arg, int key,
struct fuse_args *outargs)
{
(void) data; (void) key; (void) outargs;
fprintf(stderr, "unknown workaround: '%s'\n", arg);
return -1;
}
int parse_workarounds(void)
{
int res;
char *argv[] = { "", "-o", sshfs.workarounds, NULL };
struct fuse_args args = FUSE_ARGS_INIT(3, argv);
char *s = sshfs.workarounds;
if (!s)
return 0;
while ((s = strchr(s, ':')))
*s = ',';
res = fuse_opt_parse(&args, &sshfs, workaround_opts, workaround_opt_proc);
fuse_opt_free_args(&args);
return res;
}
#if FUSE_VERSION == 25
static int fuse_opt_insert_arg(struct fuse_args *args, int pos,
const char *arg)
{
assert(pos <= args->argc);
if (fuse_opt_add_arg(args, arg) == -1)
return -1;
if (pos != args->argc - 1) {
char *newarg = args->argv[args->argc - 1];
memmove(&args->argv[pos + 1], &args->argv[pos],
sizeof(char *) * (args->argc - pos - 1));
args->argv[pos] = newarg;
}
return 0;
}
#endif
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");
}
int main(int argc, char *argv[])
{
int res;
@@ -2024,13 +2298,17 @@ int main(int argc, char *argv[])
sshfs.blksize = 4096;
sshfs.max_read = 65536;
sshfs.nodelay_workaround = 1;
sshfs.rename_workaround = 0;
sshfs.ssh_ver = 2;
sshfs.progname = argv[0];
ssh_add_arg("ssh");
ssh_add_arg("-x");
ssh_add_arg("-a");
ssh_add_arg("-oClearAllForwardings=yes");
if (fuse_opt_parse(&args, &sshfs, sshfs_opts, sshfs_opt_proc) == -1)
if (fuse_opt_parse(&args, &sshfs, sshfs_opts, sshfs_opt_proc) == -1 ||
parse_workarounds() == -1)
exit(1);
if (!sshfs.host) {
@@ -2069,18 +2347,18 @@ int main(int argc, char *argv[])
ssh_add_arg(sftp_server);
free(sshfs.sftp_server);
res = processing_init();
if (res == -1)
exit(1);
if (connect_remote() == -1)
exit(1);
#ifndef SSHFS_USE_INIT
if (detect_uid)
if (sshfs.detect_uid)
sftp_detect_uid();
#endif
res = processing_init();
if (res == -1)
exit(1);
res = cache_parse_options(&args);
if (res == -1)
exit(1);
@@ -2090,24 +2368,14 @@ int main(int argc, char *argv[])
if (sshfs.max_read > 65536)
sshfs.max_read = 65536;
if (sshfs.transform_symlinks && !sshfs.mountpoint) {
fprintf(stderr, "cannot transform symlinks: no mountpoint given\n");
exit(1);
}
if (!sshfs.transform_symlinks)
sshfs.symlink_prefix_len = 0;
else if (realpath(sshfs.mountpoint, sshfs.symlink_prefix) != NULL)
sshfs.symlink_prefix_len = strlen(sshfs.symlink_prefix);
else {
perror("unable to normalize mount path");
exit(1);
}
if (fuse_is_lib_option("ac_attr_timeout="))
fuse_opt_insert_arg(&args, 1, "-oauto_cache,ac_attr_timeout=0");
tmp = g_strdup_printf("-omax_read=%u", sshfs.max_read);
fuse_opt_add_arg(&args, tmp);
fuse_opt_insert_arg(&args, 1, tmp);
g_free(tmp);
tmp = g_strdup_printf("-ofsname=sshfs#%s", fsname);
fuse_opt_add_arg(&args, tmp);
fuse_opt_insert_arg(&args, 1, tmp);
check_large_read(&args);
g_free(tmp);
g_free(fsname);
res = fuse_main(args.argc, args.argv, cache_init(&sshfs_oper));
+18
View File
@@ -0,0 +1,18 @@
#define _GNU_SOURCE
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
int connect(int sock, const struct sockaddr *addr, socklen_t addrlen)
{
int (*next_connect)(int, const struct sockaddr *, socklen_t) =
dlsym(RTLD_NEXT, "connect");
int res = next_connect(sock, addr, addrlen);
if (!res && addr->sa_family == AF_INET) {
int opt = 1;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
}
return res;
}