Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3cfb3a6dab | |||
| 790bb8dd36 | |||
| accc218cdb | |||
| 8468900705 | |||
| b60a970602 | |||
| 27c1b7b09d | |||
| 6f0233e11e | |||
| 89da93d07e | |||
| cb4239036a | |||
| 40393a4b0b | |||
| a6e0d2ddd9 | |||
| 1cb9aba6ea | |||
| 3883b4eb0c | |||
| f1f738d5b8 | |||
| d2a3ed327e | |||
| cd6a924cdc |
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
------------------
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user