Compare commits

...

18 Commits

Author SHA1 Message Date
Miklos Szeredi 09e37d536f Released 2.2 2008-10-20 12:15:07 +00:00
Miklos Szeredi be44229cfe Escape commas in fsname option if libfuse supports it 2008-10-20 12:03:16 +00:00
Miklos Szeredi c7dbdc1f77 Fix harmless glib assertations with "cache=no" 2008-10-08 18:23:33 +00:00
Miklos Szeredi 8a4015a3b6 * Handle numerical IPv6 addresses enclosed in square brackets. Reported by Andre-John Mas * Fix error if username contains a comma character. Reported by Yang Zhang 2008-10-08 11:07:48 +00:00
Miklos Szeredi fd52ef3e51 Released 2.1 2008-07-11 10:59:12 +00:00
Miklos Szeredi 1abdbe64a3 Add '-F' option to specify the ssh config file 2008-06-24 15:24:01 +00:00
Miklos Szeredi 4b28b15c34 Fix bug in caching which could cause file corruption for append mode writes 2008-05-06 15:10:28 +00:00
Miklos Szeredi 1a76a16bd9 man page update from Bartosz 2008-04-25 06:39:37 +00:00
Miklos Szeredi b1748d87aa Add manual page 2008-04-23 12:12:25 +00:00
Miklos Szeredi ff1a5c7a71 Add manual page 2008-04-23 12:07:39 +00:00
Miklos Szeredi 09579af980 Released 2.0 2008-04-22 16:01:59 +00:00
Miklos Szeredi eeea0ff5a6 Support "statvfs@openssh.com" extension... 2008-04-22 14:25:19 +00:00
Miklos Szeredi 4b88fc4731 Limit write requests to 64kB 2008-04-22 13:36:26 +00:00
Miklos Szeredi b246ad3da0 Allow the '-ossh_command=CMD' command to contain parameters... 2008-04-22 13:00:14 +00:00
Miklos Szeredi c61e700aab Fix incorrect disk usage reported by 'du' for files of size 4GB or above 2008-04-22 10:20:05 +00:00
Miklos Szeredi 4d6e5a03da Support "posix-rename@openssh.com" extension... 2008-03-31 11:30:43 +00:00
Miklos Szeredi 4802b14791 Add support for password authentication with pam_mount. 2008-03-28 19:22:19 +00:00
Miklos Szeredi 03381402b2 *** empty log message *** 2008-03-27 16:18:09 +00:00
8 changed files with 977 additions and 40 deletions
+100
View File
@@ -1,3 +1,103 @@
2008-10-20 Miklos Szeredi <miklos@szeredi.hu>
* Released 2.2
2008-10-20 Miklos Szeredi <miklos@szeredi.hu>
* Escape commas in fsname option if libfuse supports it
2008-10-08 Miklos Szeredi <miklos@szeredi.hu>
* Handle numerical IPv6 addresses enclosed in square brackets.
Reported by Andre-John Mas
* Fix error if username contains a comma character. Reported by
Yang Zhang
* Fix harmless glib assertations with "cache=no"
2008-07-11 Miklos Szeredi <miklos@szeredi.hu>
* Released 2.1
2008-07-11 Miklos Szeredi <miklos@szeredi.hu>
* Fix statvfs extension to match the current protocol in
opensshfs
* Check version numbers of extensions, so such changes wouldn't
cause stupid behavior
2008-06-24 Miklos Szeredi <miklos@szeredi.hu>
* Add '-F' option to specify the ssh config file. Patch by Pat
Pascal.
2008-05-06 Miklos Szeredi <miklos@szeredi.hu>
* Fix bug in caching which could cause file corruption for append
mode writes. Reported by Jose Alonso
2008-05-05 Miklos Szeredi <miklos@szeredi.hu>
* Fix compile on OS X. Original patch from Michael G Schwern
* Fix compile on Solaris. Reported by Jean-Jacques Sarton
2008-04-23 Miklos Szeredi <miklos@szeredi.hu>
* Released 2.0
2008-04-23 Miklos Szeredi <miklos@szeredi.hu>
* Add manual page. Written by Bartosz Fenski
2008-04-22 Miklos Szeredi <miklos@szeredi.hu>
* Add missing ssh options: ControlMaster, ControlPath,
KbdInteractiveAuthentication, KbdInteractiveDevices, LocalCommand,
RekeyLimit (Debian bug #430225).
* Allow the '-ossh_command=CMD' command to contain parameters.
Escape charater is backslash.
* Limit write requests to 64kB.
* Support "statvfs@openssh.com" extension, which will be available
in OpenSSH 5.1.
2008-04-21 Miklos Szeredi <miklos@szeredi.hu>
* Fix incorrect disk usage reported by 'du' for files of size 4GB
or above. Reported by Christian Boltz.
2008-04-16 Miklos Szeredi <miklos@szeredi.hu>
* If debugging is enabled, print some statistics at exit about the
number of bytes transferred, etc..
2008-03-31 Miklos Szeredi <miklos@szeredi.hu>
* Support "posix-rename@openssh.com" extension available in
OpenSSH 4.9. This allows rename to be atomic even when target
file or directory exists. If available, the extension will be
used instead of the rename operation in the original protocol and
the "-oworkaround=rename" option will be ignored.
2008-03-28 Miklos Szeredi <miklos@szeredi.hu>
* Add support for password authentication with pam_mount.
Original patch and help with testing: John S. Skogtvedt
2008-03-03 Miklos Szeredi <miklos@szeredi.hu>
* Fix ssh debug only appearing if "-d" is used. Reported by
Michael Gorbach
2008-01-03 Miklos Szeredi <miklos@szeredi.hu>
* Fix condition for building of sshnodelay.so
2007-12-17 Miklos Szeredi <miklos@szeredi.hu>
* Released 1.9
+2
View File
@@ -14,6 +14,8 @@ sshfs_CPPFLAGS = -D_REENTRANT -DFUSE_USE_VERSION=26 -DLIBDIR=\"$(libdir)\"
EXTRA_DIST = sshnodelay.c FAQ.txt
CLEANFILES = sshnodelay.so
dist_man_MANS = sshfs.1
if SSH_NODELAY_SO
all-local: sshnodelay.so
+11
View File
@@ -1,3 +1,14 @@
What is new in 2.0
------------------
* Support password authentication with pam_mount
* Support atomic renames if server is OpenSSH 4.9 or later
* Support getting disk usage if server is OpenSSH 5.1 or later
* Small enhancements and bug fixes
What is new in 1.9
------------------
+44 -12
View File
@@ -28,6 +28,7 @@ struct cache {
GHashTable *table;
pthread_mutex_t lock;
time_t last_cleaned;
uint64_t write_ctr;
};
static struct cache cache;
@@ -47,6 +48,7 @@ struct fuse_cache_dirhandle {
fuse_dirh_t h;
fuse_dirfil_t filler;
GPtrArray *dir;
uint64_t wrctr;
};
static void free_node(gpointer node_)
@@ -103,11 +105,22 @@ static void cache_purge_parent(const char *path)
void cache_invalidate(const char *path)
{
if (!cache.on)
return;
pthread_mutex_lock(&cache.lock);
cache_purge(path);
pthread_mutex_unlock(&cache.lock);
}
void cache_invalidate_write(const char *path)
{
pthread_mutex_lock(&cache.lock);
cache_purge(path);
cache.write_ctr++;
pthread_mutex_unlock(&cache.lock);
}
static void cache_invalidate_dir(const char *path)
{
pthread_mutex_lock(&cache.lock);
@@ -148,19 +161,24 @@ static struct node *cache_get(const char *path)
return node;
}
void cache_add_attr(const char *path, const struct stat *stbuf)
void cache_add_attr(const char *path, const struct stat *stbuf, uint64_t wrctr)
{
struct node *node;
time_t now;
if (!cache.on)
return;
pthread_mutex_lock(&cache.lock);
node = cache_get(path);
now = time(NULL);
node->stat = *stbuf;
node->stat_valid = time(NULL) + cache.stat_timeout;
if (node->stat_valid > node->valid)
node->valid = node->stat_valid;
cache_clean();
if (wrctr == cache.write_ctr) {
node = cache_get(path);
now = time(NULL);
node->stat = *stbuf;
node->stat_valid = time(NULL) + cache.stat_timeout;
if (node->stat_valid > node->valid)
node->valid = node->stat_valid;
cache_clean();
}
pthread_mutex_unlock(&cache.lock);
}
@@ -222,13 +240,25 @@ static int cache_get_attr(const char *path, struct stat *stbuf)
return err;
}
uint64_t cache_get_write_ctr(void)
{
uint64_t res;
pthread_mutex_lock(&cache.lock);
res = cache.write_ctr;
pthread_mutex_unlock(&cache.lock);
return res;
}
static int cache_getattr(const char *path, struct stat *stbuf)
{
int err = cache_get_attr(path, stbuf);
if (err) {
uint64_t wrctr = cache_get_write_ctr();
err = cache.next_oper->oper.getattr(path, stbuf);
if (!err)
cache_add_attr(path, stbuf);
cache_add_attr(path, stbuf, wrctr);
}
return err;
}
@@ -268,7 +298,7 @@ static int cache_dirfill(fuse_cache_dirh_t ch, const char *name,
const char *basepath = !ch->path[1] ? "" : ch->path;
fullpath = g_strdup_printf("%s/%s", basepath, name);
cache_add_attr(fullpath, stbuf);
cache_add_attr(fullpath, stbuf, ch->wrctr);
g_free(fullpath);
}
}
@@ -299,6 +329,7 @@ static int cache_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler)
ch.h = h;
ch.filler = filler;
ch.dir = g_ptr_array_new();
ch.wrctr = cache_get_write_ctr();
err = cache.next_oper->cache_getdir(path, &ch, cache_dirfill);
g_ptr_array_add(ch.dir, NULL);
dir = (char **) ch.dir->pdata;
@@ -421,7 +452,7 @@ static int cache_write(const char *path, const char *buf, size_t size,
{
int res = cache.next_oper->oper.write(path, buf, size, offset, fi);
if (res >= 0)
cache_invalidate(path);
cache_invalidate_write(path);
return res;
}
@@ -449,9 +480,10 @@ static int cache_fgetattr(const char *path, struct stat *stbuf,
{
int err = cache_get_attr(path, stbuf);
if (err) {
uint64_t wrctr = cache_get_write_ctr();
err = cache.next_oper->oper.fgetattr(path, stbuf, fi);
if (!err)
cache_add_attr(path, stbuf);
cache_add_attr(path, stbuf, wrctr);
}
return err;
}
+2 -1
View File
@@ -24,5 +24,6 @@ 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_add_attr(const char *path, const struct stat *stbuf, uint64_t wrctr);
void cache_invalidate(const char *path);
uint64_t cache_get_write_ctr(void);
+3 -3
View File
@@ -1,4 +1,4 @@
AC_INIT(sshfs-fuse, 1.9)
AC_INIT(sshfs-fuse, 2.2)
AM_INIT_AUTOMAKE
AM_CONFIG_HEADER(config.h)
@@ -30,11 +30,11 @@ if test -z "$enable_sshnodelay"; then
fi
fi
if test "$enable_sshnodelay" != "no"; then
if test "$enable_sshnodelay" = "yes"; then
AC_DEFINE(SSH_NODELAY_WORKAROUND, 1, [Compile ssh NODELAY workaround])
fi
AM_CONDITIONAL(SSH_NODELAY_SO, test "$enable_sshnodelay" != "no")
AM_CONDITIONAL(SSH_NODELAY_SO, test "$enable_sshnodelay" = "yes")
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH
PKG_CHECK_MODULES(SSHFS, [fuse >= 2.2 glib-2.0 gthread-2.0])
+246
View File
@@ -0,0 +1,246 @@
.TH SSHFS "1" "April 2008" "SSHFS version 2.0" "User Commands"
.SH NAME
SSHFS \- filesystem client based on ssh
.SH SYNOPSIS
.SS mounting
.TP
\fBsshfs\fP [\fIuser\fP@]\fBhost\fP:[\fIdir\fP] \fBmountpoint\fP [\fIoptions\fP]
.SS unmounting
.TP
\fBfusermount -u mountpoint\fP
.SH DESCRIPTION
SSHFS (Secure SHell FileSystem) is a file system for Linux (and other
operating systems with a FUSE implementation, such as Mac OS X or FreeBSD)
capable of operating on files on a remote computer using just a secure
shell login on the remote computer. On the local computer where the SSHFS
is mounted, the implementation makes use of the FUSE (Filesystem in
Userspace) kernel module. The practical effect of this is that the end user
can seamlessly interact with remote files being securely served over SSH
just as if they were local files on his/her computer. On the remote
computer the SFTP subsystem of SSH is used.
.SH OPTIONS
.SS "general options:"
.TP
\fB\-o\fR opt,[opt...]
mount options
.TP
\fB\-h\fR \fB\-\-help\fR
print help
.TP
\fB\-V\fR \fB\-\-version\fR
print version
.SS "SSHFS options:"
.TP
\fB\-p\fR PORT
equivalent to '\-o port=PORT'
.TP
\fB\-C\fR
equivalent to '\-o compression=yes'
.TP
\fB\-F\fR ssh_configfile
specifies alternative ssh configuration file
.TP
\fB\-1\fR
equivalent to '\-o ssh_protocol=1'
.TP
\fB\-o\fR reconnect
reconnect to server
.TP
\fB\-o\fR sshfs_sync
synchronous writes
.TP
\fB\-o\fR no_readahead
synchronous reads (no speculative readahead)
.TP
\fB\-o\fR sshfs_debug
print some debugging information
.TP
\fB\-o\fR cache=YESNO
enable caching {yes,no} (default: yes)
.TP
\fB\-o\fR cache_timeout=N
sets timeout for caches in seconds (default: 20)
.TP
\fB\-o\fR cache_X_timeout=N
sets timeout for {stat,dir,link} cache
.TP
\fB\-o\fR workaround=LIST
colon separated list of workarounds
.RS 8
.TP
none
no workarounds enabled
.TP
all
all workarounds enabled
.TP
[no]rename
fix renaming to existing file (default: off)
.TP
[no]nodelaysrv
set nodelay tcp flag in ssh (default: off)
.TP
[no]truncate
fix truncate for old servers (default: off)
.TP
[no]buflimit
fix buffer fillup bug in server (default: on)
.RE
.TP
\fB\-o\fR idmap=TYPE
user/group ID mapping, possible types are:
.RS 8
.TP
none
no translation of the ID space (default)
.TP
user
only translate UID of connecting user
.RE
.TP
\fB\-o\fR ssh_command=CMD
execute CMD instead of 'ssh'
.TP
\fB\-o\fR ssh_protocol=N
ssh protocol to use (default: 2)
.TP
\fB\-o\fR sftp_server=SERV
path to sftp server or subsystem (default: sftp)
.TP
\fB\-o\fR directport=PORT
directly connect to PORT bypassing ssh
.TP
\fB\-o\fR transform_symlinks
transform absolute symlinks to relative
.TP
\fB\-o\fR follow_symlinks
follow symlinks on the server
.TP
\fB\-o\fR no_check_root
don't check for existence of 'dir' on server
.TP
\fB\-o\fR password_stdin
read password from stdin (only for pam_mount!)
.TP
\fB\-o\fR SSHOPT=VAL
ssh options (see man ssh_config)
.SS "FUSE options:"
.TP
\fB\-d\fR \fB\-o\fR debug
enable debug output (implies \fB\-f\fR)
.TP
\fB\-f\fR
foreground operation
.TP
\fB\-s\fR
disable multi\-threaded operation
.TP
\fB\-o\fR allow_other
allow access to other users
.TP
\fB\-o\fR allow_root
allow access to root
.TP
\fB\-o\fR nonempty
allow mounts over non\-empty file/dir
.HP
\fB\-o\fR default_permissions
enable permission checking by kernel
.TP
\fB\-o\fR fsname=NAME
set filesystem name
.TP
\fB\-o\fR subtype=NAME
set filesystem type
.TP
\fB\-o\fR large_read
issue large read requests (2.4 only)
.TP
\fB\-o\fR max_read=N
set maximum size of read requests
.TP
\fB\-o\fR hard_remove
immediate removal (don't hide files)
.TP
\fB\-o\fR use_ino
let filesystem set inode numbers
.TP
\fB\-o\fR readdir_ino
try to fill in d_ino in readdir
.TP
\fB\-o\fR direct_io
use direct I/O
.TP
\fB\-o\fR kernel_cache
cache files in kernel
.TP
\fB\-o\fR [no]auto_cache
enable caching based on modification times
.TP
\fB\-o\fR umask=M
set file permissions (octal)
.TP
\fB\-o\fR uid=N
set file owner
.TP
\fB\-o\fR gid=N
set file group
.TP
\fB\-o\fR entry_timeout=T
cache timeout for names (1.0s)
.TP
\fB\-o\fR negative_timeout=T
cache timeout for deleted names (0.0s)
.TP
\fB\-o\fR attr_timeout=T
cache timeout for attributes (1.0s)
.TP
\fB\-o\fR ac_attr_timeout=T
auto cache timeout for attributes (attr_timeout)
.TP
\fB\-o\fR intr
allow requests to be interrupted
.TP
\fB\-o\fR intr_signal=NUM
signal to send on interrupt (10)
.TP
\fB\-o\fR modules=M1[:M2...]
names of modules to push onto filesystem stack
.TP
\fB\-o\fR max_write=N
set maximum size of write requests
.TP
\fB\-o\fR max_readahead=N
set maximum readahead
.TP
\fB\-o\fR async_read
perform reads asynchronously (default)
.TP
\fB\-o\fR sync_read
perform reads synchronously
.SS "Module options:"
.TP
[subdir]
.TP
\fB\-o\fR subdir=DIR
prepend this directory to all paths (mandatory)
.TP
\fB\-o\fR [no]rellinksa
transform absolute symlinks to relative
.TP
[iconv]
.TP
\fB\-o\fR from_code=CHARSET
original encoding of file names (default: UTF-8)
.TP
\fB\-o\fR to_code=CHARSET
new encoding of the file names (default: ISO-8859-2)
.PD
.SH "AUTHORS"
.LP
SSHFS has been written by Miklos Seredi <miklos@szeredi.hu>.
.LP
This man page was written by Bartosz Fenski <fenio@debian.org> for the
Debian GNU/Linux distribution (but it may be used by others).
+569 -24
View File
@@ -6,6 +6,7 @@
See the file COPYING.
*/
#define _GNU_SOURCE /* avoid implicit declaration of *pt* functions */
#include "config.h"
#include <fuse.h>
@@ -23,16 +24,28 @@
#include <netdb.h>
#include <signal.h>
#include <sys/uio.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/utsname.h>
#include <sys/mman.h>
#include <sys/poll.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <glib.h>
#include "cache.h"
#ifndef MAP_LOCKED
#define MAP_LOCKED 0
#endif
#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
#define MAP_ANONYMOUS MAP_ANON
#endif
#if FUSE_VERSION >= 23
#define SSHFS_USE_INIT
#endif
@@ -88,6 +101,13 @@
#define SSH_FXF_TRUNC 0x00000010
#define SSH_FXF_EXCL 0x00000020
/* statvfs@openssh.com f_flag flags */
#define SSH2_FXE_STATVFS_ST_RDONLY 0x00000001
#define SSH2_FXE_STATVFS_ST_NOSUID 0x00000002
#define SFTP_EXT_POSIX_RENAME "posix-rename@openssh.com"
#define SFTP_EXT_STATVFS "statvfs@openssh.com"
#define PROTO_VERSION 3
#define MY_EOF 1
@@ -167,10 +187,12 @@ struct sshfs {
int no_check_root;
int detect_uid;
unsigned max_read;
unsigned max_write;
unsigned ssh_ver;
int sync_write;
int sync_read;
int debug;
int foreground;
int reconnect;
char *host;
char *base_path;
@@ -180,6 +202,8 @@ struct sshfs {
int processing_thread_started;
unsigned int randseed;
int fd;
int ptyfd;
int ptyslavefd;
int connver;
int server_version;
unsigned remote_uid;
@@ -191,6 +215,20 @@ struct sshfs {
unsigned outstanding_len;
unsigned max_outstanding_len;
pthread_cond_t outstanding_cond;
int password_stdin;
char *password;
int ext_posix_rename;
int ext_statvfs;
/* statistics */
uint64_t bytes_sent;
uint64_t bytes_received;
uint64_t num_sent;
uint64_t num_received;
unsigned int min_rtt;
unsigned int max_rtt;
uint64_t total_rtt;
unsigned int num_connect;
};
static struct sshfs sshfs;
@@ -207,6 +245,8 @@ static const char *ssh_opts[] = {
"CompressionLevel",
"ConnectionAttempts",
"ConnectTimeout",
"ControlMaster",
"ControlPath",
"GlobalKnownHostsFile",
"GSSAPIAuthentication",
"GSSAPIDelegateCredentials",
@@ -214,8 +254,11 @@ static const char *ssh_opts[] = {
"HostKeyAlgorithms",
"HostKeyAlias",
"HostName",
"IdentityFile",
"IdentitiesOnly",
"IdentityFile",
"KbdInteractiveAuthentication",
"KbdInteractiveDevices",
"LocalCommand",
"LogLevel",
"MACs",
"NoHostAuthenticationForLocalhost",
@@ -225,10 +268,11 @@ static const char *ssh_opts[] = {
"PreferredAuthentications",
"ProxyCommand",
"PubkeyAuthentication",
"RekeyLimit",
"RhostsRSAAuthentication",
"RSAAuthentication",
"ServerAliveInterval",
"ServerAliveCountMax",
"ServerAliveInterval",
"SmartcardDevice",
"StrictHostKeyChecking",
"TCPKeepAlive",
@@ -243,6 +287,8 @@ enum {
KEY_COMPRESS,
KEY_HELP,
KEY_VERSION,
KEY_FOREGROUND,
KEY_CONFIGFILE,
};
#define SSHFS_OPT(t, p, v) { t, offsetof(struct sshfs, p), v }
@@ -252,6 +298,7 @@ static struct fuse_opt sshfs_opts[] = {
SSHFS_OPT("ssh_command=%s", ssh_command, 0),
SSHFS_OPT("sftp_server=%s", sftp_server, 0),
SSHFS_OPT("max_read=%u", max_read, 0),
SSHFS_OPT("max_write=%u", max_write, 0),
SSHFS_OPT("ssh_protocol=%u", ssh_ver, 0),
SSHFS_OPT("-1", ssh_ver, 1),
SSHFS_OPT("workaround=%s", workarounds, 0),
@@ -264,6 +311,7 @@ static struct fuse_opt sshfs_opts[] = {
SSHFS_OPT("transform_symlinks", transform_symlinks, 1),
SSHFS_OPT("follow_symlinks", follow_symlinks, 1),
SSHFS_OPT("no_check_root", no_check_root, 1),
SSHFS_OPT("password_stdin", password_stdin, 1),
FUSE_OPT_KEY("-p ", KEY_PORT),
FUSE_OPT_KEY("-C", KEY_COMPRESS),
@@ -271,6 +319,10 @@ static struct fuse_opt sshfs_opts[] = {
FUSE_OPT_KEY("--version", KEY_VERSION),
FUSE_OPT_KEY("-h", KEY_HELP),
FUSE_OPT_KEY("--help", KEY_HELP),
FUSE_OPT_KEY("debug", KEY_FOREGROUND),
FUSE_OPT_KEY("-d", KEY_FOREGROUND),
FUSE_OPT_KEY("-f", KEY_FOREGROUND),
FUSE_OPT_KEY("-F ", KEY_CONFIGFILE),
FUSE_OPT_END
};
@@ -599,7 +651,7 @@ static int buf_get_attrs(struct buffer *buf, struct stat *stbuf, int *flagsp)
if (sshfs.blksize) {
stbuf->st_blksize = sshfs.blksize;
stbuf->st_blocks = ((size + sshfs.blksize - 1) &
~(sshfs.blksize - 1)) >> 9;
~((unsigned long long) sshfs.blksize - 1)) >> 9;
}
stbuf->st_uid = uid;
stbuf->st_gid = gid;
@@ -608,6 +660,48 @@ static int buf_get_attrs(struct buffer *buf, struct stat *stbuf, int *flagsp)
return 0;
}
static int buf_get_statvfs(struct buffer *buf, struct statvfs *stbuf)
{
uint64_t bsize;
uint64_t frsize;
uint64_t blocks;
uint64_t bfree;
uint64_t bavail;
uint64_t files;
uint64_t ffree;
uint64_t favail;
uint64_t fsid;
uint64_t flag;
uint64_t namemax;
if (buf_get_uint64(buf, &bsize) == -1 ||
buf_get_uint64(buf, &frsize) == -1 ||
buf_get_uint64(buf, &blocks) == -1 ||
buf_get_uint64(buf, &bfree) == -1 ||
buf_get_uint64(buf, &bavail) == -1 ||
buf_get_uint64(buf, &files) == -1 ||
buf_get_uint64(buf, &ffree) == -1 ||
buf_get_uint64(buf, &favail) == -1 ||
buf_get_uint64(buf, &fsid) == -1 ||
buf_get_uint64(buf, &flag) == -1 ||
buf_get_uint64(buf, &namemax) == -1) {
return -1;
}
memset(stbuf, 0, sizeof(struct statvfs));
stbuf->f_bsize = bsize;
stbuf->f_frsize = frsize;
stbuf->f_blocks = blocks;
stbuf->f_bfree = bfree;
stbuf->f_bavail = bavail;
stbuf->f_files = files;
stbuf->f_ffree = ffree;
stbuf->f_favail = favail;
stbuf->f_namemax = namemax;
return 0;
}
static int buf_get_entries(struct buffer *buf, fuse_cache_dirh_t h,
fuse_cache_dirfil_t filler)
{
@@ -695,11 +789,123 @@ static int do_ssh_nodelay_workaround(void)
}
#endif
static int pty_expect_loop(void)
{
int res;
char buf[256];
const char *passwd_str = "assword:";
int timeout = 60 * 1000; /* 1min timeout for the prompt to appear */
int passwd_len = strlen(passwd_str);
int len = 0;
char c;
while (1) {
struct pollfd fds[2];
fds[0].fd = sshfs.fd;
fds[0].events = POLLIN;
fds[1].fd = sshfs.ptyfd;
fds[1].events = POLLIN;
res = poll(fds, 2, timeout);
if (res == -1) {
perror("poll");
return -1;
}
if (res == 0) {
fprintf(stderr, "Timeout waiting for prompt\n");
return -1;
}
if (fds[0].revents) {
/*
* Something happened on stdout of ssh, this
* either means, that we are connected, or
* that we are disconnected. In any case the
* password doesn't matter any more.
*/
break;
}
res = read(sshfs.ptyfd, &c, 1);
if (res == -1) {
perror("read");
return -1;
}
if (res == 0) {
fprintf(stderr, "EOF while waiting for prompt\n");
return -1;
}
buf[len] = c;
len++;
if (len == passwd_len) {
if (memcmp(buf, passwd_str, passwd_len) == 0) {
write(sshfs.ptyfd, sshfs.password,
strlen(sshfs.password));
}
memmove(buf, buf + 1, passwd_len - 1);
len--;
}
}
if (!sshfs.reconnect) {
size_t size = getpagesize();
memset(sshfs.password, 0, size);
munmap(sshfs.password, size);
sshfs.password = NULL;
}
return 0;
}
static int pty_master(char **name)
{
int mfd;
mfd = open("/dev/ptmx", O_RDWR | O_NOCTTY);
if (mfd == -1) {
perror("failed to open pty");
return -1;
}
if (grantpt(mfd) != 0) {
perror("grantpt");
return -1;
}
if (unlockpt(mfd) != 0) {
perror("unlockpt");
return -1;
}
*name = ptsname(mfd);
return mfd;
}
static void replace_arg(char **argp, const char *newarg)
{
free(*argp);
*argp = strdup(newarg);
if (*argp == NULL) {
fprintf(stderr, "sshfs: memory allocation failed\n");
abort();
}
}
static int start_ssh(void)
{
char *ptyname = NULL;
int sockpair[2];
int pid;
if (sshfs.password_stdin) {
sshfs.ptyfd = pty_master(&ptyname);
if (sshfs.ptyfd == -1)
return -1;
sshfs.ptyslavefd = open(ptyname, O_RDWR | O_NOCTTY);
if (sshfs.ptyslavefd == -1)
return -1;
}
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockpair) == -1) {
perror("failed to create socket pair");
return -1;
@@ -709,6 +915,7 @@ static int start_ssh(void)
pid = fork();
if (pid == -1) {
perror("failed to fork");
close(sockpair[1]);
return -1;
} else if (pid == 0) {
int devnull;
@@ -722,11 +929,18 @@ static int start_ssh(void)
#endif
if (sshfs.nodelaysrv_workaround) {
int i;
/*
* Hack to work around missing TCP_NODELAY
* setting in sshd
*/
sshfs.ssh_args.argv[1] = "-X";
for (i = 1; i < sshfs.ssh_args.argc; i++) {
if (strcmp(sshfs.ssh_args.argv[i], "-x") == 0) {
replace_arg(&sshfs.ssh_args.argv[i],
"-X");
break;
}
}
}
devnull = open("/dev/null", O_WRONLY);
@@ -735,7 +949,7 @@ static int start_ssh(void)
perror("failed to redirect input/output");
_exit(1);
}
if (!sshfs.debug && devnull != -1)
if (!sshfs.foreground && devnull != -1)
dup2(devnull, 2);
close(devnull);
@@ -753,8 +967,33 @@ static int start_ssh(void)
}
chdir("/");
if (sshfs.password_stdin) {
int sfd;
setsid();
sfd = open(ptyname, O_RDWR);
if (sfd == -1) {
perror(ptyname);
_exit(1);
}
close(sfd);
close(sshfs.ptyslavefd);
close(sshfs.ptyfd);
}
if (sshfs.debug) {
int i;
fprintf(stderr, "executing");
for (i = 0; i < sshfs.ssh_args.argc; i++)
fprintf(stderr, " <%s>",
sshfs.ssh_args.argv[i]);
fprintf(stderr, "\n");
}
execvp(sshfs.ssh_args.argv[0], sshfs.ssh_args.argv);
perror("execvp");
fprintf(stderr, "failed to execute '%s': %s\n",
sshfs.ssh_args.argv[0], strerror(errno));
_exit(1);
}
waitpid(pid, NULL, 0);
@@ -999,12 +1238,21 @@ static int process_one_request(void)
if (sshfs.debug) {
struct timeval now;
unsigned int difftime;
unsigned msgsize = buf.size + 5;
gettimeofday(&now, NULL);
difftime = (now.tv_sec - req->start.tv_sec) * 1000;
difftime += (now.tv_usec - req->start.tv_usec) / 1000;
DEBUG(" [%05i] %14s %8ubytes (%ims)\n", id,
type_name(type),
(unsigned) buf.size + 5, difftime);
type_name(type), msgsize, difftime);
if (difftime < sshfs.min_rtt || !sshfs.num_received)
sshfs.min_rtt = difftime;
if (difftime > sshfs.max_rtt)
sshfs.max_rtt = difftime;
sshfs.total_rtt += difftime;
sshfs.num_received++;
sshfs.bytes_received += msgsize;
}
req->reply = buf;
req->reply_type = type;
@@ -1025,6 +1273,20 @@ static int process_one_request(void)
return 0;
}
static void close_conn(void)
{
close(sshfs.fd);
sshfs.fd = -1;
if (sshfs.ptyfd != -1) {
close(sshfs.ptyfd);
sshfs.ptyfd = -1;
}
if (sshfs.ptyslavefd != -1) {
close(sshfs.ptyslavefd);
sshfs.ptyslavefd = -1;
}
}
static void *process_requests(void *data_)
{
(void) data_;
@@ -1040,8 +1302,7 @@ static void *process_requests(void *data_)
} else {
pthread_mutex_lock(&sshfs.lock);
sshfs.processing_thread_started = 0;
close(sshfs.fd);
sshfs.fd = -1;
close_conn();
g_hash_table_foreach_remove(sshfs.reqtab, (GHRFunc) clean_req,
NULL);
sshfs.connver ++;
@@ -1070,10 +1331,34 @@ static int sftp_init_reply_ok(struct buffer *buf, uint32_t *version)
if (buf_get_uint32(buf, version) == -1)
return -1;
DEBUG("Server version: %u\n", *version);
if (len > 5) {
struct buffer buf2;
buf_init(&buf2, len - 5);
return do_read(&buf2);
if (do_read(&buf2) == -1)
return -1;
do {
char *ext;
char *extdata;
if (buf_get_string(&buf2, &ext) == -1 ||
buf_get_string(&buf2, &extdata) == -1)
return -1;
DEBUG("Extension: %s <%s>\n", ext, extdata);
if (strcmp(ext, SFTP_EXT_POSIX_RENAME) == 0 &&
strcmp(extdata, "1") == 0) {
sshfs.ext_posix_rename = 1;
sshfs.rename_workaround = 0;
}
if (strcmp(ext, SFTP_EXT_STATVFS) == 0 &&
strcmp(extdata, "2") == 0)
sshfs.ext_statvfs = 1;
} while (buf2.len < buf2.size);
}
return 0;
}
@@ -1112,11 +1397,14 @@ static int sftp_init()
buf_init(&buf, 0);
if (sftp_send_iov(SSH_FXP_INIT, PROTO_VERSION, NULL, 0) == -1)
goto out;
if (sshfs.password_stdin && pty_expect_loop() == -1)
goto out;
if (sftp_find_init_reply(&version) == -1)
goto out;
sshfs.server_version = version;
DEBUG("Server version: %i\n", sshfs.server_version);
if (version > PROTO_VERSION) {
fprintf(stderr,
"Warning: server uses version: %i, we support: %i\n",
@@ -1268,6 +1556,11 @@ static int connect_remote(void)
if (!err)
err = sftp_init();
if (err)
close_conn();
else
sshfs.num_connect++;
return err;
}
@@ -1413,8 +1706,11 @@ static int sftp_request_send(uint8_t type, struct iovec *iov, size_t count,
pthread_cond_wait(&sshfs.outstanding_cond, &sshfs.lock);
g_hash_table_insert(sshfs.reqtab, GUINT_TO_POINTER(id), req);
if (sshfs.debug)
if (sshfs.debug) {
gettimeofday(&req->start, NULL);
sshfs.num_sent++;
sshfs.bytes_sent += req->len;
}
DEBUG("[%05i] %s\n", id, type_name(type));
pthread_mutex_unlock(&sshfs.lock);
@@ -1706,6 +2002,19 @@ static int sshfs_do_rename(const char *from, const char *to)
return err;
}
static int sshfs_ext_posix_rename(const char *from, const char *to)
{
int err;
struct buffer buf;
buf_init(&buf, 0);
buf_add_string(&buf, SFTP_EXT_POSIX_RENAME);
buf_add_path(&buf, from);
buf_add_path(&buf, to);
err = sftp_request(SSH_FXP_EXTENDED, &buf, SSH_FXP_STATUS, NULL);
buf_free(&buf);
return err;
}
static void random_string(char *str, int length)
{
int i;
@@ -1717,7 +2026,10 @@ static void random_string(char *str, int length)
static int sshfs_rename(const char *from, const char *to)
{
int err;
err = sshfs_do_rename(from, to);
if (sshfs.ext_posix_rename)
err = sshfs_ext_posix_rename(from, to);
else
err = sshfs_do_rename(from, to);
if (err == -EPERM && sshfs.rename_workaround) {
size_t tolen = strlen(to);
if (tolen + RENAME_TEMP_CHARS < PATH_MAX) {
@@ -1818,6 +2130,7 @@ static int sshfs_open_common(const char *path, mode_t mode,
uint32_t pflags = 0;
struct iovec iov;
uint8_t type;
uint64_t wrctr = cache_get_write_ctr();
if ((fi->flags & O_ACCMODE) == O_RDONLY)
pflags = SSH_FXF_READ;
@@ -1873,7 +2186,7 @@ static int sshfs_open_common(const char *path, mode_t mode,
}
if (!err) {
cache_add_attr(path, &stbuf);
cache_add_attr(path, &stbuf, wrctr);
buf_finish(&sf->handle);
fi->fh = (unsigned long) sf;
} else {
@@ -2216,10 +2529,31 @@ static int sshfs_write(const char *path, const char *wbuf, size_t size,
return err ? err : (int) size;
}
static int sshfs_ext_statvfs(const char *path, struct statvfs *stbuf)
{
int err;
struct buffer buf;
struct buffer outbuf;
buf_init(&buf, 0);
buf_add_string(&buf, SFTP_EXT_STATVFS);
buf_add_path(&buf, path);
err = sftp_request(SSH_FXP_EXTENDED, &buf, SSH_FXP_EXTENDED_REPLY,
&outbuf);
if (!err) {
if (buf_get_statvfs(&outbuf, stbuf) == -1)
err = -EIO;
buf_free(&outbuf);
}
buf_free(&buf);
return err;
}
#if FUSE_VERSION >= 25
static int sshfs_statfs(const char *path, struct statvfs *buf)
{
(void) path;
if (sshfs.ext_statvfs)
return sshfs_ext_statvfs(path, buf);
buf->f_namemax = 255;
buf->f_bsize = sshfs.blksize;
@@ -2236,7 +2570,22 @@ static int sshfs_statfs(const char *path, struct statvfs *buf)
#else
static int sshfs_statfs(const char *path, struct statfs *buf)
{
(void) path;
if (sshfs.ext_statvfs) {
int err;
struct statvfs vbuf;
err = sshfs_ext_statvfs(path, &vbuf);
if (!err) {
buf->f_bsize = vbuf.f_bsize;
buf->f_blocks = vbuf.f_blocks;
buf->f_bfree = vbuf.f_bfree;
buf->f_bavail = vbuf.f_bavail;
buf->f_files = vbuf.f_files;
buf->f_ffree = vbuf.f_ffree;
buf->f_namelen = vbuf.f_namemax;
}
return err;
}
buf->f_namelen = 255;
buf->f_bsize = sshfs.blksize;
@@ -2430,6 +2779,8 @@ static int sshfs_truncate_workaround(const char *path, off_t size,
static int processing_init(void)
{
signal(SIGPIPE, SIG_IGN);
pthread_mutex_init(&sshfs.lock, NULL);
pthread_mutex_init(&sshfs.lock_write, NULL);
pthread_cond_init(&sshfs.outstanding_cond, NULL);
@@ -2487,6 +2838,7 @@ static void usage(const char *progname)
"SSHFS options:\n"
" -p PORT equivalent to '-o port=PORT'\n"
" -C equivalent to '-o compression=yes'\n"
" -F ssh_configfile specifies alternative ssh configuration file\n"
" -1 equivalent to '-o ssh_protocol=1'\n"
" -o reconnect reconnect to server\n"
" -o sshfs_sync synchronous writes\n"
@@ -2515,6 +2867,7 @@ static void usage(const char *progname)
" -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 password_stdin read password from stdin (only for pam_mount!)\n"
" -o SSHOPT=VAL ssh options (see man ssh_config)\n"
"\n", progname);
}
@@ -2576,6 +2929,12 @@ static int sshfs_opt_proc(void *data, const char *arg, int key,
ssh_add_arg("-oCompression=yes");
return 0;
case KEY_CONFIGFILE:
tmp = g_strdup_printf("-F%s", arg + 2);
ssh_add_arg(tmp);
g_free(tmp);
return 0;
case KEY_HELP:
usage(outargs->argv[0]);
fuse_opt_add_arg(outargs, "-ho");
@@ -2590,6 +2949,10 @@ static int sshfs_opt_proc(void *data, const char *arg, int key,
#endif
exit(0);
case KEY_FOREGROUND:
sshfs.foreground = 1;
return 1;
default:
fprintf(stderr, "internal error\n");
abort();
@@ -2641,7 +3004,7 @@ static int fuse_opt_insert_arg(struct fuse_args *args, int pos,
}
#endif
void check_large_read(struct fuse_args *args)
static void check_large_read(struct fuse_args *args)
{
struct utsname buf;
int err = uname(&buf);
@@ -2650,6 +3013,150 @@ void check_large_read(struct fuse_args *args)
fuse_opt_insert_arg(args, 1, "-olarge_read");
}
static int read_password(void)
{
int size = getpagesize();
int max_password = 64;
int n;
sshfs.password = mmap(NULL, size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_LOCKED,
-1, 0);
if (sshfs.password == MAP_FAILED) {
perror("Failed to allocate locked page for password");
return -1;
}
/* Don't use fgets() because password might stay in memory */
for (n = 0; n < max_password; n++) {
int res;
res = read(0, &sshfs.password[n], 1);
if (res == -1) {
perror("Reading password");
return -1;
}
if (res == 0) {
sshfs.password[n] = '\n';
break;
}
if (sshfs.password[n] == '\n')
break;
}
if (n == max_password) {
fprintf(stderr, "Password too long\n");
return -1;
}
sshfs.password[n+1] = '\0';
ssh_add_arg("-oNumberOfPasswordPrompts=1");
ssh_add_arg("-oPreferredAuthentications=password,keyboard-interactive");
return 0;
}
static void set_ssh_command(void)
{
char *s;
char *d;
int i = 0;
int end = 0;
d = sshfs.ssh_command;
s = sshfs.ssh_command;
while (!end) {
switch (*s) {
case '\0':
end = 1;
case ' ':
*d = '\0';
if (i == 0) {
replace_arg(&sshfs.ssh_args.argv[0],
sshfs.ssh_command);
} else {
if (fuse_opt_insert_arg(&sshfs.ssh_args, i,
sshfs.ssh_command) == -1)
_exit(1);
}
i++;
d = sshfs.ssh_command;
break;
case '\\':
if (s[1])
s++;
default:
*d++ = *s;
}
s++;
}
}
static char *find_base_path(void)
{
char *s = sshfs.host;
char *d = s;
for (; *s && *s != ':'; s++) {
if (*s == '[') {
/*
* Handle IPv6 numerical address enclosed in square
* brackets
*/
s++;
for (; *s != ']'; s++) {
if (!*s) {
fprintf(stderr, "missing ']' in hostname\n");
exit(1);
}
*d++ = *s;
}
} else {
*d++ = *s;
}
}
*d++ = '\0';
s++;
return s;
}
/*
* Remove commas from fsname, as it confuses the fuse option parser.
*/
static void fsname_remove_commas(char *fsname)
{
if (strchr(fsname, ',') != NULL) {
char *s = fsname;
char *d = s;
for (; *s; s++) {
if (*s != ',')
*d++ = *s;
}
*d = *s;
}
}
#if FUSE_VERSION >= 27
static char *fsname_escape_commas(char *fsnameold)
{
char *fsname = g_malloc(strlen(fsnameold) * 2 + 1);
char *d = fsname;
char *s;
for (s = fsnameold; *s; s++) {
if (*s == '\\' || *s == ',')
*d++ = '\\';
*d++ = *s;
}
*d = '\0';
g_free(fsnameold);
return fsname;
}
#endif
int main(int argc, char *argv[])
{
int res;
@@ -2664,6 +3171,7 @@ int main(int argc, char *argv[])
sshfs.blksize = 4096;
sshfs.max_read = 65536;
sshfs.max_write = 65536;
sshfs.nodelay_workaround = 1;
sshfs.nodelaysrv_workaround = 0;
sshfs.rename_workaround = 0;
@@ -2671,6 +3179,9 @@ int main(int argc, char *argv[])
sshfs.buflimit_workaround = 1;
sshfs.ssh_ver = 2;
sshfs.progname = argv[0];
sshfs.fd = -1;
sshfs.ptyfd = -1;
sshfs.ptyslavefd = -1;
ssh_add_arg("ssh");
ssh_add_arg("-x");
ssh_add_arg("-a");
@@ -2680,6 +3191,14 @@ int main(int argc, char *argv[])
parse_workarounds() == -1)
exit(1);
DEBUG("SSHFS version %s\n", PACKAGE_VERSION);
if (sshfs.password_stdin) {
res = read_password();
if (res == -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
@@ -2695,17 +3214,14 @@ int main(int argc, char *argv[])
}
fsname = g_strdup(sshfs.host);
base_path = strchr(sshfs.host, ':');
*base_path++ = '\0';
base_path = find_base_path();
if (base_path[0] && base_path[strlen(base_path)-1] != '/')
sshfs.base_path = g_strdup_printf("%s/", base_path);
else
sshfs.base_path = g_strdup(base_path);
if (sshfs.ssh_command) {
free(sshfs.ssh_args.argv[0]);
sshfs.ssh_args.argv[0] = sshfs.ssh_command;
}
if (sshfs.ssh_command)
set_ssh_command();
tmp = g_strdup_printf("-%i", sshfs.ssh_ver);
ssh_add_arg(tmp);
@@ -2747,17 +3263,26 @@ int main(int argc, char *argv[])
if (sshfs.max_read > 65536)
sshfs.max_read = 65536;
if (sshfs.max_write > 65536)
sshfs.max_write = 65536;
if (fuse_is_lib_option("ac_attr_timeout="))
fuse_opt_insert_arg(&args, 1, "-oauto_cache,ac_attr_timeout=0");
tmp = g_strdup_printf("-omax_read=%u", sshfs.max_read);
fuse_opt_insert_arg(&args, 1, tmp);
tmp = g_strdup_printf("-omax_write=%u", sshfs.max_write);
fuse_opt_insert_arg(&args, 1, tmp);
g_free(tmp);
#if FUSE_VERSION >= 27
libver = fuse_version();
assert(libver >= 27);
if (libver >= 28)
fsname = fsname_escape_commas(fsname);
else
fsname_remove_commas(fsname);
tmp = g_strdup_printf("-osubtype=sshfs,fsname=%s", fsname);
#else
fsname_remove_commas(fsname);
tmp = g_strdup_printf("-ofsname=sshfs#%s", fsname);
#endif
fuse_opt_insert_arg(&args, 1, tmp);
@@ -2765,6 +3290,26 @@ int main(int argc, char *argv[])
g_free(fsname);
check_large_read(&args);
res = sshfs_fuse_main(&args);
if (sshfs.debug) {
unsigned int avg_rtt = 0;
if (sshfs.num_sent)
avg_rtt = sshfs.total_rtt / sshfs.num_sent;
DEBUG("\n"
"sent: %llu messages, %llu bytes\n"
"received: %llu messages, %llu bytes\n"
"rtt min/max/avg: %ums/%ums/%ums\n"
"num connect: %u\n",
(unsigned long long) sshfs.num_sent,
(unsigned long long) sshfs.bytes_sent,
(unsigned long long) sshfs.num_received,
(unsigned long long) sshfs.bytes_received,
sshfs.min_rtt, sshfs.max_rtt, avg_rtt,
sshfs.num_connect);
}
fuse_opt_free_args(&args);
fuse_opt_free_args(&sshfs.ssh_args);
free(sshfs.directport);