Compare commits

...

10 Commits

Author SHA1 Message Date
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
6 changed files with 758 additions and 24 deletions
+54
View File
@@ -1,3 +1,57 @@
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
------------------
+3 -3
View File
@@ -1,4 +1,4 @@
AC_INIT(sshfs-fuse, 1.9)
AC_INIT(sshfs-fuse, 2.0)
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])
+214
View File
@@ -0,0 +1,214 @@
.TH SSHFS "1" "April 2008" "SSHFS version 2.0" "User Commands"
.SH NAME
SSHFS \- filesystem client based on ssh
.SH DESCRIPTION
usage: sshfs [user@]host:[dir] mountpoint [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\-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
.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)
.TP
\fB\-o\fR idmap=TYPE
user/group ID mapping, possible types are:
.TP
none
no translation of the ID space (default)
.TP
user
only translate UID of connecting user
.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)
+474 -21
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>
@@ -27,6 +28,8 @@
#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>
@@ -88,6 +91,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 +177,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 +192,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 +205,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 +235,8 @@ static const char *ssh_opts[] = {
"CompressionLevel",
"ConnectionAttempts",
"ConnectTimeout",
"ControlMaster",
"ControlPath",
"GlobalKnownHostsFile",
"GSSAPIAuthentication",
"GSSAPIDelegateCredentials",
@@ -214,8 +244,11 @@ static const char *ssh_opts[] = {
"HostKeyAlgorithms",
"HostKeyAlias",
"HostName",
"IdentityFile",
"IdentitiesOnly",
"IdentityFile",
"KbdInteractiveAuthentication",
"KbdInteractiveDevices",
"LocalCommand",
"LogLevel",
"MACs",
"NoHostAuthenticationForLocalhost",
@@ -225,10 +258,11 @@ static const char *ssh_opts[] = {
"PreferredAuthentications",
"ProxyCommand",
"PubkeyAuthentication",
"RekeyLimit",
"RhostsRSAAuthentication",
"RSAAuthentication",
"ServerAliveInterval",
"ServerAliveCountMax",
"ServerAliveInterval",
"SmartcardDevice",
"StrictHostKeyChecking",
"TCPKeepAlive",
@@ -243,6 +277,7 @@ enum {
KEY_COMPRESS,
KEY_HELP,
KEY_VERSION,
KEY_FOREGROUND,
};
#define SSHFS_OPT(t, p, v) { t, offsetof(struct sshfs, p), v }
@@ -252,6 +287,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 +300,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 +308,9 @@ 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_END
};
@@ -599,7 +639,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 +648,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)
{
uint32_t bsize;
uint32_t frsize;
uint64_t blocks;
uint64_t bfree;
uint64_t bavail;
uint64_t files;
uint64_t ffree;
uint64_t favail;
uint32_t fsid;
uint32_t flag;
uint32_t namemax;
if (buf_get_uint32(buf, &bsize) == -1 ||
buf_get_uint32(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_uint32(buf, &fsid) == -1 ||
buf_get_uint32(buf, &flag) == -1 ||
buf_get_uint32(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 +777,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 +903,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 +917,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 +937,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 +955,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 +1226,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 +1261,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 +1290,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 +1319,32 @@ 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) {
sshfs.ext_posix_rename = 1;
sshfs.rename_workaround = 0;
}
if (strcmp(ext, SFTP_EXT_STATVFS) == 0)
sshfs.ext_statvfs = 1;
} while (buf2.len < buf2.size);
}
return 0;
}
@@ -1112,11 +1383,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 +1542,11 @@ static int connect_remote(void)
if (!err)
err = sftp_init();
if (err)
close_conn();
else
sshfs.num_connect++;
return err;
}
@@ -1413,8 +1692,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 +1988,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 +2012,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) {
@@ -2216,10 +2514,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 +2555,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 +2764,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);
@@ -2515,6 +2851,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);
}
@@ -2590,6 +2927,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 +2982,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 +2991,84 @@ 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++;
}
}
int main(int argc, char *argv[])
{
int res;
@@ -2664,6 +3083,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 +3091,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 +3103,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
@@ -2702,10 +3133,8 @@ int main(int argc, char *argv[])
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,11 +3176,15 @@ 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();
@@ -2765,6 +3198,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);