ssshfs is obsoleted by mount_psshfs
This commit is contained in:
parent
c3ef8ea5d0
commit
fb7890b666
@ -1,19 +0,0 @@
|
||||
# $NetBSD: Makefile,v 1.1 2006/11/21 00:54:06 pooka Exp $
|
||||
|
||||
NOMAN= 1
|
||||
|
||||
.include <bsd.own.mk>
|
||||
|
||||
SSHDIST= ${NETBSDSRCDIR}/crypto/dist/ssh
|
||||
.PATH: ${SSHDIST}
|
||||
|
||||
PROG= ssshfs
|
||||
SRCS= ssshfs.c sftp-getput.c sftp-connect.c sftp-client.c \
|
||||
sftp-common.c sftp-glob.c misc.c
|
||||
LDADD+= -lpuffs -lutil -lssh -lcrypto -lcrypt -lz
|
||||
CPPFLAGS+= -I${SSHDIST}
|
||||
DBG=-g -O0
|
||||
|
||||
WARNS= 0 # XXX: openssh code
|
||||
|
||||
.include <bsd.prog.mk>
|
@ -1,292 +0,0 @@
|
||||
/* $NetBSD: sftp-connect.c,v 1.2 2006/11/21 23:09:23 pooka Exp $ */
|
||||
|
||||
/* NetBSD: sftp.c,v 1.21 2006/09/28 21:22:15 christos Exp */
|
||||
/* $OpenBSD: sftp.c,v 1.91 2006/08/03 03:34:42 deraadt Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
__RCSID("$NetBSD: sftp-connect.c,v 1.2 2006/11/21 23:09:23 pooka Exp $");
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <glob.h>
|
||||
#include <histedit.h>
|
||||
#include <paths.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "xmalloc.h"
|
||||
#include "log.h"
|
||||
#include "pathnames.h"
|
||||
#include "misc.h"
|
||||
|
||||
#include "sftp.h"
|
||||
#include "buffer.h"
|
||||
#include "sftp-common.h"
|
||||
#include "sftp-client.h"
|
||||
|
||||
/* File to read commands from */
|
||||
FILE* infile;
|
||||
|
||||
/* Are we in batchfile mode? */
|
||||
int batchmode = 0;
|
||||
|
||||
/* Size of buffer used when copying files */
|
||||
size_t copy_buffer_len = 32768;
|
||||
|
||||
/* Number of concurrent outstanding requests */
|
||||
size_t num_requests = 16;
|
||||
|
||||
/* PID of ssh transport process */
|
||||
static pid_t sshpid = -1;
|
||||
|
||||
/* This is set to 0 if the progressmeter is not desired. */
|
||||
int showprogress = 1;
|
||||
|
||||
/* SIGINT received during command processing */
|
||||
volatile sig_atomic_t interrupted = 0;
|
||||
|
||||
static void
|
||||
killchild(int signo)
|
||||
{
|
||||
if (sshpid > 1) {
|
||||
kill(sshpid, SIGTERM);
|
||||
waitpid(sshpid, NULL, 0);
|
||||
}
|
||||
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
static void
|
||||
connect_to_server(char *path, char **args, int *in, int *out)
|
||||
{
|
||||
int c_in, c_out;
|
||||
|
||||
int inout[2];
|
||||
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1)
|
||||
fatal("socketpair: %s", strerror(errno));
|
||||
*in = *out = inout[0];
|
||||
c_in = c_out = inout[1];
|
||||
|
||||
if ((sshpid = fork()) == -1)
|
||||
fatal("fork: %s", strerror(errno));
|
||||
else if (sshpid == 0) {
|
||||
if ((dup2(c_in, STDIN_FILENO) == -1) ||
|
||||
(dup2(c_out, STDOUT_FILENO) == -1)) {
|
||||
fprintf(stderr, "dup2: %s\n", strerror(errno));
|
||||
_exit(1);
|
||||
}
|
||||
close(*in);
|
||||
close(*out);
|
||||
close(c_in);
|
||||
close(c_out);
|
||||
|
||||
/*
|
||||
* The underlying ssh is in the same process group, so we must
|
||||
* ignore SIGINT if we want to gracefully abort commands,
|
||||
* otherwise the signal will make it to the ssh process and
|
||||
* kill it too
|
||||
*/
|
||||
signal(SIGINT, SIG_IGN);
|
||||
execvp(path, args);
|
||||
fprintf(stderr, "exec: %s: %s\n", path, strerror(errno));
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
signal(SIGTERM, killchild);
|
||||
signal(SIGINT, killchild);
|
||||
signal(SIGHUP, killchild);
|
||||
close(c_in);
|
||||
close(c_out);
|
||||
}
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
extern char *__progname;
|
||||
|
||||
fprintf(stderr,
|
||||
"usage: %s [-1Cv] [-B buffer_size] [-b batchfile] [-F ssh_config]\n"
|
||||
" [-o ssh_option] [-P sftp_server_path] [-R num_requests]\n"
|
||||
" [-S program] [-s subsystem | sftp_server] host\n"
|
||||
" %s [[user@]host[:file [file]]]\n"
|
||||
" %s [[user@]host[:dir[/]]]\n"
|
||||
" %s -b batchfile [user@]host\n", __progname, __progname, __progname, __progname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int sftp_main(int, char *[]);
|
||||
int
|
||||
sftp_main(int argc, char **argv)
|
||||
{
|
||||
int ch;
|
||||
extern int in, out;
|
||||
char *host, *userhost, *cp, *file2 = NULL;
|
||||
int debug_level = 0, sshver = 2;
|
||||
char *sftp_server = NULL;
|
||||
char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL;
|
||||
LogLevel ll = SYSLOG_LEVEL_INFO;
|
||||
arglist args;
|
||||
extern int optind;
|
||||
extern char *optarg;
|
||||
extern char *argpath;
|
||||
|
||||
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
|
||||
sanitise_stdfd();
|
||||
|
||||
memset(&args, '\0', sizeof(args));
|
||||
args.list = NULL;
|
||||
addargs(&args, "%s", ssh_program);
|
||||
addargs(&args, "-oForwardX11 no");
|
||||
addargs(&args, "-oForwardAgent no");
|
||||
addargs(&args, "-oPermitLocalCommand no");
|
||||
addargs(&args, "-oClearAllForwardings yes");
|
||||
|
||||
ll = SYSLOG_LEVEL_INFO;
|
||||
infile = stdin;
|
||||
|
||||
while ((ch = getopt(argc, argv, "1hvCo:s:S:b:B:F:P:R:")) != -1) {
|
||||
switch (ch) {
|
||||
case 'C':
|
||||
addargs(&args, "-C");
|
||||
break;
|
||||
case 'v':
|
||||
if (debug_level < 3) {
|
||||
addargs(&args, "-v");
|
||||
ll = SYSLOG_LEVEL_DEBUG1 + debug_level;
|
||||
}
|
||||
debug_level++;
|
||||
break;
|
||||
case 'F':
|
||||
case 'o':
|
||||
addargs(&args, "-%c%s", ch, optarg);
|
||||
break;
|
||||
case '1':
|
||||
sshver = 1;
|
||||
if (sftp_server == NULL)
|
||||
sftp_server = _PATH_SFTP_SERVER;
|
||||
break;
|
||||
case 's':
|
||||
sftp_server = optarg;
|
||||
break;
|
||||
case 'S':
|
||||
ssh_program = optarg;
|
||||
replacearg(&args, 0, "%s", ssh_program);
|
||||
break;
|
||||
case 'b':
|
||||
if (batchmode)
|
||||
fatal("Batch file already specified.");
|
||||
|
||||
/* Allow "-" as stdin */
|
||||
if (strcmp(optarg, "-") != 0 &&
|
||||
(infile = fopen(optarg, "r")) == NULL)
|
||||
fatal("%s (%s).", strerror(errno), optarg);
|
||||
showprogress = 0;
|
||||
batchmode = 1;
|
||||
addargs(&args, "-obatchmode yes");
|
||||
break;
|
||||
case 'P':
|
||||
sftp_direct = optarg;
|
||||
break;
|
||||
case 'B':
|
||||
copy_buffer_len = strtol(optarg, &cp, 10);
|
||||
if (copy_buffer_len == 0 || *cp != '\0')
|
||||
fatal("Invalid buffer size \"%s\"", optarg);
|
||||
break;
|
||||
case 'R':
|
||||
num_requests = strtol(optarg, &cp, 10);
|
||||
if (num_requests == 0 || *cp != '\0')
|
||||
fatal("Invalid number of requests \"%s\"",
|
||||
optarg);
|
||||
break;
|
||||
case 'h':
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isatty(STDERR_FILENO))
|
||||
showprogress = 0;
|
||||
|
||||
log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1);
|
||||
|
||||
if (sftp_direct == NULL) {
|
||||
#if 0
|
||||
if (optind == argc || argc > (optind + 2))
|
||||
usage();
|
||||
#endif
|
||||
|
||||
userhost = xstrdup(argv[optind]);
|
||||
file2 = argv[optind+1];
|
||||
|
||||
if ((host = strrchr(userhost, '@')) == NULL)
|
||||
host = userhost;
|
||||
else {
|
||||
*host++ = '\0';
|
||||
if (!userhost[0]) {
|
||||
fprintf(stderr, "Missing username\n");
|
||||
usage();
|
||||
}
|
||||
addargs(&args, "-l%s",userhost);
|
||||
}
|
||||
|
||||
if ((cp = colon(host)) != NULL) {
|
||||
*cp++ = '\0';
|
||||
argpath = cp;
|
||||
}
|
||||
|
||||
host = cleanhostname(host);
|
||||
if (!*host) {
|
||||
fprintf(stderr, "Missing hostname\n");
|
||||
usage();
|
||||
}
|
||||
|
||||
addargs(&args, "-oProtocol %d", sshver);
|
||||
|
||||
/* no subsystem if the server-spec contains a '/' */
|
||||
if (sftp_server == NULL || strchr(sftp_server, '/') == NULL)
|
||||
addargs(&args, "-s");
|
||||
|
||||
addargs(&args, "%s", host);
|
||||
addargs(&args, "%s", (sftp_server != NULL ?
|
||||
sftp_server : "sftp"));
|
||||
|
||||
if (!batchmode)
|
||||
fprintf(stderr, "Connecting to %s...\n", host);
|
||||
connect_to_server(ssh_program, args.list, &in, &out);
|
||||
} else {
|
||||
args.list = NULL;
|
||||
addargs(&args, "sftp-server");
|
||||
|
||||
if (!batchmode)
|
||||
fprintf(stderr, "Attaching to %s...\n", sftp_direct);
|
||||
connect_to_server(sftp_direct, args.list, &in, &out);
|
||||
}
|
||||
freeargs(&args);
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,580 +0,0 @@
|
||||
/* $NetBSD: sftp-getput.c,v 1.3 2006/12/05 23:14:13 pooka Exp $ */
|
||||
|
||||
/* NetBSD: sftp-client.c,v 1.26 2006/09/28 21:22:15 christos Exp */
|
||||
/* $OpenBSD: sftp-client.c,v 1.74 2006/08/03 03:34:42 deraadt Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* hacked, mangled & maimed for ssshfs by pooka */
|
||||
|
||||
/* XXX: memleaks */
|
||||
/* XXX: signed vs unsigned */
|
||||
/* XXX: remove all logging, only return status codes */
|
||||
/* XXX: copy between two remote sites */
|
||||
|
||||
#include "includes.h"
|
||||
__RCSID("$NetBSD: sftp-getput.c,v 1.3 2006/12/05 23:14:13 pooka Exp $");
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "xmalloc.h"
|
||||
#include "buffer.h"
|
||||
#include "log.h"
|
||||
#include "atomicio.h"
|
||||
#include "misc.h"
|
||||
|
||||
#include "sftp.h"
|
||||
#include "sftp-common.h"
|
||||
#include "sftp-client.h"
|
||||
|
||||
extern volatile sig_atomic_t interrupted;
|
||||
|
||||
/* Minimum amount of data to read at a time */
|
||||
#define MIN_READ_SIZE 512
|
||||
|
||||
struct sftp_conn {
|
||||
int fd_in;
|
||||
int fd_out;
|
||||
u_int transfer_buflen;
|
||||
u_int num_requests;
|
||||
u_int version;
|
||||
u_int msg_id;
|
||||
};
|
||||
|
||||
static void
|
||||
send_msg(int fd, Buffer *m)
|
||||
{
|
||||
u_char mlen[4];
|
||||
struct iovec iov[2];
|
||||
|
||||
if (buffer_len(m) > SFTP_MAX_MSG_LENGTH)
|
||||
fatal("Outbound message too long %u", buffer_len(m));
|
||||
|
||||
/* Send length first */
|
||||
put_u32(mlen, buffer_len(m));
|
||||
iov[0].iov_base = mlen;
|
||||
iov[0].iov_len = sizeof(mlen);
|
||||
iov[1].iov_base = buffer_ptr(m);
|
||||
iov[1].iov_len = buffer_len(m);
|
||||
|
||||
if (atomiciov(writev, fd, iov, 2) != buffer_len(m) + sizeof(mlen))
|
||||
fatal("Couldn't send packet: %s", strerror(errno));
|
||||
|
||||
buffer_clear(m);
|
||||
}
|
||||
|
||||
static void
|
||||
get_msg(int fd, Buffer *m)
|
||||
{
|
||||
u_int msg_len;
|
||||
|
||||
buffer_append_space(m, 4);
|
||||
if (atomicio(read, fd, buffer_ptr(m), 4) != 4) {
|
||||
if (errno == EPIPE)
|
||||
fatal("Connection closed");
|
||||
else
|
||||
fatal("Couldn't read packet: %s", strerror(errno));
|
||||
}
|
||||
|
||||
msg_len = buffer_get_int(m);
|
||||
if (msg_len > SFTP_MAX_MSG_LENGTH)
|
||||
fatal("Received message too long %u", msg_len);
|
||||
|
||||
buffer_append_space(m, msg_len);
|
||||
if (atomicio(read, fd, buffer_ptr(m), msg_len) != msg_len) {
|
||||
if (errno == EPIPE)
|
||||
fatal("Connection closed");
|
||||
else
|
||||
fatal("Read packet: %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
|
||||
static u_int
|
||||
get_status(int fd, u_int expected_id)
|
||||
{
|
||||
Buffer msg;
|
||||
u_int type, id, status;
|
||||
|
||||
buffer_init(&msg);
|
||||
get_msg(fd, &msg);
|
||||
type = buffer_get_char(&msg);
|
||||
id = buffer_get_int(&msg);
|
||||
|
||||
if (id != expected_id)
|
||||
fatal("ID mismatch (%u != %u)", id, expected_id);
|
||||
if (type != SSH2_FXP_STATUS)
|
||||
fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u",
|
||||
SSH2_FXP_STATUS, type);
|
||||
|
||||
status = buffer_get_int(&msg);
|
||||
buffer_free(&msg);
|
||||
|
||||
debug3("SSH2_FXP_STATUS %u", status);
|
||||
|
||||
return(status);
|
||||
}
|
||||
|
||||
static char *
|
||||
get_handle(int fd, u_int expected_id, u_int *len)
|
||||
{
|
||||
Buffer msg;
|
||||
u_int type, id;
|
||||
char *handle;
|
||||
|
||||
buffer_init(&msg);
|
||||
get_msg(fd, &msg);
|
||||
type = buffer_get_char(&msg);
|
||||
id = buffer_get_int(&msg);
|
||||
|
||||
if (id != expected_id)
|
||||
fatal("ID mismatch (%u != %u)", id, expected_id);
|
||||
if (type == SSH2_FXP_STATUS) {
|
||||
int status = buffer_get_int(&msg);
|
||||
|
||||
error("Couldn't get handle: %s", fx2txt(status));
|
||||
buffer_free(&msg);
|
||||
return(NULL);
|
||||
} else if (type != SSH2_FXP_HANDLE)
|
||||
fatal("Expected SSH2_FXP_HANDLE(%u) packet, got %u",
|
||||
SSH2_FXP_HANDLE, type);
|
||||
|
||||
handle = buffer_get_string(&msg, len);
|
||||
buffer_free(&msg);
|
||||
|
||||
return(handle);
|
||||
}
|
||||
|
||||
static Attrib *
|
||||
get_decode_stat(int fd, u_int expected_id, int quiet)
|
||||
{
|
||||
Buffer msg;
|
||||
u_int type, id;
|
||||
Attrib *a;
|
||||
|
||||
buffer_init(&msg);
|
||||
get_msg(fd, &msg);
|
||||
|
||||
type = buffer_get_char(&msg);
|
||||
id = buffer_get_int(&msg);
|
||||
|
||||
debug3("Received stat reply T:%u I:%u", type, id);
|
||||
if (id != expected_id)
|
||||
fatal("ID mismatch (%u != %u)", id, expected_id);
|
||||
if (type == SSH2_FXP_STATUS) {
|
||||
int status = buffer_get_int(&msg);
|
||||
|
||||
if (quiet)
|
||||
debug("Couldn't stat remote file: %s", fx2txt(status));
|
||||
else
|
||||
error("Couldn't stat remote file: %s", fx2txt(status));
|
||||
buffer_free(&msg);
|
||||
return(NULL);
|
||||
} else if (type != SSH2_FXP_ATTRS) {
|
||||
fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u",
|
||||
SSH2_FXP_ATTRS, type);
|
||||
}
|
||||
a = decode_attrib(&msg);
|
||||
buffer_free(&msg);
|
||||
|
||||
return(a);
|
||||
}
|
||||
|
||||
static void
|
||||
send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len,
|
||||
char *handle, u_int handle_len)
|
||||
{
|
||||
Buffer msg;
|
||||
|
||||
buffer_init(&msg);
|
||||
buffer_clear(&msg);
|
||||
buffer_put_char(&msg, SSH2_FXP_READ);
|
||||
buffer_put_int(&msg, id);
|
||||
buffer_put_string(&msg, handle, handle_len);
|
||||
buffer_put_int64(&msg, offset);
|
||||
buffer_put_int(&msg, len);
|
||||
send_msg(fd_out, &msg);
|
||||
buffer_free(&msg);
|
||||
}
|
||||
|
||||
int
|
||||
do_creatfile(struct sftp_conn *conn, const char *path, const Attrib *a)
|
||||
{
|
||||
Buffer msg;
|
||||
char *handle;
|
||||
u_int handle_len;
|
||||
int id;
|
||||
|
||||
buffer_init(&msg);
|
||||
id = conn->msg_id++;
|
||||
buffer_put_char(&msg, SSH2_FXP_OPEN);
|
||||
buffer_put_int(&msg, id);
|
||||
buffer_put_cstring(&msg, path);
|
||||
buffer_put_int(&msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT|SSH2_FXF_TRUNC);
|
||||
encode_attrib(&msg, a);
|
||||
send_msg(conn->fd_out, &msg);
|
||||
|
||||
handle = get_handle(conn->fd_in, id, &handle_len);
|
||||
buffer_free(&msg);
|
||||
|
||||
if (handle == NULL)
|
||||
return EIO;
|
||||
|
||||
do_close(conn, handle, handle_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
do_readfile(struct sftp_conn *conn, char *path, uint8_t *localbuf,
|
||||
off_t getoff, size_t *getlen)
|
||||
{
|
||||
Attrib junk, *a;
|
||||
Buffer msg;
|
||||
char *handle;
|
||||
int status = 0, write_error;
|
||||
int read_error, write_errno;
|
||||
u_int64_t offset, size;
|
||||
u_int handle_len, mode, type, id, buflen, num_req, max_req;
|
||||
struct request {
|
||||
u_int id;
|
||||
u_int len;
|
||||
u_int64_t offset;
|
||||
TAILQ_ENTRY(request) tq;
|
||||
};
|
||||
TAILQ_HEAD(reqhead, request) requests;
|
||||
struct request *req;
|
||||
|
||||
status = -1;
|
||||
TAILQ_INIT(&requests);
|
||||
|
||||
a = do_stat(conn, path, 0);
|
||||
if (a == NULL)
|
||||
return(-1);
|
||||
|
||||
/* XXX: should we preserve set[ug]id? */
|
||||
if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
|
||||
mode = a->perm & 0777;
|
||||
else
|
||||
mode = 0666;
|
||||
|
||||
if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) &&
|
||||
(!S_ISREG(a->perm))) {
|
||||
error("Cannot download non-regular file: %s", path);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
if (a->flags & SSH2_FILEXFER_ATTR_SIZE)
|
||||
size = a->size;
|
||||
else
|
||||
size = 0;
|
||||
|
||||
buflen = conn->transfer_buflen;
|
||||
buffer_init(&msg);
|
||||
|
||||
/* Send open request */
|
||||
id = conn->msg_id++;
|
||||
buffer_put_char(&msg, SSH2_FXP_OPEN);
|
||||
buffer_put_int(&msg, id);
|
||||
buffer_put_cstring(&msg, path);
|
||||
buffer_put_int(&msg, SSH2_FXF_READ);
|
||||
attrib_clear(&junk); /* Send empty attributes */
|
||||
encode_attrib(&msg, &junk);
|
||||
send_msg(conn->fd_out, &msg);
|
||||
debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, path);
|
||||
|
||||
handle = get_handle(conn->fd_in, id, &handle_len);
|
||||
if (handle == NULL) {
|
||||
buffer_free(&msg);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
/* Read from remote and write to local */
|
||||
write_error = read_error = write_errno = num_req = 0;
|
||||
offset = getoff;
|
||||
max_req = 1;
|
||||
|
||||
while (num_req > 0 || max_req > 0) {
|
||||
char *data;
|
||||
u_int len;
|
||||
|
||||
/* Send some more requests */
|
||||
while ((num_req < max_req) && (offset < getoff + *getlen)) {
|
||||
req = xmalloc(sizeof(*req));
|
||||
req->id = conn->msg_id++;
|
||||
req->len = buflen;
|
||||
req->offset = offset;
|
||||
num_req++;
|
||||
|
||||
/* adjust for tail */
|
||||
if ((req->len + req->offset) >= (getoff + *getlen)) {
|
||||
req->len = getoff+*getlen - req->offset;
|
||||
max_req = 0;
|
||||
}
|
||||
|
||||
debug3("Request range %llu -> %llu (%d/%d)",
|
||||
(unsigned long long)offset,
|
||||
(unsigned long long)offset + req->len - 1,
|
||||
num_req, max_req);
|
||||
|
||||
offset += req->len;
|
||||
|
||||
TAILQ_INSERT_TAIL(&requests, req, tq);
|
||||
send_read_request(conn->fd_out, req->id, req->offset,
|
||||
req->len, handle, handle_len);
|
||||
}
|
||||
|
||||
buffer_clear(&msg);
|
||||
get_msg(conn->fd_in, &msg);
|
||||
type = buffer_get_char(&msg);
|
||||
id = buffer_get_int(&msg);
|
||||
debug3("Received reply T:%u I:%u R:%d", type, id, max_req);
|
||||
|
||||
/* Find the request in our queue */
|
||||
for (req = TAILQ_FIRST(&requests);
|
||||
req != NULL && req->id != id;
|
||||
req = TAILQ_NEXT(req, tq))
|
||||
;
|
||||
if (req == NULL)
|
||||
fatal("Unexpected reply %u", id);
|
||||
|
||||
switch (type) {
|
||||
case SSH2_FXP_STATUS:
|
||||
status = buffer_get_int(&msg);
|
||||
if (status != SSH2_FX_EOF)
|
||||
read_error = 1;
|
||||
max_req = 0;
|
||||
TAILQ_REMOVE(&requests, req, tq);
|
||||
xfree(req);
|
||||
num_req--;
|
||||
break;
|
||||
case SSH2_FXP_DATA:
|
||||
data = buffer_get_string(&msg, &len);
|
||||
debug3("Received data %llu -> %llu",
|
||||
(unsigned long long)req->offset,
|
||||
(unsigned long long)req->offset + len - 1);
|
||||
if (len > req->len)
|
||||
fatal("Received more data than asked for "
|
||||
"%u > %u", len, req->len);
|
||||
memcpy(localbuf + (req->offset - getoff), data, len);
|
||||
xfree(data);
|
||||
|
||||
if (len == req->len) {
|
||||
TAILQ_REMOVE(&requests, req, tq);
|
||||
xfree(req);
|
||||
num_req--;
|
||||
} else {
|
||||
/* Resend the request for the missing data */
|
||||
debug3("Short data block, re-requesting "
|
||||
"%llu -> %llu (%2d)",
|
||||
(unsigned long long)req->offset + len,
|
||||
(unsigned long long)req->offset +
|
||||
req->len - 1, num_req);
|
||||
req->id = conn->msg_id++;
|
||||
req->len -= len;
|
||||
req->offset += len;
|
||||
send_read_request(conn->fd_out, req->id,
|
||||
req->offset, req->len, handle, handle_len);
|
||||
/* Reduce the request size */
|
||||
if (len < buflen)
|
||||
buflen = MAX(MIN_READ_SIZE, len);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
fatal("Expected SSH2_FXP_DATA(%u) packet, got %u",
|
||||
SSH2_FXP_DATA, type);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sanity check */
|
||||
if (TAILQ_FIRST(&requests) != NULL)
|
||||
fatal("Transfer complete, but requests still in queue");
|
||||
|
||||
if (read_error) {
|
||||
error("Couldn't read from remote file \"%s\" : %s",
|
||||
path, fx2txt(status));
|
||||
do_close(conn, handle, handle_len);
|
||||
} else {
|
||||
status = do_close(conn, handle, handle_len);
|
||||
}
|
||||
*getlen -= (offset - getoff);
|
||||
buffer_free(&msg);
|
||||
xfree(handle);
|
||||
|
||||
return(status);
|
||||
}
|
||||
|
||||
int
|
||||
do_writefile(struct sftp_conn *conn, char *path, uint8_t *localbuf,
|
||||
off_t putoff, size_t *putlen, int append)
|
||||
{
|
||||
int status;
|
||||
u_int handle_len, id, type;
|
||||
u_int64_t offset;
|
||||
char *handle, *data;
|
||||
Buffer msg;
|
||||
Attrib a, savea;
|
||||
Attrib *ap;
|
||||
u_int32_t startid;
|
||||
u_int32_t ackid;
|
||||
struct outstanding_ack {
|
||||
u_int id;
|
||||
u_int len;
|
||||
u_int64_t offset;
|
||||
TAILQ_ENTRY(outstanding_ack) tq;
|
||||
};
|
||||
TAILQ_HEAD(ackhead, outstanding_ack) acks;
|
||||
struct outstanding_ack *ack = NULL;
|
||||
|
||||
TAILQ_INIT(&acks);
|
||||
|
||||
/* XXX: temporarily set file permission to allow writing */
|
||||
ap = do_stat(conn, path, 1);
|
||||
if (!ap)
|
||||
return -1;
|
||||
|
||||
if (append)
|
||||
putoff = ap->size;
|
||||
|
||||
memcpy(&savea, ap, sizeof(Attrib));
|
||||
savea.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
|
||||
|
||||
memset(&a, 0, sizeof(a));
|
||||
a.flags = SSH2_FILEXFER_ATTR_PERMISSIONS;
|
||||
a.perm = 0777;
|
||||
if (do_setstat(conn, path, &a)) {
|
||||
printf("lossage\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
buffer_init(&msg);
|
||||
|
||||
/* Send open request */
|
||||
id = conn->msg_id++;
|
||||
buffer_put_char(&msg, SSH2_FXP_OPEN);
|
||||
buffer_put_int(&msg, id);
|
||||
buffer_put_cstring(&msg, path);
|
||||
buffer_put_int(&msg, SSH2_FXF_WRITE | SSH2_FXF_CREAT);
|
||||
encode_attrib(&msg, &a);
|
||||
send_msg(conn->fd_out, &msg);
|
||||
debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, path);
|
||||
|
||||
buffer_clear(&msg);
|
||||
|
||||
handle = get_handle(conn->fd_in, id, &handle_len);
|
||||
if (handle == NULL) {
|
||||
do_setstat(conn, path, &savea);
|
||||
buffer_free(&msg);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
startid = ackid = id + 1;
|
||||
data = xmalloc(conn->transfer_buflen);
|
||||
|
||||
offset = putoff;
|
||||
|
||||
for (;;) {
|
||||
size_t len;
|
||||
|
||||
len = conn->transfer_buflen;
|
||||
if (offset + len > putoff + *putlen)
|
||||
len = (putoff + *putlen) - offset;
|
||||
|
||||
if (len != 0) {
|
||||
memcpy(data, localbuf + (offset - putoff), len);
|
||||
ack = xmalloc(sizeof(*ack));
|
||||
ack->id = ++id;
|
||||
ack->offset = offset;
|
||||
ack->len = len;
|
||||
TAILQ_INSERT_TAIL(&acks, ack, tq);
|
||||
|
||||
buffer_clear(&msg);
|
||||
buffer_put_char(&msg, SSH2_FXP_WRITE);
|
||||
buffer_put_int(&msg, ack->id);
|
||||
buffer_put_string(&msg, handle, handle_len);
|
||||
buffer_put_int64(&msg, offset);
|
||||
buffer_put_string(&msg, data, len);
|
||||
send_msg(conn->fd_out, &msg);
|
||||
debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u",
|
||||
id, (unsigned long long)offset, len);
|
||||
} else if (TAILQ_FIRST(&acks) == NULL)
|
||||
break;
|
||||
|
||||
if (ack == NULL)
|
||||
fatal("Unexpected ACK %u", id);
|
||||
|
||||
if (id == startid || len == 0 ||
|
||||
id - ackid >= conn->num_requests) {
|
||||
u_int r_id;
|
||||
|
||||
buffer_clear(&msg);
|
||||
get_msg(conn->fd_in, &msg);
|
||||
type = buffer_get_char(&msg);
|
||||
r_id = buffer_get_int(&msg);
|
||||
|
||||
if (type != SSH2_FXP_STATUS)
|
||||
fatal("Expected SSH2_FXP_STATUS(%d) packet, "
|
||||
"got %d", SSH2_FXP_STATUS, type);
|
||||
|
||||
status = buffer_get_int(&msg);
|
||||
debug3("SSH2_FXP_STATUS %d", status);
|
||||
|
||||
/* Find the request in our queue */
|
||||
for (ack = TAILQ_FIRST(&acks);
|
||||
ack != NULL && ack->id != r_id;
|
||||
ack = TAILQ_NEXT(ack, tq))
|
||||
;
|
||||
if (ack == NULL)
|
||||
fatal("Can't find request for ID %u", r_id);
|
||||
TAILQ_REMOVE(&acks, ack, tq);
|
||||
|
||||
if (status != SSH2_FX_OK) {
|
||||
error("Couldn't write to remote file \"%s\": %s",
|
||||
path, fx2txt(status));
|
||||
do_close(conn, handle, handle_len);
|
||||
xfree(data);
|
||||
xfree(ack);
|
||||
goto done;
|
||||
}
|
||||
debug3("In write loop, ack for %u %u bytes at %llu",
|
||||
ack->id, ack->len, (unsigned long long)ack->offset);
|
||||
++ackid;
|
||||
xfree(ack);
|
||||
}
|
||||
offset += len;
|
||||
}
|
||||
xfree(data);
|
||||
|
||||
status = do_close(conn, handle, handle_len);
|
||||
*putlen -= (offset - putoff);
|
||||
|
||||
done:
|
||||
do_setstat(conn, path, &savea);
|
||||
xfree(handle);
|
||||
buffer_free(&msg);
|
||||
return(status);
|
||||
}
|
@ -1,649 +0,0 @@
|
||||
/* $NetBSD: ssshfs.c,v 1.10 2006/12/07 10:54:29 pooka Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2006 Antti Kantee. All Rights Reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the company nor the name of the author may be used to
|
||||
* endorse or promote products derived from this software without specific
|
||||
* prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
|
||||
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* simple sshfs
|
||||
* (silly sshfs? stupid sshfs? snappy sshfs? sucky sshfs? seven sshfs???)
|
||||
* (sante sshfs? severed (dreams) sshfs? saucy sshfs? sauerkraut sshfs?)
|
||||
*/
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <puffs.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <util.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "buffer.h"
|
||||
#include "sftp.h"
|
||||
#include "sftp-common.h"
|
||||
#include "sftp-client.h"
|
||||
|
||||
PUFFSOP_PROTOS(ssshfs)
|
||||
|
||||
struct ssshnode {
|
||||
struct ssshnode *dotdot;
|
||||
int childcount;
|
||||
ino_t myid;
|
||||
|
||||
char name[MAXPATHLEN+1];
|
||||
size_t namelen;
|
||||
|
||||
SFTP_DIRENT **ents;
|
||||
int dcache;
|
||||
|
||||
struct vattr va;
|
||||
};
|
||||
#define DCACHE_EXISTS 0x1
|
||||
#define DCACHE_CHANGED 0x2
|
||||
|
||||
static struct sftp_conn *sftpc;
|
||||
int in, out;
|
||||
|
||||
static struct ssshnode rn;
|
||||
static ino_t nextid = 3;
|
||||
|
||||
extern int sftp_main(int argc, char *argv[]);
|
||||
|
||||
char *argpath; /* XXX: arg passing nightmare */
|
||||
|
||||
/*
|
||||
* uberquickhack one-person uidgid-mangler in case the target
|
||||
* system doesn't have the same uids and gids
|
||||
*/
|
||||
static int mangle = 0;
|
||||
static uid_t uidmangle_from = 1323, uidmangle_to = 5988;
|
||||
static uid_t gidmangle_from = 100, gidmangle_to = 806;
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct puffs_usermount *pu;
|
||||
struct puffs_ops pops;
|
||||
char *mountpath;
|
||||
|
||||
setprogname(argv[0]);
|
||||
|
||||
if (argc < 3)
|
||||
errx(1, "usage: %s user@host:path mountpath", getprogname());
|
||||
|
||||
PUFFSOP_INIT(&pops);
|
||||
|
||||
PUFFSOP_SET(&pops, ssshfs, fs, mount);
|
||||
PUFFSOP_SET(&pops, ssshfs, fs, unmount);
|
||||
PUFFSOP_SETFSNOP(&pops, sync); /* XXX */
|
||||
PUFFSOP_SETFSNOP(&pops, statvfs);
|
||||
|
||||
PUFFSOP_SET(&pops, ssshfs, node, lookup);
|
||||
PUFFSOP_SET(&pops, ssshfs, node, getattr);
|
||||
PUFFSOP_SET(&pops, ssshfs, node, setattr);
|
||||
PUFFSOP_SET(&pops, ssshfs, node, readdir);
|
||||
PUFFSOP_SET(&pops, ssshfs, node, symlink);
|
||||
PUFFSOP_SET(&pops, ssshfs, node, readlink);
|
||||
PUFFSOP_SET(&pops, ssshfs, node, remove);
|
||||
PUFFSOP_SET(&pops, ssshfs, node, create);
|
||||
PUFFSOP_SET(&pops, ssshfs, node, mkdir);
|
||||
PUFFSOP_SET(&pops, ssshfs, node, rmdir);
|
||||
PUFFSOP_SET(&pops, ssshfs, node, read);
|
||||
PUFFSOP_SET(&pops, ssshfs, node, write);
|
||||
PUFFSOP_SET(&pops, ssshfs, node, rename);
|
||||
PUFFSOP_SET(&pops, ssshfs, node, reclaim);
|
||||
|
||||
mountpath = argv[--argc]; /* urgh */
|
||||
|
||||
sftp_main(argc, argv);
|
||||
|
||||
if ((pu = puffs_mount(&pops, mountpath, 0, "ssshfs",
|
||||
PUFFS_KFLAG_NOCACHE, 0))==NULL)
|
||||
err(1, "mount");
|
||||
|
||||
if (puffs_mainloop(pu, 0) == -1)
|
||||
err(1, "mainloop");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
buildpath(struct ssshnode *ssn, const struct ssshnode *ossn, const char *pcomp)
|
||||
{
|
||||
size_t clen = strlen(pcomp);
|
||||
|
||||
assert((ossn->namelen + clen + 1 + 1) <= MAXPATHLEN);
|
||||
|
||||
memcpy(ssn->name, ossn->name, ossn->namelen);
|
||||
ssn->name[ossn->namelen] = '/';
|
||||
strcat(ssn->name, pcomp);
|
||||
ssn->namelen = ossn->namelen + clen + 1; /* not nil, but '/' */
|
||||
ssn->name[ssn->namelen] = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
buildvattr(struct ssshnode *ssn, const Attrib *a)
|
||||
{
|
||||
struct vattr *va = &ssn->va;
|
||||
|
||||
if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
|
||||
va->va_size = a->size;
|
||||
va->va_bytes = a->size;
|
||||
}
|
||||
if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
|
||||
if (a->uid == uidmangle_to && mangle)
|
||||
va->va_uid = uidmangle_from;
|
||||
else
|
||||
va->va_uid = a->uid;
|
||||
|
||||
if (a->gid == gidmangle_to && mangle)
|
||||
va->va_gid = gidmangle_from;
|
||||
else
|
||||
va->va_gid = a->gid;
|
||||
}
|
||||
if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
|
||||
va->va_mode = a->perm;
|
||||
if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
|
||||
va->va_atime.tv_sec = a->atime;
|
||||
va->va_mtime.tv_sec = a->mtime;
|
||||
}
|
||||
|
||||
va->va_type = puffs_mode2vt(va->va_mode);
|
||||
}
|
||||
|
||||
static Attrib *
|
||||
vattrtoAttrib(const struct vattr *va)
|
||||
{
|
||||
static Attrib a; /* XXX, but sftp isn't threadsafe either */
|
||||
|
||||
memset(&a, 0, sizeof(a));
|
||||
|
||||
if (va->va_size != PUFFS_VNOVAL) {
|
||||
a.size = va->va_size;
|
||||
a.flags |= SSH2_FILEXFER_ATTR_SIZE;
|
||||
}
|
||||
|
||||
if (va->va_uid != PUFFS_VNOVAL) {
|
||||
if (va->va_uid == uidmangle_from && mangle)
|
||||
a.uid = uidmangle_to;
|
||||
else
|
||||
a.uid = va->va_uid;
|
||||
|
||||
if (va->va_gid == gidmangle_from && mangle)
|
||||
a.gid = gidmangle_to;
|
||||
else
|
||||
a.gid = va->va_gid;
|
||||
|
||||
a.flags |= SSH2_FILEXFER_ATTR_UIDGID;
|
||||
}
|
||||
|
||||
if (va->va_mode != PUFFS_VNOVAL) {
|
||||
a.perm = va->va_mode & 0777;
|
||||
a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
|
||||
}
|
||||
|
||||
if (va->va_atime.tv_sec != PUFFS_VNOVAL) {
|
||||
a.atime = va->va_atime.tv_sec;
|
||||
a.mtime = va->va_mtime.tv_sec;
|
||||
a.flags |= SSH2_FILEXFER_ATTR_ACMODTIME;
|
||||
}
|
||||
|
||||
return &a;
|
||||
}
|
||||
|
||||
static int
|
||||
dircache(struct ssshnode *ssn)
|
||||
{
|
||||
|
||||
assert(ssn->va.va_type == VDIR);
|
||||
|
||||
if ((ssn->dcache & DCACHE_EXISTS)
|
||||
&& ((ssn->dcache & DCACHE_CHANGED) == 0))
|
||||
return 0;
|
||||
|
||||
if (ssn->dcache & DCACHE_EXISTS)
|
||||
free_sftp_dirents(ssn->ents);
|
||||
ssn->dcache &= ~DCACHE_EXISTS;
|
||||
|
||||
if (do_readdir(sftpc, ssn->name, &ssn->ents) != 0)
|
||||
return 1;
|
||||
|
||||
ssn->dcache |= DCACHE_EXISTS;
|
||||
ssn->dcache &= ~DCACHE_CHANGED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ssshnode *
|
||||
makenewnode(struct ssshnode *ossn, const char *pcomp, const char *longname)
|
||||
{
|
||||
struct ssshnode *newssn;
|
||||
int links;
|
||||
|
||||
newssn = emalloc(sizeof(struct ssshnode));
|
||||
memset(newssn, 0, sizeof(struct ssshnode));
|
||||
|
||||
newssn->dotdot = ossn;
|
||||
newssn->myid = nextid++;
|
||||
|
||||
buildpath(newssn, ossn, pcomp);
|
||||
|
||||
ossn->childcount++;
|
||||
newssn->childcount = 0;
|
||||
newssn->va.va_fileid = newssn->myid;
|
||||
newssn->va.va_blocksize = 512;
|
||||
newssn->va.va_size = 0;
|
||||
newssn->va.va_bytes = 0;
|
||||
|
||||
/* XXX: only way I know how (didn't look into the protocol, though) */
|
||||
if (longname && (sscanf(longname, "%*s%d", &links) == 1))
|
||||
newssn->va.va_nlink = links;
|
||||
else
|
||||
newssn->va.va_nlink = 1;
|
||||
|
||||
return newssn;
|
||||
}
|
||||
|
||||
int
|
||||
ssshfs_fs_mount(struct puffs_usermount *pu, void **rootcookie)
|
||||
{
|
||||
|
||||
sftpc = do_init(in, out, 1<<15, 1);
|
||||
if (sftpc == NULL) {
|
||||
printf("can't init sftpc\n");
|
||||
return EBUSY;
|
||||
}
|
||||
|
||||
rn.childcount = 1;
|
||||
rn.myid = 2;
|
||||
|
||||
memset(rn.name, 0, sizeof(rn.name));
|
||||
|
||||
if (argpath)
|
||||
strcpy(rn.name, argpath);
|
||||
else {
|
||||
char *dotpath;
|
||||
dotpath = do_realpath(sftpc, ".");
|
||||
if (!dotpath)
|
||||
return ENOENT;
|
||||
strcpy(rn.name, dotpath);
|
||||
}
|
||||
rn.namelen = strlen(rn.name);
|
||||
|
||||
rn.va.va_type = VDIR;
|
||||
rn.va.va_mode = 0777;
|
||||
rn.va.va_nlink = 1024; /* XXXX */
|
||||
|
||||
*rootcookie = &rn;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ssshfs_fs_unmount(struct puffs_usermount *pu, int flags, pid_t pid)
|
||||
{
|
||||
|
||||
close(in);
|
||||
close(out);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ssshfs_node_lookup(struct puffs_usermount *pu, void *opc, void **newnode,
|
||||
enum vtype *newtype, voff_t *newsize, dev_t *newrdev,
|
||||
const struct puffs_cn *pcn)
|
||||
{
|
||||
struct ssshnode *ssn = opc;
|
||||
struct ssshnode *newssn;
|
||||
struct SFTP_DIRENT *de;
|
||||
int i;
|
||||
|
||||
if (pcn->pcn_flags & PUFFS_ISDOTDOT) {
|
||||
*newnode = ssn->dotdot;
|
||||
*newtype = VDIR;
|
||||
return 0;
|
||||
}
|
||||
|
||||
dircache(ssn);
|
||||
|
||||
for (i = 0, de = ssn->ents[0]; de; de = ssn->ents[i++])
|
||||
if (strcmp(de->filename, pcn->pcn_name) == 0)
|
||||
break;
|
||||
|
||||
if (!de)
|
||||
return ENOENT;
|
||||
|
||||
newssn = makenewnode(ssn, de->filename, de->longname);
|
||||
buildvattr(newssn, &de->a);
|
||||
|
||||
*newnode = newssn;
|
||||
*newtype = newssn->va.va_type;
|
||||
*newsize = newssn->va.va_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ssshfs_node_getattr(struct puffs_usermount *pu, void *opc, struct vattr *va,
|
||||
const struct puffs_cred *pcr, pid_t pid)
|
||||
{
|
||||
struct ssshnode *ssn = opc;
|
||||
|
||||
memcpy(va, &ssn->va, sizeof(struct vattr));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ssshfs_node_setattr(struct puffs_usermount *pu, void *opc,
|
||||
const struct vattr *va, const struct puffs_cred *pcr, pid_t pid)
|
||||
{
|
||||
struct ssshnode *ssn = opc;
|
||||
Attrib *a;
|
||||
int rv;
|
||||
|
||||
puffs_setvattr(&ssn->va, va);
|
||||
a = vattrtoAttrib(va);
|
||||
|
||||
/* XXX: compensate for lack of granulatity of SSH2_FILEXFER */
|
||||
if ((a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)
|
||||
&& (va->va_mtime.tv_sec == PUFFS_VNOVAL))
|
||||
a->mtime = ssn->va.va_mtime.tv_sec;
|
||||
|
||||
rv = do_setstat(sftpc, ssn->name, a);
|
||||
if (rv)
|
||||
return EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int do_creatfile(struct sftp_conn *conn, char *path, Attrib *a); /* XXX */
|
||||
int
|
||||
ssshfs_node_create(struct puffs_usermount *pu, void *opc, void **newnode,
|
||||
const struct puffs_cn *pcn, const struct vattr *va)
|
||||
{
|
||||
struct ssshnode *ssd = opc, *newssn;
|
||||
Attrib *a;
|
||||
int rv;
|
||||
|
||||
newssn = makenewnode(ssd, pcn->pcn_name, NULL);
|
||||
puffs_setvattr(&newssn->va, va);
|
||||
a = vattrtoAttrib(va);
|
||||
if ((rv = do_creatfile(sftpc, newssn->name, a)) != 0) {
|
||||
/* XXX: free newssn */
|
||||
return EIO;
|
||||
}
|
||||
|
||||
ssd->dcache |= DCACHE_CHANGED;
|
||||
ssd->va.va_nlink++;
|
||||
|
||||
*newnode = newssn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ssshfs_node_readdir(struct puffs_usermount *pu, void *opc, struct dirent *dent,
|
||||
const struct puffs_cred *pcr, off_t *readoff, size_t *reslen)
|
||||
{
|
||||
struct ssshnode *ssn = opc;
|
||||
struct SFTP_DIRENT *de;
|
||||
|
||||
dircache(ssn);
|
||||
|
||||
for (de = ssn->ents[*readoff]; de; de = ssn->ents[++(*readoff)]) {
|
||||
if (!puffs_nextdent(&dent, de->filename, nextid++,
|
||||
puffs_vtype2dt(ssn->va.va_type), reslen))
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ssshfs_node_read(struct puffs_usermount *pu, void *opc, uint8_t *buf,
|
||||
off_t offset, size_t *resid, const struct puffs_cred *pcr,
|
||||
int ioflag)
|
||||
{
|
||||
size_t x1, x2;
|
||||
struct ssshnode *ssn = opc;
|
||||
int rv;
|
||||
|
||||
if (offset > ssn->va.va_size)
|
||||
return 0;
|
||||
|
||||
x1 = *resid;
|
||||
if (offset + *resid > ssn->va.va_size)
|
||||
x1 = ssn->va.va_size - offset;
|
||||
|
||||
if (x1 == 0)
|
||||
return 0;
|
||||
|
||||
x2 = x1;
|
||||
rv = do_readfile(sftpc, ssn->name, buf, offset, &x1);
|
||||
if (rv)
|
||||
return EIO;
|
||||
|
||||
*resid -= (x2 - x1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ssshfs_node_write(struct puffs_usermount *pu, void *opc, uint8_t *buf,
|
||||
off_t offset, size_t *resid, const struct puffs_cred *cred,
|
||||
int ioflag)
|
||||
{
|
||||
struct ssshnode *ssn = opc;
|
||||
size_t origres;
|
||||
int rv;
|
||||
|
||||
origres = *resid;
|
||||
rv = do_writefile(sftpc, ssn->name, buf, offset, resid,
|
||||
ioflag & PUFFS_IO_APPEND);
|
||||
|
||||
if (ioflag & PUFFS_IO_APPEND) {
|
||||
ssn->va.va_size += origres - *resid;
|
||||
} else {
|
||||
if (offset + (origres - *resid) > ssn->va.va_size)
|
||||
ssn->va.va_size = offset + (origres - *resid);
|
||||
}
|
||||
|
||||
if (rv)
|
||||
return EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ssshfs_node_readlink(struct puffs_usermount *pu, void *opc,
|
||||
const struct puffs_cred *cred, char *linkvalue, size_t *linklen)
|
||||
{
|
||||
struct ssshnode *ssn = opc;
|
||||
char *res;
|
||||
|
||||
if (sftp_proto_version(sftpc) < 3)
|
||||
return EOPNOTSUPP;
|
||||
|
||||
res = do_readlink(sftpc, ssn->name);
|
||||
if (!res)
|
||||
return EIO;
|
||||
*linklen = strlen(res);
|
||||
memcpy(linkvalue, res, *linklen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ssshfs_node_remove(struct puffs_usermount *pu, void *opc, void *targ,
|
||||
const struct puffs_cn *pcn)
|
||||
{
|
||||
struct ssshnode *ssn = targ, *ssd = opc;
|
||||
int rv;
|
||||
|
||||
if (ssn->va.va_type == VDIR)
|
||||
return EISDIR;
|
||||
|
||||
if ((rv = do_rm(sftpc, ssn->name)) != 0)
|
||||
return EIO;
|
||||
|
||||
ssd->dcache |= DCACHE_CHANGED;
|
||||
ssd->va.va_nlink--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ssshfs_node_mkdir(struct puffs_usermount *pu, void *opc, void **newnode,
|
||||
const struct puffs_cn *pcn, const struct vattr *va)
|
||||
{
|
||||
struct ssshnode *ssd = opc, *newssn;
|
||||
Attrib *a;
|
||||
int rv;
|
||||
|
||||
newssn = makenewnode(ssd, pcn->pcn_name, NULL);
|
||||
newssn->va.va_nlink++;
|
||||
puffs_setvattr(&newssn->va, va);
|
||||
a = vattrtoAttrib(va);
|
||||
if ((rv = do_mkdir(sftpc, newssn->name, a)) != 0) {
|
||||
/* XXX: free newssn */
|
||||
return EIO;
|
||||
}
|
||||
|
||||
ssd->dcache |= DCACHE_CHANGED;
|
||||
ssd->va.va_nlink++;
|
||||
|
||||
*newnode = newssn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ssshfs_node_rmdir(struct puffs_usermount *pu, void *opc, void *targ,
|
||||
const struct puffs_cn *pcn)
|
||||
{
|
||||
struct ssshnode *ssn = targ, *ssd = opc;
|
||||
int rv;
|
||||
|
||||
if (ssn->va.va_type != VDIR)
|
||||
return ENOTDIR;
|
||||
|
||||
if ((rv = do_rmdir(sftpc, ssn->name)) != 0)
|
||||
return EIO;
|
||||
|
||||
ssd->dcache |= DCACHE_CHANGED;
|
||||
ssd->va.va_nlink--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ssshfs_node_symlink(struct puffs_usermount *pu, void *opc, void **newnode,
|
||||
const struct puffs_cn *pcn, const struct vattr *va,
|
||||
const char *link_target)
|
||||
{
|
||||
struct ssshnode *ssd = opc, *newssn;
|
||||
char buf[MAXPATHLEN+1];
|
||||
Attrib *a;
|
||||
int rv;
|
||||
|
||||
if (sftp_proto_version(sftpc) < 3)
|
||||
return EOPNOTSUPP;
|
||||
|
||||
if (*link_target == '/') {
|
||||
strcpy(buf, link_target);
|
||||
} else {
|
||||
strcpy(buf, ssd->name);
|
||||
strcat(buf, "/");
|
||||
strcat(buf, link_target);
|
||||
}
|
||||
|
||||
newssn = makenewnode(ssd, pcn->pcn_name, NULL);
|
||||
puffs_setvattr(&newssn->va, va);
|
||||
a = vattrtoAttrib(va);
|
||||
if ((rv = do_symlink(sftpc, newssn->name, buf)) != 0)
|
||||
return EIO;
|
||||
|
||||
ssd->dcache |= DCACHE_CHANGED;
|
||||
ssd->va.va_nlink++;
|
||||
|
||||
*newnode = newssn;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ssshfs_node_rename(struct puffs_usermount *pu, void *opc, void *src,
|
||||
const struct puffs_cn *pcn_src, void *targ_dir, void *targ,
|
||||
const struct puffs_cn *pcn_targ)
|
||||
{
|
||||
struct ssshnode *ssd_src = opc, *ssd_dest = targ_dir;
|
||||
struct ssshnode *ssn_file = src;
|
||||
char newname[MAXPATHLEN+1];
|
||||
|
||||
strcpy(newname, ssd_dest->name);
|
||||
strcat(newname, "/");
|
||||
strcat(newname, pcn_targ->pcn_name);
|
||||
|
||||
if (targ)
|
||||
if (do_rm(sftpc, newname))
|
||||
return EIO;
|
||||
|
||||
if (do_rename(sftpc, ssn_file->name, newname))
|
||||
return EIO;
|
||||
|
||||
/* ok, commit */
|
||||
ssn_file->namelen = strlen(newname);
|
||||
ssd_src->childcount--;
|
||||
ssd_src->va.va_nlink--;
|
||||
ssd_dest->childcount++;
|
||||
ssd_dest->va.va_nlink++;
|
||||
|
||||
ssd_src->dcache |= DCACHE_CHANGED;
|
||||
ssd_dest->dcache |= DCACHE_CHANGED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
ssshfs_node_reclaim(struct puffs_usermount *pu, void *opc, pid_t pid)
|
||||
{
|
||||
struct ssshnode *ssn, *ssn_next;
|
||||
|
||||
for (ssn = opc; ssn != &rn; ssn = ssn_next) {
|
||||
if (ssn->childcount == 0) {
|
||||
if (ssn->dcache & DCACHE_EXISTS)
|
||||
free_sftp_dirents(ssn->ents);
|
||||
ssn_next = ssn->dotdot;
|
||||
ssn_next->childcount--;
|
||||
free(ssn);
|
||||
} else
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user