Compare commits

..

7 Commits

Author SHA1 Message Date
Miklos Szeredi 88ab18b8f8 release 1.0 2005-01-09 19:48:14 +00:00
Miklos Szeredi 716bfbf697 fix 2004-12-05 21:26:16 +00:00
Miklos Szeredi 7fb3a02ec5 *** empty log message *** 2004-12-04 21:20:50 +00:00
Miklos Szeredi b3483b6bb9 fix 2004-12-04 12:40:21 +00:00
Miklos Szeredi 6f3b9bfdd1 automakeify 2004-12-04 12:29:44 +00:00
Miklos Szeredi c73761963b fix 2004-11-29 19:13:15 +00:00
Miklos Szeredi 5dc1d26e70 optimizations 2004-11-26 12:17:39 +00:00
9 changed files with 620 additions and 147 deletions
+15
View File
@@ -0,0 +1,15 @@
.deps
COPYING
INSTALL
Makefile
Makefile.in
aclocal.m4
*.cache
config.*
configure
depcomp
install-sh
missing
sshfs
stamp*
.deps
+1
View File
@@ -0,0 +1 @@
Miklos Szeredi <miklos@szeredi.hu>
+7
View File
@@ -0,0 +1,7 @@
2005-01-09 Miklos Szeredi <miklos@szeredi.hu>
* Released 1.0
2004-12-04 Miklos Szeredi <miklos@szeredi.hu>
* Started ChangeLog
-21
View File
@@ -1,21 +0,0 @@
CC = gcc
CFLAGS := -Wall -W -g
LDLIBS := -lpthread -ldl -rdynamic
PKGCONFIG := env PKG_CONFIG_PATH=/usr/local/lib/pkgconfig pkg-config
FUSEVER := $(shell $(PKGCONFIG) --modversion fuse 2> /dev/null)
ifeq ($(FUSEVER),)
LDLIBS += -lfuse
else
CFLAGS += $(shell $(PKGCONFIG) --cflags fuse)
LDLIBS += $(shell $(PKGCONFIG) --libs fuse)
endif
CPPFLAGS := -D_FILE_OFFSET_BITS=64
#CPPFLAGS += -DDEBUG
sshfs: sshfs.o
clean:
rm -f *.o sshfs
+4
View File
@@ -0,0 +1,4 @@
## Process this file with automake to produce Makefile.in
bin_PROGRAMS = sshfs
sshfs_SOURCES = sshfs.c
+4
View File
@@ -0,0 +1,4 @@
What is new in 1.0
------------------
* Initial release
+63
View File
@@ -0,0 +1,63 @@
Abstract
========
This is a filesystem client based on the SSH File Transfer Protocol.
Since most SSH servers already support this protocol it is very easy
to set up: i.e. on the server side there's nothing to do. On the
client side mounting the filesystem is as easy as logging into the
server with ssh.
The idea of sshfs was taken from the SSHFS filesystem distributed with
LUFS, which I found very useful. There were some limitations of that
codebase, so I rewrote it. Features of this implementation are:
- Based on FUSE (the best userspace filesystem framework for linux ;)
- Multithreading: more than one request can be on it's way to the
server
- Allowing large reads (max 64k)
- Caching directory contents
How to mount a filesystem
=========================
Once sshfs is installed (see next section) running it is very simple:
sshfs hostname: /mountpoint
Note, that it's recommended to run it as user, not as root. For this
to work the mountpoint must be owned by the user. If the username is
different on the host you are connecting to, then use the
"username@host:" form. If you need to enter a password sshfs will ask
for it (actually it just runs ssh which ask for the password if
needed). You can also specify a directory after the ":". The default
is the home directory.
Installing
==========
First you need to download FUSE 2.2 or later from:
http://fuse.sourceforge.net
You also need to install the devel package for glib2.0. After
installing FUSE, compile sshfs the usual way:
./configure
make
make install (as root)
And you are ready to go.
Bugs and feature requests
=========================
Send bug reports to <miklos@szeredi.hu>.
Good luck!
Miklos Szeredi
+12
View File
@@ -0,0 +1,12 @@
AC_INIT(sshfs-fuse, 1.0)
AM_INIT_AUTOMAKE
AM_CONFIG_HEADER(config.h)
AC_PROG_CC
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
PKG_CHECK_MODULES(SSHFS, [fuse >= 2.2 glib-2.0])
CFLAGS="$CFLAGS -Wall -W -D_REENTRANT $SSHFS_CFLAGS"
LIBS="$SSHFS_LIBS"
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
+514 -126
View File
@@ -1,3 +1,12 @@
/*
SSH file system
Copyright (C) 2004 Miklos Szeredi <miklos@szeredi.hu>
This program can be distributed under the terms of the GNU GPL.
See the file COPYING.
*/
#define FUSE_USE_VERSION 22
#include <fuse.h>
#include <stdio.h>
#include <stdlib.h>
@@ -6,7 +15,13 @@
#include <string.h>
#include <stdint.h>
#include <errno.h>
#include <semaphore.h>
#include <pthread.h>
#include <netdb.h>
#include <signal.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <glib.h>
#define SSH_FXP_INIT 1
#define SSH_FXP_VERSION 2
@@ -63,8 +78,15 @@
#define MY_EOF 1
#define MAX_REPLY_LEN (1 << 17)
#define CACHE_TIMEOUT 20
#define MAX_CACHE_SIZE 10000
#define CACHE_CLEAN_INTERVAL 60
static int infd;
static int outfd;
static int debug = 0;
static char *base_path;
struct buffer {
uint8_t *p;
@@ -72,10 +94,74 @@ struct buffer {
size_t size;
};
struct request {
unsigned int want_reply;
sem_t ready;
uint8_t reply_type;
struct buffer reply;
struct timeval start;
};
struct openfile {
unsigned int read_ctr;
unsigned int write_ctr;
int rw;
struct buffer read_handle;
struct buffer write_handle;
};
struct node {
struct stat stat;
time_t updated;
};
static GHashTable *reqtab;
static GHashTable *cache;
static time_t last_cleaned;
static pthread_mutex_t lock;
static int processing_thread_started;
#define DEBUG(format, args...) \
do { if (debug) fprintf(stderr, format, args); } while(0)
static const char *type_name(uint8_t type)
{
switch(type) {
case SSH_FXP_INIT: return "INIT";
case SSH_FXP_VERSION: return "VERSION";
case SSH_FXP_OPEN: return "OPEN";
case SSH_FXP_CLOSE: return "CLOSE";
case SSH_FXP_READ: return "READ";
case SSH_FXP_WRITE: return "WRITE";
case SSH_FXP_LSTAT: return "LSTAT";
case SSH_FXP_FSTAT: return "FSTAT";
case SSH_FXP_SETSTAT: return "SETSTAT";
case SSH_FXP_FSETSTAT: return "FSETSTAT";
case SSH_FXP_OPENDIR: return "OPENDIR";
case SSH_FXP_READDIR: return "READDIR";
case SSH_FXP_REMOVE: return "REMOVE";
case SSH_FXP_MKDIR: return "MKDIR";
case SSH_FXP_RMDIR: return "RMDIR";
case SSH_FXP_REALPATH: return "REALPATH";
case SSH_FXP_STAT: return "STAT";
case SSH_FXP_RENAME: return "RENAME";
case SSH_FXP_READLINK: return "READLINK";
case SSH_FXP_SYMLINK: return "SYMLINK";
case SSH_FXP_STATUS: return "STATUS";
case SSH_FXP_HANDLE: return "HANDLE";
case SSH_FXP_DATA: return "DATA";
case SSH_FXP_NAME: return "NAME";
case SSH_FXP_ATTRS: return "ATTRS";
case SSH_FXP_EXTENDED: return "EXTENDED";
case SSH_FXP_EXTENDED_REPLY: return "EXTENDED_REPLY";
default: return "???";
}
}
static inline void buf_init(struct buffer *buf, size_t size)
{
if (size) {
buf->p = malloc(size);
buf->p = (uint8_t *) malloc(size);
if (!buf->p)
exit(1);
} else
@@ -127,6 +213,11 @@ static inline void buf_add_mem(struct buffer *buf, const void *data,
_buf_add_mem(buf, data, len);
}
static inline void buf_add_buf(struct buffer *buf, const struct buffer *bufa)
{
_buf_add_mem(buf, bufa->p, bufa->len);
}
static inline void buf_add_uint8(struct buffer *buf, uint8_t val)
{
_buf_add_mem(buf, &val, 1);
@@ -158,6 +249,13 @@ static inline void buf_add_string(struct buffer *buf, const char *str)
buf_add_data(buf, &data);
}
static inline void buf_add_path(struct buffer *buf, const char *path)
{
char *realpath = g_strdup_printf("%s%s", base_path, path[1] ? path+1 : ".");
buf_add_string(buf, realpath);
g_free(realpath);
}
static int buf_check_get(struct buffer *buf, size_t len)
{
if (buf->len + len > buf->size) {
@@ -277,8 +375,75 @@ static int buf_get_attrs(struct buffer *buf, struct stat *stbuf)
return 0;
}
static int cache_clean_entry(void *_key, struct node *node, time_t *now)
{
(void) _key;
if (*now > node->updated + CACHE_TIMEOUT)
return TRUE;
else
return FALSE;
}
static void cache_clean(void)
{
time_t now = time(NULL);
if (g_hash_table_size(cache) > MAX_CACHE_SIZE ||
now > last_cleaned + CACHE_CLEAN_INTERVAL) {
g_hash_table_foreach_remove(cache, (GHRFunc) cache_clean_entry, &now);
last_cleaned = now;
}
}
static struct node *cache_lookup(const char *path)
{
return (struct node *) g_hash_table_lookup(cache, path);
}
static void cache_remove(const char *path)
{
pthread_mutex_lock(&lock);
g_hash_table_remove(cache, path);
pthread_mutex_unlock(&lock);
}
static void cache_invalidate(const char *path)
{
cache_remove(path);
}
static void cache_rename(const char *from, const char *to)
{
cache_remove(from);
cache_remove(to);
}
static struct node *cache_get(const char *path)
{
struct node *node = cache_lookup(path);
if (node == NULL) {
char *pathcopy = g_strdup(path);
node = g_new0(struct node, 1);
g_hash_table_insert(cache, pathcopy, node);
}
return node;
}
static void cache_add_attr(const char *path, const struct stat *stbuf)
{
struct node *node;
time_t now;
pthread_mutex_lock(&lock);
node = cache_get(path);
now = time(NULL);
node->stat = *stbuf;
node->updated = time(NULL);
cache_clean();
pthread_mutex_unlock(&lock);
}
static int buf_get_entries(struct buffer *buf, fuse_dirh_t h,
fuse_dirfil_t filler)
fuse_dirfil_t filler, const char *path)
{
uint32_t count;
unsigned i;
@@ -296,7 +461,11 @@ static int buf_get_entries(struct buffer *buf, fuse_dirh_t h,
if (buf_get_string(buf, &longname) != -1) {
free(longname);
if (buf_get_attrs(buf, &stbuf) != -1) {
filler(h, name, stbuf.st_mode >> 12);
char *fullpath;
filler(h, name, stbuf.st_mode >> 12, 0);
fullpath = g_strdup_printf("%s/%s", !path[1] ? "" : path, name);
cache_add_attr(fullpath, &stbuf);
g_free(fullpath);
err = 0;
}
}
@@ -307,7 +476,7 @@ static int buf_get_entries(struct buffer *buf, fuse_dirh_t h,
return 0;
}
static int start_ssh(const char *host)
static int start_ssh(const char *host, const char *port)
{
int inpipe[2];
int outpipe[2];
@@ -334,7 +503,7 @@ static int start_ssh(const char *host)
close(outpipe[0]);
close(outpipe[1]);
execlp("ssh", "ssh", "-2", "-x", "-a", "-oClearAllForwardings=yes",
host, "-s", "sftp", NULL);
host, "-s", "sftp", port ? "-p" : NULL, port, NULL);
exit(1);
}
close(inpipe[1]);
@@ -342,6 +511,39 @@ static int start_ssh(const char *host)
return 0;
}
static int connect_to(char *host, char *port)
{
int err;
int sock;
struct addrinfo *ai;
struct addrinfo hint;
memset(&hint, 0, sizeof(hint));
hint.ai_family = PF_INET;
hint.ai_socktype = SOCK_STREAM;
err = getaddrinfo(host, port, &hint, &ai);
if (err) {
fprintf(stderr, "failed to resolve %s:%s: %s\n", host, port,
gai_strerror(err));
return -1;
}
sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if (sock == -1) {
perror("failed to create socket");
return -1;
}
err = connect(sock, ai->ai_addr, ai->ai_addrlen);
if (err == -1) {
perror("failed to connect");
return -1;
}
freeaddrinfo(ai);
infd = sock;
outfd = sock;
return 0;
}
static int do_write(struct buffer *buf)
{
uint8_t *p = buf->p;
@@ -375,9 +577,11 @@ static int sftp_send(uint8_t type, struct buffer *buf)
buf_init(&buf2, 5);
buf_add_uint32(&buf2, buf->len + 1);
buf_add_uint8(&buf2, type);
pthread_mutex_lock(&lock);
res = do_write(&buf2);
if (res != -1)
res = do_write(buf);
pthread_mutex_unlock(&lock);
buf_free(&buf2);
return res;
}
@@ -411,6 +615,10 @@ static int sftp_read(uint8_t *type, struct buffer *buf)
res = do_read(&buf2);
if (res != -1) {
buf_get_uint32(&buf2, &len);
if (len > MAX_REPLY_LEN) {
fprintf(stderr, "reply len too large: %u\n", len);
return -1;
}
buf_get_uint8(&buf2, type);
buf_init(buf, len - 1);
res = do_read(buf);
@@ -419,39 +627,117 @@ static int sftp_read(uint8_t *type, struct buffer *buf)
return res;
}
static void *process_requests(void *_data)
{
(void) _data;
while (1) {
int res;
struct buffer buf;
uint8_t type;
struct request *req;
uint32_t id;
buf_init(&buf, 0);
res = sftp_read(&type, &buf);
if (res == -1)
break;
if (buf_get_uint32(&buf, &id) == -1)
break;
pthread_mutex_lock(&lock);
req = (struct request *) g_hash_table_lookup(reqtab, (gpointer) id);
if (req == NULL)
fprintf(stderr, "request %i not found\n", id);
else
g_hash_table_remove(reqtab, (gpointer) id);
pthread_mutex_unlock(&lock);
if (req != NULL) {
struct timeval now;
unsigned int difftime;
gettimeofday(&now, NULL);
difftime = (now.tv_sec - req->start.tv_sec) * 1000;
difftime += (now.tv_usec - req->start.tv_usec) / 1000;
DEBUG(" [%05i] %14s %8ibytes (%ims)\n", id, type_name(type),
buf.size+5, difftime);
req->reply = buf;
if (req->want_reply) {
req->reply_type = type;
sem_post(&req->ready);
} else {
buf_free(&req->reply);
sem_destroy(&req->ready);
free(req);
}
} else
buf_free(&buf);
}
kill(getpid(), SIGTERM);
return NULL;
}
static int start_processing_thread(void)
{
int err;
pthread_t thread_id;
if (processing_thread_started)
return 0;
err = pthread_create(&thread_id, NULL, process_requests, NULL);
if (err) {
fprintf(stderr, "failed to create thread: %s\n", strerror(err));
return -EPERM;
}
pthread_detach(thread_id);
processing_thread_started = 1;
return 0;
}
static int sftp_request(uint8_t type, const struct buffer *buf,
uint8_t expect_type, struct buffer *outbuf)
{
int err;
struct buffer buf2;
uint32_t id = sftp_get_id();
uint32_t idback;
uint8_t typeback;
struct request *req = (struct request *) malloc(sizeof(struct request));
buf_init(&buf2, buf->len + 4);
buf_add_uint32(&buf2, id);
buf_add_mem(&buf2, buf->p, buf->len);
req->want_reply = expect_type != 0 ? 1 : 0;
sem_init(&req->ready, 0, 0);
buf_init(&req->reply, 0);
pthread_mutex_lock(&lock);
err = start_processing_thread();
g_hash_table_insert(reqtab, (gpointer) id, req);
gettimeofday(&req->start, NULL);
DEBUG("[%05i] %s\n", id, type_name(type));
pthread_mutex_unlock(&lock);
if (err)
goto out;
err = -EIO;
if (sftp_send(type, &buf2) == -1)
goto out;
buf_clear(&buf2);
if (sftp_read(&typeback, &buf2) == -1)
if (sftp_send(type, &buf2) == -1) {
pthread_mutex_lock(&lock);
g_hash_table_remove(reqtab, (gpointer) id);
pthread_mutex_unlock(&lock);
goto out;
}
if (expect_type == 0) {
buf_free(&buf2);
return 0;
}
sem_wait(&req->ready);
err = -EPROTO;
if (typeback != expect_type && typeback != SSH_FXP_STATUS) {
if (req->reply_type != expect_type && req->reply_type != SSH_FXP_STATUS) {
fprintf(stderr, "protocol error\n");
goto out;
}
if (buf_get_uint32(&buf2, &idback) == -1)
goto out;
if (idback != id) {
fprintf(stderr, "Invalid ID received\n");
goto out;
}
if (typeback == SSH_FXP_STATUS) {
if (req->reply_type == SSH_FXP_STATUS) {
uint32_t serr;
if (buf_get_uint32(&buf2, &serr) == -1)
if (buf_get_uint32(&req->reply, &serr) == -1)
goto out;
switch (serr) {
@@ -476,24 +762,27 @@ static int sftp_request(uint8_t type, const struct buffer *buf,
default: err = -EPROTO; break;
}
} else {
buf_init(outbuf, buf2.size - buf2.len);
buf_get_mem(&buf2, outbuf->p, outbuf->size);
buf_init(outbuf, req->reply.size - req->reply.len);
buf_get_mem(&req->reply, outbuf->p, outbuf->size);
err = 0;
}
out:
buf_free(&buf2);
buf_free(&req->reply);
sem_destroy(&req->ready);
free(req);
return err;
}
static int sshfs_getattr(const char *path, struct stat *stbuf)
static int sshfs_send_getattr(const char *path, struct stat *stbuf)
{
int err;
struct buffer buf;
struct buffer outbuf;
buf_init(&buf, 0);
buf_add_string(&buf, path);
buf_add_path(&buf, path);
err = sftp_request(SSH_FXP_LSTAT, &buf, SSH_FXP_ATTRS, &outbuf);
if (!err) {
if (buf_get_attrs(&outbuf, stbuf) == -1)
@@ -501,16 +790,36 @@ static int sshfs_getattr(const char *path, struct stat *stbuf)
buf_free(&outbuf);
}
buf_free(&buf);
if (!err)
cache_add_attr(path, stbuf);
return err;
}
static int sshfs_getattr(const char *path, struct stat *stbuf)
{
struct node *node;
pthread_mutex_lock(&lock);
node = cache_lookup(path);
if (node != NULL) {
time_t now = time(NULL);
if (now - node->updated < CACHE_TIMEOUT) {
*stbuf = node->stat;
pthread_mutex_unlock(&lock);
return 0;
}
}
pthread_mutex_unlock(&lock);
return sshfs_send_getattr(path, stbuf);
}
static int sshfs_readlink(const char *path, char *linkbuf, size_t size)
{
int err;
struct buffer buf;
struct buffer name;
buf_init(&buf, 0);
buf_add_string(&buf, path);
buf_add_path(&buf, path);
err = sftp_request(SSH_FXP_READLINK, &buf, SSH_FXP_NAME, &name);
if (!err) {
uint32_t count;
@@ -535,7 +844,7 @@ static int sshfs_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler)
struct buffer buf;
struct buffer handle;
buf_init(&buf, 0);
buf_add_string(&buf, path);
buf_add_path(&buf, path);
err = sftp_request(SSH_FXP_OPENDIR, &buf, SSH_FXP_HANDLE, &handle);
if (!err) {
int err2;
@@ -544,7 +853,7 @@ static int sshfs_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler)
struct buffer name;
err = sftp_request(SSH_FXP_READDIR, &handle, SSH_FXP_NAME, &name);
if (!err) {
if (buf_get_entries(&name, h, filler) == -1)
if (buf_get_entries(&name, h, filler, path) == -1)
err = -EPROTO;
buf_free(&name);
}
@@ -552,8 +861,7 @@ static int sshfs_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler)
if (err == MY_EOF)
err = 0;
err2 = sftp_request(SSH_FXP_CLOSE, &handle, SSH_FXP_STATUS, NULL);
err2 = sftp_request(SSH_FXP_CLOSE, &handle, 0, NULL);
if (!err)
err = err2;
buf_free(&handle);
@@ -567,7 +875,7 @@ static int sshfs_mkdir(const char *path, mode_t mode)
int err;
struct buffer buf;
buf_init(&buf, 0);
buf_add_string(&buf, path);
buf_add_path(&buf, path);
buf_add_uint32(&buf, SSH_FILEXFER_ATTR_PERMISSIONS);
buf_add_uint32(&buf, mode);
err = sftp_request(SSH_FXP_MKDIR, &buf, SSH_FXP_STATUS, NULL);
@@ -579,14 +887,13 @@ static int sshfs_mknod(const char *path, mode_t mode, dev_t rdev)
int err;
struct buffer buf;
struct buffer handle;
(void) rdev;
if ((mode & S_IFMT) != S_IFREG)
return -EPERM;
buf_init(&buf, 0);
buf_add_string(&buf, path);
buf_add_path(&buf, path);
buf_add_uint32(&buf, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_EXCL);
buf_add_uint32(&buf, SSH_FILEXFER_ATTR_PERMISSIONS);
buf_add_uint32(&buf, mode);
@@ -607,11 +914,11 @@ static int sshfs_symlink(const char *from, const char *to)
{
int err;
struct buffer buf;
buf_init(&buf, 0);
/* openssh sftp server doesn't follow standard: link target and
link name are mixed up, so we must also be non-standard :( */
buf_init(&buf, 0);
buf_add_string(&buf, from);
buf_add_string(&buf, to);
buf_add_path(&buf, to);
err = sftp_request(SSH_FXP_SYMLINK, &buf, SSH_FXP_STATUS, NULL);
buf_free(&buf);
return err;
@@ -622,8 +929,10 @@ static int sshfs_unlink(const char *path)
int err;
struct buffer buf;
buf_init(&buf, 0);
buf_add_string(&buf, path);
buf_add_path(&buf, path);
err = sftp_request(SSH_FXP_REMOVE, &buf, SSH_FXP_STATUS, NULL);
if (!err)
cache_remove(path);
buf_free(&buf);
return err;
}
@@ -633,8 +942,10 @@ static int sshfs_rmdir(const char *path)
int err;
struct buffer buf;
buf_init(&buf, 0);
buf_add_string(&buf, path);
buf_add_path(&buf, path);
err = sftp_request(SSH_FXP_RMDIR, &buf, SSH_FXP_STATUS, NULL);
if (!err)
cache_remove(path);
buf_free(&buf);
return err;
}
@@ -644,9 +955,11 @@ static int sshfs_rename(const char *from, const char *to)
int err;
struct buffer buf;
buf_init(&buf, 0);
buf_add_string(&buf, from);
buf_add_string(&buf, to);
buf_add_path(&buf, from);
buf_add_path(&buf, to);
err = sftp_request(SSH_FXP_RENAME, &buf, SSH_FXP_STATUS, NULL);
if (!err)
cache_rename(from, to);
buf_free(&buf);
return err;
}
@@ -656,10 +969,12 @@ static int sshfs_chmod(const char *path, mode_t mode)
int err;
struct buffer buf;
buf_init(&buf, 0);
buf_add_string(&buf, path);
buf_add_path(&buf, path);
buf_add_uint32(&buf, SSH_FILEXFER_ATTR_PERMISSIONS);
buf_add_uint32(&buf, mode);
err = sftp_request(SSH_FXP_SETSTAT, &buf, SSH_FXP_STATUS, NULL);
if (!err)
cache_invalidate(path);
buf_free(&buf);
return err;
}
@@ -669,7 +984,7 @@ static int sshfs_chown(const char *path, uid_t uid, gid_t gid)
int err;
struct buffer buf;
buf_init(&buf, 0);
buf_add_string(&buf, path);
buf_add_path(&buf, path);
buf_add_uint32(&buf, SSH_FILEXFER_ATTR_UIDGID);
buf_add_uint32(&buf, uid);
buf_add_uint32(&buf, gid);
@@ -683,10 +998,12 @@ static int sshfs_truncate(const char *path, off_t size)
int err;
struct buffer buf;
buf_init(&buf, 0);
buf_add_string(&buf, path);
buf_add_path(&buf, path);
buf_add_uint32(&buf, SSH_FILEXFER_ATTR_SIZE);
buf_add_uint64(&buf, size);
err = sftp_request(SSH_FXP_SETSTAT, &buf, SSH_FXP_STATUS, NULL);
if (!err)
cache_invalidate(path);
buf_free(&buf);
return err;
}
@@ -695,112 +1012,108 @@ static int sshfs_utime(const char *path, struct utimbuf *ubuf)
{
int err;
struct buffer buf;
cache_remove(path);
buf_init(&buf, 0);
buf_add_string(&buf, path);
buf_add_path(&buf, path);
buf_add_uint32(&buf, SSH_FILEXFER_ATTR_ACMODTIME);
buf_add_uint32(&buf, ubuf->actime);
buf_add_uint32(&buf, ubuf->modtime);
err = sftp_request(SSH_FXP_SETSTAT, &buf, SSH_FXP_STATUS, NULL);
if (!err)
cache_invalidate(path);
buf_free(&buf);
return err;
}
static int sshfs_open(const char *path, int flags)
static int sshfs_open(const char *path, struct fuse_file_info *fi)
{
int err;
struct buffer buf;
struct buffer handle;
uint32_t pflags;
if ((flags & O_ACCMODE) == O_RDONLY)
struct buffer *handle;
uint32_t pflags = 0;
if ((fi->flags & O_ACCMODE) == O_RDONLY)
pflags = SSH_FXF_READ;
else if((flags & O_ACCMODE) == O_WRONLY)
else if((fi->flags & O_ACCMODE) == O_WRONLY)
pflags = SSH_FXF_WRITE;
else
else if ((fi->flags & O_ACCMODE) == O_RDWR)
pflags = SSH_FXF_READ | SSH_FXF_WRITE;
else
return -EINVAL;
handle = g_new0(struct buffer, 1);
buf_init(&buf, 0);
buf_add_string(&buf, path);
buf_add_path(&buf, path);
buf_add_uint32(&buf, pflags);
buf_add_uint32(&buf, 0);
err = sftp_request(SSH_FXP_OPEN, &buf, SSH_FXP_HANDLE, &handle);
err = sftp_request(SSH_FXP_OPEN, &buf, SSH_FXP_HANDLE, handle);
if (!err) {
int err2;
buf_finish(&handle);
err2 = sftp_request(SSH_FXP_CLOSE, &handle, SSH_FXP_STATUS, NULL);
if (!err)
err = err2;
buf_free(&handle);
}
buf_finish(handle);
fi->fh = (unsigned long) handle;
} else
g_free(handle);
buf_free(&buf);
return err;
}
static int sshfs_read(const char *path, char *rbuf, size_t size, off_t offset)
static int sshfs_release(const char *path, struct fuse_file_info *fi)
{
struct buffer *handle = (struct buffer *) fi->fh;
(void) path;
sftp_request(SSH_FXP_CLOSE, handle, 0, NULL);
buf_free(handle);
g_free(handle);
return 0;
}
static int sshfs_read(const char *path, char *rbuf, size_t size, off_t offset,
struct fuse_file_info *fi)
{
int err;
struct buffer buf;
struct buffer handle;
struct buffer data;
struct buffer *handle = (struct buffer *) fi->fh;
(void) path;
buf_init(&buf, 0);
buf_add_string(&buf, path);
buf_add_uint32(&buf, SSH_FXF_READ);
buf_add_uint32(&buf, 0);
err = sftp_request(SSH_FXP_OPEN, &buf, SSH_FXP_HANDLE, &handle);
buf_add_buf(&buf, handle);
buf_add_uint64(&buf, offset);
buf_add_uint32(&buf, size);
err = sftp_request(SSH_FXP_READ, &buf, SSH_FXP_DATA, &data);
if (!err) {
struct buffer data;
int err2;
buf_finish(&handle);
buf_add_uint64(&handle, offset);
buf_add_uint32(&handle, size);
err = sftp_request(SSH_FXP_READ, &handle, SSH_FXP_DATA, &data);
if (!err) {
uint32_t retsize;
err = -EPROTO;
if (buf_get_uint32(&data, &retsize) != -1) {
if (retsize > size)
fprintf(stderr, "long read\n");
else {
buf_get_mem(&data, rbuf, retsize);
err = retsize;
}
uint32_t retsize;
err = -EPROTO;
if (buf_get_uint32(&data, &retsize) != -1) {
if (retsize > size)
fprintf(stderr, "long read\n");
else {
buf_get_mem(&data, rbuf, retsize);
err = retsize;
}
buf_free(&data);
}
err2 = sftp_request(SSH_FXP_CLOSE, &handle, SSH_FXP_STATUS, NULL);
if (err2 && err >= 0)
err = err2;
buf_free(&handle);
}
buf_free(&data);
} else if (err == MY_EOF)
err = 0;
buf_free(&buf);
return err;
}
static int sshfs_write(const char *path, const char *wbuf, size_t size,
off_t offset)
off_t offset, struct fuse_file_info *fi)
{
int err;
struct buffer buf;
struct buffer handle;
struct buffer data;
struct buffer *handle = (struct buffer *) fi->fh;
data.p = (uint8_t *) wbuf;
data.len = size;
buf_init(&buf, 0);
buf_add_string(&buf, path);
buf_add_uint32(&buf, SSH_FXF_WRITE);
buf_add_uint32(&buf, 0);
err = sftp_request(SSH_FXP_OPEN, &buf, SSH_FXP_HANDLE, &handle);
if (!err) {
struct buffer data;
int err2;
data.p = (uint8_t *) wbuf;
data.len = size;
buf_finish(&handle);
buf_add_uint64(&handle, offset);
buf_add_data(&handle, &data);
err = sftp_request(SSH_FXP_WRITE, &handle, SSH_FXP_STATUS, NULL);
err2 = sftp_request(SSH_FXP_CLOSE, &handle, SSH_FXP_STATUS, NULL);
if (err2 && err >= 0)
err = err2;
buf_free(&handle);
}
buf_add_buf(&buf, handle);
buf_add_uint64(&buf, offset);
buf_add_data(&buf, &data);
err = sftp_request(SSH_FXP_WRITE, &buf, SSH_FXP_STATUS, NULL);
if (!err)
cache_invalidate(path);
buf_free(&buf);
return err;
return err ? err : (int) size;
}
static int sftp_init()
@@ -833,6 +1146,18 @@ static int sftp_init()
return res;
}
static int processing_init(void)
{
pthread_mutex_init(&lock, NULL);
reqtab = g_hash_table_new(NULL, NULL);
cache = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
if (!reqtab || !cache) {
fprintf(stderr, "failed to create hash tables\n");
return -1;
}
return 0;
}
static struct fuse_operations sshfs_oper = {
.getattr = sshfs_getattr,
.readlink = sshfs_readlink,
@@ -848,40 +1173,103 @@ static struct fuse_operations sshfs_oper = {
.truncate = sshfs_truncate,
.utime = sshfs_utime,
.open = sshfs_open,
.release = sshfs_release,
.read = sshfs_read,
.write = sshfs_write,
};
static void usage(const char *progname)
{
const char *fusehelp[] = { progname, "-ho", NULL };
fprintf(stderr,
"usage: %s [user@]host:[dir]] mountpoint [options]\n"
"\n"
"SSH Options:\n"
" -p port remote port\n"
" -c port directly connect to port bypassing ssh\n"
"\n", progname);
fuse_main(2, (char **) fusehelp, &sshfs_oper);
exit(1);
}
int main(int argc, char *argv[])
{
char *host;
char *host = NULL;
char *port = NULL;
char *fsname;
int res;
int argctr;
char **newargv;
int direct = 0;
int newargc = 0;
char **newargv = (char **) malloc((argc + 10) * sizeof(char *));
newargv[newargc++] = argv[0];
if (argc < 3) {
fprintf(stderr, "usage: %s [user@]host mountpoint [mount options]\n",
argv[0]);
for (argctr = 1; argctr < argc; argctr++) {
char *arg = argv[argctr];
if (arg[0] == '-') {
switch (arg[1]) {
case 'c':
direct = 1;
/* fallthrough */
case 'p':
if (arg[2])
port = &arg[2];
else if (argctr + 1 < argc)
port = argv[++argctr];
else {
fprintf(stderr, "missing argument to %s option\n", arg);
fprintf(stderr, "see `%s -h' for usage\n", argv[0]);
exit(1);
}
break;
case 'h':
usage(argv[0]);
break;
default:
newargv[newargc++] = arg;
}
} else if (!host && strchr(arg, ':'))
host = g_strdup(arg);
else
newargv[newargc++] = arg;
}
if (!host) {
fprintf(stderr, "missing host\n");
fprintf(stderr, "see `%s -h' for usage\n", argv[0]);
exit(1);
}
host = argv[1];
res = start_ssh(host);
fsname = g_strdup(host);
base_path = strchr(host, ':');
*base_path++ = '\0';
if (base_path[0] && base_path[strlen(base_path)-1] != '/')
base_path = g_strdup_printf("%s/", base_path);
else
base_path = g_strdup(base_path);
if (!direct)
res = start_ssh(host, port);
else
res = connect_to(host, port);
if (res == -1)
exit(1);
g_free(host);
res = sftp_init();
if (res == -1)
exit(1);
newargv = (char **) malloc((argc + 10) * sizeof(char *));
newargv[0] = argv[0];
for (argctr = 1; argctr < argc - 1; argctr++)
newargv[argctr] = argv[argctr + 1];
newargv[argctr++] = "-s";
newargv[argctr++] = "-omax_read=65536";
newargv[argctr] = NULL;
return fuse_main(argctr, newargv, &sshfs_oper);
res = processing_init();
if (res == -1)
exit(1);
newargv[newargc++] = "-omax_read=65536";
newargv[newargc++] = g_strdup_printf("-ofsname=sshfs#%s", fsname);
g_free(fsname);
newargv[newargc] = NULL;
return fuse_main(newargc, newargv, &sshfs_oper);
}