Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1af7516f1e | |||
| 15f911853e | |||
| eff1631428 | |||
| 4399cfca84 | |||
| dd258a5117 | |||
| feeaf76340 | |||
| b3ffcfe3ac | |||
| d045a9be5e | |||
| 09d5ee2eee | |||
| b917f9a420 | |||
| 2d2ad5176a | |||
| 006aa04ff4 | |||
| 4046be4487 | |||
| d8c313ff85 |
@@ -1,6 +1,7 @@
|
||||
.deps
|
||||
COPYING
|
||||
INSTALL
|
||||
FAQ.txt
|
||||
Makefile
|
||||
Makefile.in
|
||||
aclocal.m4
|
||||
|
||||
@@ -1,3 +1,81 @@
|
||||
2007-05-16 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Released 1.8
|
||||
|
||||
2007-05-15 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add needed g_thread_init() to fix rare crashes. Reported by
|
||||
Dimitrios Apostolou
|
||||
|
||||
* Fix memory leak in sshfs_open_common()
|
||||
|
||||
2007-04-18 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix crash within gdb, caused by sem_wait() returning with an
|
||||
error on interrupt. Reported by Dimitrios Apostolou
|
||||
|
||||
* Fix spurious cache entries remaining after renaming a directory
|
||||
|
||||
2007-02-28 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Don't set DISPLAY environment variable to "", because it breaks
|
||||
ssh-askpass. Make nodelaysrv_workaround default to off, because
|
||||
with this change it may have security implications.
|
||||
|
||||
2007-02-19 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* OpenSSH sftp-server can read requests faster, than it processes
|
||||
them, when it's buffer is full it aborts. This can happen on a
|
||||
large upload to a slow server. Work around this by limiting the
|
||||
total size of outstanding reqests. Debian bug #365541. Tracked
|
||||
down by Thue Janus Kristensen
|
||||
|
||||
* Add --disable-sshnodelay configure option. The sshnodelay.so
|
||||
hack shouldnt be needed with OpenSSH client versions >= 4.3
|
||||
|
||||
2006-12-20 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Work around write performace issues due to missing TCP_NODELAY
|
||||
in sshd. Reported by Justin Searle
|
||||
|
||||
2006-11-10 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix bug which ommitted directory entries for symlinks with the
|
||||
-ofollow_symlinks option. Bug reported by Mikael Ståldal
|
||||
|
||||
2006-09-29 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix segfault if there are outstanding writes to the server after
|
||||
release on the file descriptor. This only happened on FreeBSD.
|
||||
Reported by Andriy Gapon
|
||||
|
||||
2006-08-18 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Released 1.7
|
||||
|
||||
2006-08-17 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Cosmetics on bogus statfs numbers
|
||||
|
||||
2006-08-16 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add option 'follow_symlinks' to resolve symlinks on the server.
|
||||
|
||||
* On setups where the login shell prints a banner, the sftp
|
||||
protocol was confused, and bailed out with "reply len too large:
|
||||
...". So iterate over any rubbish read immediately after login
|
||||
until a valid reply is found.
|
||||
|
||||
* Check remote directory for existence before mounting. Closes
|
||||
Ubuntu bug #46633
|
||||
|
||||
2006-03-10 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Use socketpair() instead of pipe() for communication between ssh
|
||||
and sshfs. Patch from Csaba Henk
|
||||
|
||||
* Update to FUSE interface version 26
|
||||
|
||||
2006-03-09 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Released 1.6
|
||||
|
||||
+3
-1
@@ -11,9 +11,10 @@ sshfs_LDADD = $(SSHFS_LIBS)
|
||||
sshfs_CFLAGS = $(SSHFS_CFLAGS)
|
||||
sshfs_CPPFLAGS = -D_REENTRANT -DFUSE_USE_VERSION=26 -DLIBDIR=\"$(libdir)\"
|
||||
|
||||
EXTRA_DIST = sshnodelay.c
|
||||
EXTRA_DIST = sshnodelay.c FAQ.txt
|
||||
CLEANFILES = sshnodelay.so
|
||||
|
||||
if SSH_NODELAY_SO
|
||||
all-local: sshnodelay.so
|
||||
|
||||
install-exec-local: sshnodelay.so
|
||||
@@ -25,3 +26,4 @@ uninstall-local:
|
||||
|
||||
sshnodelay.so:
|
||||
$(CC) -Wall -W -s --shared -fPIC $(sshnodelay_libs) sshnodelay.c -o sshnodelay.so
|
||||
endif
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
What is new in 1.8
|
||||
------------------
|
||||
|
||||
* Bug fixes
|
||||
|
||||
What is new in 1.7
|
||||
------------------
|
||||
|
||||
* Tolerate servers which print a banner on login
|
||||
|
||||
* Small improvements
|
||||
|
||||
What is new in 1.6
|
||||
------------------
|
||||
|
||||
|
||||
@@ -116,9 +116,20 @@ static void cache_invalidate_dir(const char *path)
|
||||
pthread_mutex_unlock(&cache.lock);
|
||||
}
|
||||
|
||||
static int cache_del_children(const char *key, void *val_, const char *path)
|
||||
{
|
||||
(void) val_;
|
||||
if (strncmp(key, path, strlen(path)) == 0)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void cache_do_rename(const char *from, const char *to)
|
||||
{
|
||||
pthread_mutex_lock(&cache.lock);
|
||||
g_hash_table_foreach_remove(cache.table, (GHRFunc) cache_del_children,
|
||||
(char *) from);
|
||||
cache_purge(from);
|
||||
cache_purge(to);
|
||||
cache_purge_parent(from);
|
||||
@@ -251,11 +262,13 @@ static int cache_dirfill(fuse_cache_dirh_t ch, const char *name,
|
||||
{
|
||||
int err = ch->filler(ch->h, name, 0, 0);
|
||||
if (!err) {
|
||||
char *fullpath;
|
||||
g_ptr_array_add(ch->dir, g_strdup(name));
|
||||
fullpath = g_strdup_printf("%s/%s", !ch->path[1] ? "" : ch->path, name);
|
||||
cache_add_attr(fullpath, stbuf);
|
||||
g_free(fullpath);
|
||||
if (stbuf->st_mode & S_IFMT) {
|
||||
char *fullpath =
|
||||
g_strdup_printf("%s/%s", !ch->path[1] ? "" : ch->path, name);
|
||||
cache_add_attr(fullpath, stbuf);
|
||||
g_free(fullpath);
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
+11
-2
@@ -1,4 +1,4 @@
|
||||
AC_INIT(sshfs-fuse, 1.6)
|
||||
AC_INIT(sshfs-fuse, 1.8)
|
||||
AM_INIT_AUTOMAKE
|
||||
AM_CONFIG_HEADER(config.h)
|
||||
|
||||
@@ -10,8 +10,17 @@ sshnodelay_libs=$LIBS
|
||||
AC_SUBST(sshnodelay_libs)
|
||||
LIBS=
|
||||
|
||||
AC_ARG_ENABLE(sshnodelay,
|
||||
[ --disable-sshnodelay Don't compile NODELAY workaround for ssh])
|
||||
|
||||
if test "$enable_sshnodelay" != "no"; then
|
||||
AC_DEFINE(SSH_NODELAY_WORKAROUND, 1, [Compile ssh NODELAY workaround])
|
||||
fi
|
||||
|
||||
AM_CONDITIONAL(SSH_NODELAY_SO, test "$enable_sshnodelay" != "no")
|
||||
|
||||
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH
|
||||
PKG_CHECK_MODULES(SSHFS, [fuse >= 2.2 glib-2.0])
|
||||
PKG_CHECK_MODULES(SSHFS, [fuse >= 2.2 glib-2.0 gthread-2.0])
|
||||
have_fuse_opt_parse=no
|
||||
AC_CHECK_FUNC([fuse_opt_parse], [have_fuse_opt_parse=yes])
|
||||
if test "$have_fuse_opt_parse" = no; then
|
||||
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#! /bin/bash
|
||||
|
||||
(
|
||||
echo "See http://fuse.sourceforge.net/wiki/index.php/SshfsFaq for the latest"
|
||||
echo "version of this FAQ"
|
||||
echo "---"
|
||||
echo
|
||||
|
||||
lynx -nolist -dump http://fuse.sourceforge.net/wiki/index.php/SshfsFaq \
|
||||
| sed -e '1,12d' -e '/____/,$d'
|
||||
) > FAQ.txt
|
||||
@@ -124,6 +124,7 @@ struct request {
|
||||
struct timeval start;
|
||||
void *data;
|
||||
request_func end_func;
|
||||
size_t len;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
@@ -147,6 +148,7 @@ struct sshfs_file {
|
||||
int is_seq;
|
||||
int connver;
|
||||
int modifver;
|
||||
int refs;
|
||||
};
|
||||
|
||||
struct sshfs {
|
||||
@@ -157,8 +159,12 @@ struct sshfs {
|
||||
char *workarounds;
|
||||
int rename_workaround;
|
||||
int nodelay_workaround;
|
||||
int nodelaysrv_workaround;
|
||||
int truncate_workaround;
|
||||
int buflimit_workaround;
|
||||
int transform_symlinks;
|
||||
int follow_symlinks;
|
||||
int no_check_root;
|
||||
int detect_uid;
|
||||
unsigned max_read;
|
||||
unsigned ssh_ver;
|
||||
@@ -173,8 +179,7 @@ struct sshfs {
|
||||
pthread_mutex_t lock_write;
|
||||
int processing_thread_started;
|
||||
unsigned int randseed;
|
||||
int infd;
|
||||
int outfd;
|
||||
int fd;
|
||||
int connver;
|
||||
int server_version;
|
||||
unsigned remote_uid;
|
||||
@@ -183,6 +188,9 @@ struct sshfs {
|
||||
unsigned blksize;
|
||||
char *progname;
|
||||
long modifver;
|
||||
unsigned outstanding_len;
|
||||
unsigned max_outstanding_len;
|
||||
pthread_cond_t outstanding_cond;
|
||||
};
|
||||
|
||||
static struct sshfs sshfs;
|
||||
@@ -254,6 +262,8 @@ static struct fuse_opt sshfs_opts[] = {
|
||||
SSHFS_OPT("sshfs_debug", debug, 1),
|
||||
SSHFS_OPT("reconnect", reconnect, 1),
|
||||
SSHFS_OPT("transform_symlinks", transform_symlinks, 1),
|
||||
SSHFS_OPT("follow_symlinks", follow_symlinks, 1),
|
||||
SSHFS_OPT("no_check_root", no_check_root, 1),
|
||||
|
||||
FUSE_OPT_KEY("-p ", KEY_PORT),
|
||||
FUSE_OPT_KEY("-C", KEY_COMPRESS),
|
||||
@@ -267,16 +277,24 @@ static struct fuse_opt sshfs_opts[] = {
|
||||
static struct fuse_opt workaround_opts[] = {
|
||||
SSHFS_OPT("none", rename_workaround, 0),
|
||||
SSHFS_OPT("none", nodelay_workaround, 0),
|
||||
SSHFS_OPT("none", nodelaysrv_workaround, 0),
|
||||
SSHFS_OPT("none", truncate_workaround, 0),
|
||||
SSHFS_OPT("none", buflimit_workaround, 0),
|
||||
SSHFS_OPT("all", rename_workaround, 1),
|
||||
SSHFS_OPT("all", nodelay_workaround, 1),
|
||||
SSHFS_OPT("all", nodelaysrv_workaround, 1),
|
||||
SSHFS_OPT("all", truncate_workaround, 1),
|
||||
SSHFS_OPT("all", buflimit_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),
|
||||
SSHFS_OPT("nodelaysrv", nodelaysrv_workaround, 1),
|
||||
SSHFS_OPT("nonodelaysrv", nodelaysrv_workaround, 0),
|
||||
SSHFS_OPT("truncate", truncate_workaround, 1),
|
||||
SSHFS_OPT("notruncate", truncate_workaround, 0),
|
||||
SSHFS_OPT("buflimit", buflimit_workaround, 1),
|
||||
SSHFS_OPT("nobuflimit", buflimit_workaround, 0),
|
||||
FUSE_OPT_END
|
||||
};
|
||||
|
||||
@@ -378,7 +396,6 @@ static inline void buf_finish(struct buffer *buf)
|
||||
buf->len = buf->size;
|
||||
}
|
||||
|
||||
|
||||
static inline void buf_clear(struct buffer *buf)
|
||||
{
|
||||
buf_free(buf);
|
||||
@@ -606,6 +623,8 @@ static int buf_get_entries(struct buffer *buf, fuse_cache_dirh_t h,
|
||||
if (buf_get_string(buf, &longname) != -1) {
|
||||
free(longname);
|
||||
if (buf_get_attrs(buf, &stbuf, NULL) != -1) {
|
||||
if (sshfs.follow_symlinks && S_ISLNK(stbuf.st_mode))
|
||||
stbuf.st_mode = 0;
|
||||
filler(h, name, &stbuf);
|
||||
err = 0;
|
||||
}
|
||||
@@ -623,6 +642,7 @@ static void ssh_add_arg(const char *arg)
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
#ifdef SSH_NODELAY_WORKAROUND
|
||||
static int do_ssh_nodelay_workaround(void)
|
||||
{
|
||||
char *oldpreload = getenv("LD_PRELOAD");
|
||||
@@ -664,19 +684,18 @@ static int do_ssh_nodelay_workaround(void)
|
||||
g_free(newpreload);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int start_ssh(void)
|
||||
{
|
||||
int inpipe[2];
|
||||
int outpipe[2];
|
||||
int sockpair[2];
|
||||
int pid;
|
||||
|
||||
if (pipe(inpipe) == -1 || pipe(outpipe) == -1) {
|
||||
perror("failed to create pipe");
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockpair) == -1) {
|
||||
perror("failed to create socket pair");
|
||||
return -1;
|
||||
}
|
||||
sshfs.infd = inpipe[0];
|
||||
sshfs.outfd = outpipe[1];
|
||||
sshfs.fd = sockpair[0];
|
||||
|
||||
pid = fork();
|
||||
if (pid == -1) {
|
||||
@@ -685,12 +704,19 @@ static int start_ssh(void)
|
||||
} else if (pid == 0) {
|
||||
int devnull;
|
||||
|
||||
#ifdef SSH_NODELAY_WORKAROUND
|
||||
if (sshfs.nodelay_workaround && do_ssh_nodelay_workaround() == -1)
|
||||
fprintf(stderr, "warning: ssh nodelay workaround disabled\n");
|
||||
#endif
|
||||
|
||||
if (sshfs.nodelaysrv_workaround) {
|
||||
/* Hack to work around missing TCP_NODELAY setting in sshd */
|
||||
sshfs.ssh_args.argv[1] = "-X";
|
||||
}
|
||||
|
||||
devnull = open("/dev/null", O_WRONLY);
|
||||
|
||||
if (dup2(outpipe[0], 0) == -1 || dup2(inpipe[1], 1) == -1) {
|
||||
if (dup2(sockpair[1], 0) == -1 || dup2(sockpair[1], 1) == -1) {
|
||||
perror("failed to redirect input/output");
|
||||
_exit(1);
|
||||
}
|
||||
@@ -698,10 +724,8 @@ static int start_ssh(void)
|
||||
dup2(devnull, 2);
|
||||
|
||||
close(devnull);
|
||||
close(inpipe[0]);
|
||||
close(inpipe[1]);
|
||||
close(outpipe[0]);
|
||||
close(outpipe[1]);
|
||||
close(sockpair[0]);
|
||||
close(sockpair[1]);
|
||||
|
||||
switch (fork()) {
|
||||
case -1:
|
||||
@@ -719,8 +743,7 @@ static int start_ssh(void)
|
||||
_exit(1);
|
||||
}
|
||||
waitpid(pid, NULL, 0);
|
||||
close(inpipe[1]);
|
||||
close(outpipe[0]);
|
||||
close(sockpair[1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -758,8 +781,7 @@ static int connect_to(char *host, char *port)
|
||||
|
||||
freeaddrinfo(ai);
|
||||
|
||||
sshfs.infd = sock;
|
||||
sshfs.outfd = sock;
|
||||
sshfs.fd = sock;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -767,7 +789,7 @@ static int do_write(struct iovec *iov, size_t count)
|
||||
{
|
||||
int res;
|
||||
while (count) {
|
||||
res = writev(sshfs.outfd, iov, count);
|
||||
res = writev(sshfs.fd, iov, count);
|
||||
if (res == -1) {
|
||||
perror("write");
|
||||
return -1;
|
||||
@@ -824,7 +846,7 @@ static int sftp_send_iov(uint8_t type, uint32_t id, struct iovec iov[],
|
||||
unsigned nout = 0;
|
||||
|
||||
assert(count <= SFTP_MAX_IOV - 1);
|
||||
buf_init(&buf, 5);
|
||||
buf_init(&buf, 9);
|
||||
buf_add_uint32(&buf, iov_length(iov, count) + 5);
|
||||
buf_add_uint8(&buf, type);
|
||||
buf_add_uint32(&buf, id);
|
||||
@@ -844,7 +866,7 @@ static int do_read(struct buffer *buf)
|
||||
uint8_t *p = buf->p;
|
||||
size_t size = buf->size;
|
||||
while (size) {
|
||||
res = read(sshfs.infd, p, size);
|
||||
res = read(sshfs.fd, p, size);
|
||||
if (res == -1) {
|
||||
perror("read");
|
||||
return -1;
|
||||
@@ -949,8 +971,13 @@ static void *process_requests(void *data_)
|
||||
GUINT_TO_POINTER(id));
|
||||
if (req == NULL)
|
||||
fprintf(stderr, "request %i not found\n", id);
|
||||
else
|
||||
else {
|
||||
int was_over = sshfs.outstanding_len > sshfs.max_outstanding_len;
|
||||
sshfs.outstanding_len -= req->len;
|
||||
if (was_over && sshfs.outstanding_len <= sshfs.max_outstanding_len)
|
||||
pthread_cond_broadcast(&sshfs.outstanding_cond);
|
||||
g_hash_table_remove(sshfs.reqtab, GUINT_TO_POINTER(id));
|
||||
}
|
||||
pthread_mutex_unlock(&sshfs.lock);
|
||||
if (req != NULL) {
|
||||
struct timeval now;
|
||||
@@ -982,10 +1009,8 @@ static void *process_requests(void *data_)
|
||||
} else {
|
||||
pthread_mutex_lock(&sshfs.lock);
|
||||
sshfs.processing_thread_started = 0;
|
||||
close(sshfs.infd);
|
||||
sshfs.infd = -1;
|
||||
close(sshfs.outfd);
|
||||
sshfs.outfd = -1;
|
||||
close(sshfs.fd);
|
||||
sshfs.fd = -1;
|
||||
g_hash_table_foreach_remove(sshfs.reqtab, (GHRFunc) clean_req, NULL);
|
||||
sshfs.connver ++;
|
||||
pthread_mutex_unlock(&sshfs.lock);
|
||||
@@ -993,22 +1018,69 @@ static void *process_requests(void *data_)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int sftp_init_reply_ok(struct buffer *buf, uint32_t *version)
|
||||
{
|
||||
uint32_t len;
|
||||
uint8_t type;
|
||||
|
||||
if (buf_get_uint32(buf, &len) == -1)
|
||||
return -1;
|
||||
|
||||
if (len < 5 || len > MAX_REPLY_LEN)
|
||||
return 1;
|
||||
|
||||
if (buf_get_uint8(buf, &type) == -1)
|
||||
return -1;
|
||||
|
||||
if (type != SSH_FXP_VERSION)
|
||||
return 1;
|
||||
|
||||
if (buf_get_uint32(buf, version) == -1)
|
||||
return -1;
|
||||
|
||||
if (len > 5) {
|
||||
struct buffer buf2;
|
||||
buf_init(&buf2, len - 5);
|
||||
return do_read(&buf2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sftp_find_init_reply(uint32_t *version)
|
||||
{
|
||||
int res;
|
||||
struct buffer buf;
|
||||
|
||||
buf_init(&buf, 9);
|
||||
res = do_read(&buf);
|
||||
while (res != -1) {
|
||||
struct buffer buf2;
|
||||
|
||||
res = sftp_init_reply_ok(&buf, version);
|
||||
if (res <= 0)
|
||||
break;
|
||||
|
||||
/* Iterate over any rubbish until the version reply is found */
|
||||
DEBUG("%c", *buf.p);
|
||||
memmove(buf.p, buf.p + 1, buf.size - 1);
|
||||
buf.len = 0;
|
||||
buf2.p = buf.p + buf.size - 1;
|
||||
buf2.size = 1;
|
||||
res = do_read(&buf2);
|
||||
}
|
||||
buf_free(&buf);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int sftp_init()
|
||||
{
|
||||
int res = -1;
|
||||
uint8_t type;
|
||||
uint32_t version;
|
||||
uint32_t version = 0;
|
||||
struct buffer buf;
|
||||
buf_init(&buf, 0);
|
||||
if (sftp_send_iov(SSH_FXP_INIT, PROTO_VERSION, NULL, 0) == -1)
|
||||
goto out;
|
||||
if (sftp_read(&type, &buf) == -1)
|
||||
goto out;
|
||||
if (type != SSH_FXP_VERSION) {
|
||||
fprintf(stderr, "protocol error\n");
|
||||
goto out;
|
||||
}
|
||||
if (buf_get_uint32(&buf, &version) == -1)
|
||||
if (sftp_find_init_reply(&version) == -1)
|
||||
goto out;
|
||||
|
||||
sshfs.server_version = version;
|
||||
@@ -1023,6 +1095,21 @@ static int sftp_init()
|
||||
return res;
|
||||
}
|
||||
|
||||
static int sftp_error_to_errno(uint32_t error)
|
||||
{
|
||||
switch (error) {
|
||||
case SSH_FX_OK: return 0;
|
||||
case SSH_FX_NO_SUCH_FILE: return ENOENT;
|
||||
case SSH_FX_PERMISSION_DENIED: return EACCES;
|
||||
case SSH_FX_FAILURE: return EPERM;
|
||||
case SSH_FX_BAD_MESSAGE: return EBADMSG;
|
||||
case SSH_FX_NO_CONNECTION: return ENOTCONN;
|
||||
case SSH_FX_CONNECTION_LOST: return ECONNABORTED;
|
||||
case SSH_FX_OP_UNSUPPORTED: return EOPNOTSUPP;
|
||||
default: return EIO;
|
||||
}
|
||||
}
|
||||
|
||||
static void sftp_detect_uid()
|
||||
{
|
||||
int flags;
|
||||
@@ -1077,6 +1164,64 @@ static void sftp_detect_uid()
|
||||
buf_free(&buf);
|
||||
}
|
||||
|
||||
static int sftp_check_root(const char *base_path)
|
||||
{
|
||||
int flags;
|
||||
uint32_t id = sftp_get_id();
|
||||
uint32_t replid;
|
||||
uint8_t type;
|
||||
struct buffer buf;
|
||||
struct stat stbuf;
|
||||
struct iovec iov[1];
|
||||
int err = -1;
|
||||
const char *remote_dir = base_path[0] ? base_path : ".";
|
||||
|
||||
buf_init(&buf, 0);
|
||||
buf_add_string(&buf, remote_dir);
|
||||
buf_to_iov(&buf, &iov[0]);
|
||||
if (sftp_send_iov(SSH_FXP_STAT, id, iov, 1) == -1)
|
||||
goto out;
|
||||
buf_clear(&buf);
|
||||
if (sftp_read(&type, &buf) == -1)
|
||||
goto out;
|
||||
if (type != SSH_FXP_ATTRS && type != SSH_FXP_STATUS) {
|
||||
fprintf(stderr, "protocol error\n");
|
||||
goto out;
|
||||
}
|
||||
if (buf_get_uint32(&buf, &replid) == -1)
|
||||
goto out;
|
||||
if (replid != id) {
|
||||
fprintf(stderr, "bad reply ID\n");
|
||||
goto out;
|
||||
}
|
||||
if (type == SSH_FXP_STATUS) {
|
||||
uint32_t serr;
|
||||
if (buf_get_uint32(&buf, &serr) == -1)
|
||||
goto out;
|
||||
|
||||
fprintf(stderr, "%s:%s: %s\n", sshfs.host, remote_dir,
|
||||
strerror(sftp_error_to_errno(serr)));
|
||||
|
||||
goto out;
|
||||
}
|
||||
if (buf_get_attrs(&buf, &stbuf, &flags) == -1)
|
||||
goto out;
|
||||
|
||||
if (!(flags & SSH_FILEXFER_ATTR_PERMISSIONS))
|
||||
goto out;
|
||||
|
||||
if (!S_ISDIR(stbuf.st_mode)) {
|
||||
fprintf(stderr, "%s:%s: Not a directory\n", sshfs.host, remote_dir);
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = 0;
|
||||
|
||||
out:
|
||||
buf_free(&buf);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int connect_remote(void)
|
||||
{
|
||||
int err;
|
||||
@@ -1101,7 +1246,7 @@ static int start_processing_thread(void)
|
||||
if (sshfs.processing_thread_started)
|
||||
return 0;
|
||||
|
||||
if (sshfs.outfd == -1) {
|
||||
if (sshfs.fd == -1) {
|
||||
err = connect_remote();
|
||||
if (err)
|
||||
return -EIO;
|
||||
@@ -1154,7 +1299,7 @@ static int sftp_request_wait(struct request *req, uint8_t type,
|
||||
err = req->error;
|
||||
goto out;
|
||||
}
|
||||
sem_wait(&req->ready);
|
||||
while (sem_wait(&req->ready));
|
||||
if (req->error) {
|
||||
err = req->error;
|
||||
goto out;
|
||||
@@ -1184,11 +1329,8 @@ static int sftp_request_wait(struct request *req, uint8_t type,
|
||||
err = -EIO;
|
||||
break;
|
||||
|
||||
case SSH_FX_NO_SUCH_FILE: err = -ENOENT; break;
|
||||
case SSH_FX_PERMISSION_DENIED: err = -EACCES; break;
|
||||
case SSH_FX_FAILURE: err = -EPERM; break;
|
||||
case SSH_FX_BAD_MESSAGE:
|
||||
default: err = -EIO; break;
|
||||
default:
|
||||
err = -sftp_error_to_errno(serr);
|
||||
}
|
||||
} else {
|
||||
buf_init(outbuf, req->reply.size - req->reply.len);
|
||||
@@ -1229,6 +1371,11 @@ static int sftp_request_send(uint8_t type, struct iovec *iov, size_t count,
|
||||
pthread_mutex_unlock(&sshfs.lock);
|
||||
goto out;
|
||||
}
|
||||
req->len = iov_length(iov, count) + 9;
|
||||
sshfs.outstanding_len += req->len;
|
||||
while (sshfs.outstanding_len > sshfs.max_outstanding_len)
|
||||
pthread_cond_wait(&sshfs.outstanding_cond, &sshfs.lock);
|
||||
|
||||
g_hash_table_insert(sshfs.reqtab, GUINT_TO_POINTER(id), req);
|
||||
gettimeofday(&req->start, NULL);
|
||||
DEBUG("[%05i] %s\n", id, type_name(type));
|
||||
@@ -1284,7 +1431,8 @@ static int sshfs_getattr(const char *path, struct stat *stbuf)
|
||||
struct buffer outbuf;
|
||||
buf_init(&buf, 0);
|
||||
buf_add_path(&buf, path);
|
||||
err = sftp_request(SSH_FXP_LSTAT, &buf, SSH_FXP_ATTRS, &outbuf);
|
||||
err = sftp_request(sshfs.follow_symlinks ? SSH_FXP_STAT : SSH_FXP_LSTAT,
|
||||
&buf, SSH_FXP_ATTRS, &outbuf);
|
||||
if (!err) {
|
||||
if (buf_get_attrs(&outbuf, stbuf, NULL) == -1)
|
||||
err = -EIO;
|
||||
@@ -1654,6 +1802,7 @@ static int sshfs_open_common(const char *path, mode_t mode,
|
||||
pthread_cond_init(&sf->write_finished, NULL);
|
||||
/* Assume random read after open */
|
||||
sf->is_seq = 0;
|
||||
sf->refs = 1;
|
||||
sf->next_pos = 0;
|
||||
sf->modifver= sshfs.modifver;
|
||||
sf->connver = sshfs.connver;
|
||||
@@ -1666,9 +1815,13 @@ static int sshfs_open_common(const char *path, mode_t mode,
|
||||
sftp_request_send(SSH_FXP_OPEN, &iov, 1, 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;
|
||||
err2 = sftp_request(sshfs.follow_symlinks ? SSH_FXP_STAT : SSH_FXP_LSTAT,
|
||||
&buf, SSH_FXP_ATTRS, &outbuf);
|
||||
if (!err2) {
|
||||
if (buf_get_attrs(&outbuf, &stbuf, NULL) == -1)
|
||||
err2 = -EIO;
|
||||
buf_free(&outbuf);
|
||||
}
|
||||
err = sftp_request_wait(open_req, SSH_FXP_OPEN, SSH_FXP_HANDLE,
|
||||
&sf->handle);
|
||||
if (!err && err2) {
|
||||
@@ -1736,6 +1889,19 @@ static int sshfs_fsync(const char *path, int isdatasync,
|
||||
return sshfs_flush(path, fi);
|
||||
}
|
||||
|
||||
static void sshfs_file_put(struct sshfs_file *sf)
|
||||
{
|
||||
sf->refs--;
|
||||
if (!sf->refs)
|
||||
g_free(sf);
|
||||
}
|
||||
|
||||
static struct sshfs_file *sshfs_file_get(struct sshfs_file *sf)
|
||||
{
|
||||
sf->refs++;
|
||||
return sf;
|
||||
}
|
||||
|
||||
static int sshfs_release(const char *path, struct fuse_file_info *fi)
|
||||
{
|
||||
struct sshfs_file *sf = get_sshfs_file(fi);
|
||||
@@ -1746,7 +1912,7 @@ static int sshfs_release(const char *path, struct fuse_file_info *fi)
|
||||
}
|
||||
buf_free(handle);
|
||||
chunk_put_locked(sf->readahead);
|
||||
g_free(sf);
|
||||
sshfs_file_put(sf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1858,7 +2024,7 @@ static void submit_read(struct sshfs_file *sf, size_t size, off_t offset,
|
||||
static int wait_chunk(struct read_chunk *chunk, char *buf, size_t size)
|
||||
{
|
||||
int res;
|
||||
sem_wait(&chunk->ready);
|
||||
while (sem_wait(&chunk->ready));
|
||||
res = chunk->res;
|
||||
if (res > 0) {
|
||||
if ((size_t) res > size)
|
||||
@@ -1969,6 +2135,7 @@ static void sshfs_write_end(struct request *req)
|
||||
}
|
||||
list_del(&req->list);
|
||||
pthread_cond_broadcast(&sf->write_finished);
|
||||
sshfs_file_put(sf);
|
||||
}
|
||||
|
||||
static int sshfs_write(const char *path, const char *wbuf, size_t size,
|
||||
@@ -1995,7 +2162,7 @@ static int sshfs_write(const char *path, const char *wbuf, size_t size,
|
||||
iov[1].iov_len = size;
|
||||
if (!sshfs.sync_write && !sf->write_error)
|
||||
err = sftp_request_send(SSH_FXP_WRITE, iov, 2, sshfs_write_begin,
|
||||
sshfs_write_end, 0, sf, NULL);
|
||||
sshfs_write_end, 0, sshfs_file_get(sf), NULL);
|
||||
else
|
||||
err = sftp_request_iov(SSH_FXP_WRITE, iov, 2, SSH_FXP_STATUS, NULL);
|
||||
buf_free(&buf);
|
||||
@@ -2009,12 +2176,11 @@ static int sshfs_statfs(const char *path, struct statvfs *buf)
|
||||
|
||||
buf->f_namemax = 255;
|
||||
buf->f_bsize = sshfs.blksize;
|
||||
buf->f_frsize = 512;
|
||||
buf->f_blocks = 999999999 * 2;
|
||||
buf->f_bfree = 999999999 * 2;
|
||||
buf->f_bavail = 999999999 * 2;
|
||||
buf->f_files = 999999999;
|
||||
buf->f_ffree = 999999999;
|
||||
/* df seems to use f_bsize instead of f_frsize, so make them the same */
|
||||
buf->f_frsize = buf->f_bsize;
|
||||
buf->f_blocks = buf->f_bfree = buf->f_bavail =
|
||||
1000ULL * 1024 * 1024 * 1024 / buf->f_frsize;
|
||||
buf->f_files = buf->f_ffree = 1000000000;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
@@ -2023,12 +2189,10 @@ static int sshfs_statfs(const char *path, struct statfs *buf)
|
||||
(void) path;
|
||||
|
||||
buf->f_namelen = 255;
|
||||
buf->f_bsize = 512;
|
||||
buf->f_blocks = 999999999 * 2;
|
||||
buf->f_bfree = 999999999 * 2;
|
||||
buf->f_bavail = 999999999 * 2;
|
||||
buf->f_files = 999999999;
|
||||
buf->f_ffree = 999999999;
|
||||
buf->f_bsize = sshfs.blksize;
|
||||
buf->f_blocks = buf->f_bfree = buf->f_bavail =
|
||||
1000ULL * 1024 * 1024 * 1024 / buf->f_bsize;
|
||||
buf->f_files = buf->f_ffree = 1000000000;
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -2218,6 +2382,7 @@ static int processing_init(void)
|
||||
{
|
||||
pthread_mutex_init(&sshfs.lock, NULL);
|
||||
pthread_mutex_init(&sshfs.lock_write, NULL);
|
||||
pthread_cond_init(&sshfs.outstanding_cond, NULL);
|
||||
sshfs.reqtab = g_hash_table_new(NULL, NULL);
|
||||
if (!sshfs.reqtab) {
|
||||
fprintf(stderr, "failed to create hash table\n");
|
||||
@@ -2284,8 +2449,12 @@ static void usage(const char *progname)
|
||||
" none no workarounds enabled\n"
|
||||
" all all workarounds enabled\n"
|
||||
" [no]rename fix renaming to existing file (default: off)\n"
|
||||
#ifdef SSH_NODELAY_WORKAROUND
|
||||
" [no]nodelay set nodelay tcp flag in ssh (default: on)\n"
|
||||
#endif
|
||||
" [no]nodelaysrv set nodelay tcp flag in sshd (default: on)\n"
|
||||
" [no]truncate fix truncate for old servers (default: off)\n"
|
||||
" [no]buflimit fix buffer fillup bug in server (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"
|
||||
@@ -2294,6 +2463,8 @@ static void usage(const char *progname)
|
||||
" -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 transform absolute symlinks to relative\n"
|
||||
" -o follow_symlinks follow symlinks on the server\n"
|
||||
" -o no_check_root don't check for existence of 'dir' on server\n"
|
||||
" -o SSHOPT=VAL ssh options (see man ssh_config)\n"
|
||||
"\n", progname);
|
||||
}
|
||||
@@ -2313,6 +2484,15 @@ static int is_ssh_opt(const char *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sshfs_fuse_main(struct fuse_args *args)
|
||||
{
|
||||
#if FUSE_VERSION >= 26
|
||||
return fuse_main(args->argc, args->argv, cache_init(&sshfs_oper), NULL);
|
||||
#else
|
||||
return fuse_main(args->argc, args->argv, cache_init(&sshfs_oper));
|
||||
#endif
|
||||
}
|
||||
|
||||
static int sshfs_opt_proc(void *data, const char *arg, int key,
|
||||
struct fuse_args *outargs)
|
||||
{
|
||||
@@ -2349,14 +2529,14 @@ static int sshfs_opt_proc(void *data, const char *arg, int key,
|
||||
case KEY_HELP:
|
||||
usage(outargs->argv[0]);
|
||||
fuse_opt_add_arg(outargs, "-ho");
|
||||
fuse_main(outargs->argc, outargs->argv, &sshfs_oper.oper);
|
||||
sshfs_fuse_main(outargs);
|
||||
exit(1);
|
||||
|
||||
case KEY_VERSION:
|
||||
fprintf(stderr, "SSHFS version %s\n", PACKAGE_VERSION);
|
||||
#if FUSE_VERSION >= 25
|
||||
fuse_opt_add_arg(outargs, "--version");
|
||||
fuse_main(outargs->argc, outargs->argv, &sshfs_oper.oper);
|
||||
sshfs_fuse_main(outargs);
|
||||
#endif
|
||||
exit(0);
|
||||
|
||||
@@ -2428,11 +2608,15 @@ int main(int argc, char *argv[])
|
||||
char *base_path;
|
||||
const char *sftp_server;
|
||||
|
||||
g_thread_init(NULL);
|
||||
|
||||
sshfs.blksize = 4096;
|
||||
sshfs.max_read = 65536;
|
||||
sshfs.nodelay_workaround = 1;
|
||||
sshfs.nodelaysrv_workaround = 0;
|
||||
sshfs.rename_workaround = 0;
|
||||
sshfs.truncate_workaround = 0;
|
||||
sshfs.buflimit_workaround = 1;
|
||||
sshfs.ssh_ver = 2;
|
||||
sshfs.progname = argv[0];
|
||||
ssh_add_arg("ssh");
|
||||
@@ -2444,6 +2628,14 @@ int main(int argc, char *argv[])
|
||||
parse_workarounds() == -1)
|
||||
exit(1);
|
||||
|
||||
if (sshfs.buflimit_workaround)
|
||||
/* Work around buggy sftp-server in OpenSSH. Without this on
|
||||
a slow server a 10Mbyte buffer would fill up and the server
|
||||
would abort */
|
||||
sshfs.max_outstanding_len = 8388608;
|
||||
else
|
||||
sshfs.max_outstanding_len = ~0;
|
||||
|
||||
if (!sshfs.host) {
|
||||
fprintf(stderr, "missing host\n");
|
||||
fprintf(stderr, "see `%s -h' for usage\n", argv[0]);
|
||||
@@ -2492,6 +2684,9 @@ int main(int argc, char *argv[])
|
||||
sftp_detect_uid();
|
||||
#endif
|
||||
|
||||
if (!sshfs.no_check_root && sftp_check_root(base_path) == -1)
|
||||
exit(1);
|
||||
|
||||
res = cache_parse_options(&args);
|
||||
if (res == -1)
|
||||
exit(1);
|
||||
@@ -2511,7 +2706,7 @@ int main(int argc, char *argv[])
|
||||
check_large_read(&args);
|
||||
g_free(tmp);
|
||||
g_free(fsname);
|
||||
res = fuse_main(args.argc, args.argv, cache_init(&sshfs_oper));
|
||||
res = sshfs_fuse_main(&args);
|
||||
fuse_opt_free_args(&args);
|
||||
fuse_opt_free_args(&sshfs.ssh_args);
|
||||
free(sshfs.directport);
|
||||
|
||||
Reference in New Issue
Block a user