2008-09-29 14:11:29 +00:00
|
|
|
/* Copyright (c) 2004-2008, Sara Golemon <sarag@libssh2.org>
|
2010-05-05 15:44:15 +07:00
|
|
|
* Copyright (c) 2007 Eli Fant <elifantu@mail.ru>
|
2012-03-12 22:49:25 +01:00
|
|
|
* Copyright (c) 2009-2012 by Daniel Stenberg
|
2004-12-22 00:20:02 +00:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms,
|
|
|
|
* with or without modification, are permitted provided
|
|
|
|
* that the following conditions are met:
|
|
|
|
*
|
|
|
|
* Redistributions of source code must retain the above
|
|
|
|
* copyright notice, this list of conditions and the
|
|
|
|
* following disclaimer.
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
* Neither the name of the copyright holder nor the names
|
|
|
|
* of any other contributors may be used to endorse or
|
|
|
|
* promote products derived from this software without
|
|
|
|
* specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
|
|
|
* CONTRIBUTORS "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 COPYRIGHT OWNER 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.
|
|
|
|
*/
|
|
|
|
|
2009-03-27 22:24:09 +00:00
|
|
|
#include <assert.h>
|
|
|
|
|
2004-12-22 00:20:02 +00:00
|
|
|
#include "libssh2_priv.h"
|
|
|
|
#include "libssh2_sftp.h"
|
2009-03-27 20:20:48 +00:00
|
|
|
#include "channel.h"
|
2010-04-24 20:21:22 +02:00
|
|
|
#include "session.h"
|
2010-10-25 01:43:54 +02:00
|
|
|
#include "sftp.h"
|
2004-12-22 00:20:02 +00:00
|
|
|
|
|
|
|
/* Note: Version 6 was documented at the time of writing
|
|
|
|
* However it was marked as "DO NOT IMPLEMENT" due to pending changes
|
|
|
|
*
|
|
|
|
* This release of libssh2 implements Version 5 with automatic downgrade
|
|
|
|
* based on server's declaration
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* SFTP packet types */
|
2007-02-02 16:21:20 +00:00
|
|
|
#define SSH_FXP_INIT 1
|
|
|
|
#define SSH_FXP_VERSION 2
|
|
|
|
#define SSH_FXP_OPEN 3
|
|
|
|
#define SSH_FXP_CLOSE 4
|
|
|
|
#define SSH_FXP_READ 5
|
|
|
|
#define SSH_FXP_WRITE 6
|
|
|
|
#define SSH_FXP_LSTAT 7
|
|
|
|
#define SSH_FXP_FSTAT 8
|
|
|
|
#define SSH_FXP_SETSTAT 9
|
|
|
|
#define SSH_FXP_FSETSTAT 10
|
|
|
|
#define SSH_FXP_OPENDIR 11
|
|
|
|
#define SSH_FXP_READDIR 12
|
|
|
|
#define SSH_FXP_REMOVE 13
|
|
|
|
#define SSH_FXP_MKDIR 14
|
|
|
|
#define SSH_FXP_RMDIR 15
|
|
|
|
#define SSH_FXP_REALPATH 16
|
|
|
|
#define SSH_FXP_STAT 17
|
|
|
|
#define SSH_FXP_RENAME 18
|
|
|
|
#define SSH_FXP_READLINK 19
|
|
|
|
#define SSH_FXP_SYMLINK 20
|
|
|
|
#define SSH_FXP_STATUS 101
|
|
|
|
#define SSH_FXP_HANDLE 102
|
|
|
|
#define SSH_FXP_DATA 103
|
|
|
|
#define SSH_FXP_NAME 104
|
|
|
|
#define SSH_FXP_ATTRS 105
|
|
|
|
#define SSH_FXP_EXTENDED 200
|
2007-04-21 18:16:23 +00:00
|
|
|
#define SSH_FXP_EXTENDED_REPLY 201
|
|
|
|
|
2005-05-04 18:06:07 +00:00
|
|
|
/* S_IFREG */
|
2007-02-02 16:21:20 +00:00
|
|
|
#define LIBSSH2_SFTP_ATTR_PFILETYPE_FILE 0100000
|
2005-05-04 18:06:07 +00:00
|
|
|
/* S_IFDIR */
|
2007-02-02 16:21:20 +00:00
|
|
|
#define LIBSSH2_SFTP_ATTR_PFILETYPE_DIR 0040000
|
2005-05-04 18:06:07 +00:00
|
|
|
|
2010-05-22 01:48:04 -07:00
|
|
|
#define SSH_FXE_STATVFS_ST_RDONLY 0x00000001
|
|
|
|
#define SSH_FXE_STATVFS_ST_NOSUID 0x00000002
|
|
|
|
|
2011-11-18 22:53:21 +01:00
|
|
|
/* This is the maximum packet length to accept, as larger than this indicate
|
|
|
|
some kind of server problem. */
|
|
|
|
#define LIBSSH2_SFTP_PACKET_MAXLEN 80000
|
|
|
|
|
2009-03-27 20:20:48 +00:00
|
|
|
static int sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle);
|
2010-12-14 11:46:53 +01:00
|
|
|
static int sftp_packet_ask(LIBSSH2_SFTP *sftp, unsigned char packet_type,
|
2010-12-15 09:27:00 +01:00
|
|
|
uint32_t request_id, unsigned char **data,
|
2010-12-14 11:46:53 +01:00
|
|
|
size_t *data_len);
|
|
|
|
static void sftp_packet_flush(LIBSSH2_SFTP *sftp);
|
2009-03-27 20:20:48 +00:00
|
|
|
|
2010-04-28 00:23:09 +02:00
|
|
|
/* sftp_attrsize
|
|
|
|
* Size that attr with this flagset will occupy when turned into a bin struct
|
|
|
|
*/
|
2010-04-28 17:38:00 +01:00
|
|
|
static int sftp_attrsize(unsigned long flags)
|
|
|
|
{
|
|
|
|
return (4 + /* flags(4) */
|
2010-04-28 17:41:17 +01:00
|
|
|
((flags & LIBSSH2_SFTP_ATTR_SIZE) ? 8 : 0) +
|
|
|
|
((flags & LIBSSH2_SFTP_ATTR_UIDGID) ? 8 : 0) +
|
|
|
|
((flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) ? 4 : 0) +
|
|
|
|
((flags & LIBSSH2_SFTP_ATTR_ACMODTIME) ? 8 : 0));
|
2010-04-28 17:38:00 +01:00
|
|
|
/* atime + mtime as u32 */
|
|
|
|
}
|
2010-04-28 00:23:09 +02:00
|
|
|
|
2010-04-17 13:18:15 +02:00
|
|
|
/* _libssh2_store_u64
|
2009-03-27 07:03:00 +00:00
|
|
|
*/
|
2010-04-17 13:18:15 +02:00
|
|
|
static void _libssh2_store_u64(unsigned char **ptr, libssh2_uint64_t value)
|
2009-03-27 07:03:00 +00:00
|
|
|
{
|
2010-04-17 13:34:44 +02:00
|
|
|
uint32_t msl = (uint32_t)(value >> 32);
|
2010-04-17 13:18:15 +02:00
|
|
|
unsigned char *buf = *ptr;
|
2009-03-27 07:03:00 +00:00
|
|
|
|
2009-07-07 13:44:17 +02:00
|
|
|
buf[0] = (unsigned char)((msl >> 24) & 0xFF);
|
|
|
|
buf[1] = (unsigned char)((msl >> 16) & 0xFF);
|
|
|
|
buf[2] = (unsigned char)((msl >> 8) & 0xFF);
|
|
|
|
buf[3] = (unsigned char)( msl & 0xFF);
|
2009-03-27 07:03:00 +00:00
|
|
|
|
2009-07-07 13:44:17 +02:00
|
|
|
buf[4] = (unsigned char)((value >> 24) & 0xFF);
|
|
|
|
buf[5] = (unsigned char)((value >> 16) & 0xFF);
|
|
|
|
buf[6] = (unsigned char)((value >> 8) & 0xFF);
|
|
|
|
buf[7] = (unsigned char)( value & 0xFF);
|
2010-04-17 13:18:15 +02:00
|
|
|
|
|
|
|
*ptr += 8;
|
2009-03-27 07:03:00 +00:00
|
|
|
}
|
|
|
|
|
2009-03-13 22:15:27 +00:00
|
|
|
/*
|
|
|
|
* sftp_packet_add
|
|
|
|
*
|
2004-12-22 00:20:02 +00:00
|
|
|
* Add a packet to the SFTP packet brigade
|
|
|
|
*/
|
2007-08-06 20:48:04 +00:00
|
|
|
static int
|
2009-03-26 15:41:14 +00:00
|
|
|
sftp_packet_add(LIBSSH2_SFTP *sftp, unsigned char *data,
|
2010-04-15 20:36:31 +02:00
|
|
|
size_t data_len)
|
2004-12-22 00:20:02 +00:00
|
|
|
{
|
2007-05-28 17:56:08 +00:00
|
|
|
LIBSSH2_SESSION *session = sftp->channel->session;
|
2010-12-14 23:49:51 +01:00
|
|
|
LIBSSH2_SFTP_PACKET *packet;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-12-08 08:52:03 +01:00
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Received packet %d (len %d)",
|
2008-12-16 15:35:33 +00:00
|
|
|
(int) data[0], data_len);
|
2012-03-13 21:59:59 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Experience shows that if we mess up EAGAIN handling somewhere or
|
|
|
|
* otherwise get out of sync with the channel, this is where we first get
|
|
|
|
* a wrong byte and if so we need to bail out at once to aid tracking the
|
|
|
|
* problem better.
|
|
|
|
*/
|
|
|
|
|
|
|
|
switch(data[0]) {
|
|
|
|
case SSH_FXP_INIT:
|
|
|
|
case SSH_FXP_VERSION:
|
|
|
|
case SSH_FXP_OPEN:
|
|
|
|
case SSH_FXP_CLOSE:
|
|
|
|
case SSH_FXP_READ:
|
|
|
|
case SSH_FXP_WRITE:
|
|
|
|
case SSH_FXP_LSTAT:
|
|
|
|
case SSH_FXP_FSTAT:
|
|
|
|
case SSH_FXP_SETSTAT:
|
|
|
|
case SSH_FXP_FSETSTAT:
|
|
|
|
case SSH_FXP_OPENDIR:
|
|
|
|
case SSH_FXP_READDIR:
|
|
|
|
case SSH_FXP_REMOVE:
|
|
|
|
case SSH_FXP_MKDIR:
|
|
|
|
case SSH_FXP_RMDIR:
|
|
|
|
case SSH_FXP_REALPATH:
|
|
|
|
case SSH_FXP_STAT:
|
|
|
|
case SSH_FXP_RENAME:
|
|
|
|
case SSH_FXP_READLINK:
|
|
|
|
case SSH_FXP_SYMLINK:
|
|
|
|
case SSH_FXP_STATUS:
|
|
|
|
case SSH_FXP_HANDLE:
|
|
|
|
case SSH_FXP_DATA:
|
|
|
|
case SSH_FXP_NAME:
|
|
|
|
case SSH_FXP_ATTRS:
|
|
|
|
case SSH_FXP_EXTENDED:
|
|
|
|
case SSH_FXP_EXTENDED_REPLY:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
|
|
|
|
"Out of sync with the world");
|
|
|
|
}
|
|
|
|
|
2010-12-14 23:49:51 +01:00
|
|
|
packet = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_SFTP_PACKET));
|
2007-05-28 17:56:08 +00:00
|
|
|
if (!packet) {
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
|
|
|
"Unable to allocate datablock for SFTP packet");
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
packet->data = data;
|
|
|
|
packet->data_len = data_len;
|
2010-12-14 23:49:51 +01:00
|
|
|
packet->request_id = _libssh2_ntohu32(&data[1]);
|
2009-08-20 00:56:05 +02:00
|
|
|
|
|
|
|
_libssh2_list_add(&sftp->packets, &packet->node);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
return 0;
|
2004-12-22 00:20:02 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-13 22:15:27 +00:00
|
|
|
/*
|
|
|
|
* sftp_packet_read
|
|
|
|
*
|
2004-12-22 00:20:02 +00:00
|
|
|
* Frame an SFTP packet off the channel
|
|
|
|
*/
|
2007-08-06 20:48:04 +00:00
|
|
|
static int
|
2009-03-26 15:41:14 +00:00
|
|
|
sftp_packet_read(LIBSSH2_SFTP *sftp)
|
2004-12-22 00:20:02 +00:00
|
|
|
{
|
2007-04-21 18:16:23 +00:00
|
|
|
LIBSSH2_CHANNEL *channel = sftp->channel;
|
|
|
|
LIBSSH2_SESSION *session = channel->session;
|
|
|
|
unsigned char *packet;
|
2011-01-09 00:34:09 +01:00
|
|
|
size_t packet_received;
|
2010-12-29 23:36:45 +01:00
|
|
|
ssize_t rc;
|
2011-01-09 00:34:09 +01:00
|
|
|
uint32_t packet_len; /* 32bits on the wire */
|
2012-03-12 22:49:25 +01:00
|
|
|
uint32_t adjust;
|
|
|
|
unsigned long recv_window;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-12-08 08:52:03 +01:00
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP, "recv packet");
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2012-03-12 22:49:25 +01:00
|
|
|
switch(sftp->packet_state) {
|
|
|
|
case libssh2_NB_state_sent:
|
|
|
|
sftp->packet_state = libssh2_NB_state_idle;
|
|
|
|
goto window_adjust;
|
2008-12-16 15:35:33 +00:00
|
|
|
|
2012-03-12 22:49:25 +01:00
|
|
|
default:
|
2008-12-16 15:35:33 +00:00
|
|
|
|
2012-03-12 22:49:25 +01:00
|
|
|
/* If there was a previous partial, start using it */
|
|
|
|
if (sftp->partial_packet) {
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2012-03-12 22:49:25 +01:00
|
|
|
packet = sftp->partial_packet;
|
|
|
|
packet_len = sftp->partial_len;
|
|
|
|
packet_received = sftp->partial_received;
|
|
|
|
sftp->partial_packet = NULL;
|
2010-11-02 19:14:33 +01:00
|
|
|
|
2012-03-12 22:49:25 +01:00
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP,
|
|
|
|
"partial read cont, len: %lu", packet_len);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* each packet starts with a 32 bit length field */
|
|
|
|
rc = _libssh2_channel_read(channel, 0,
|
|
|
|
(char *)&sftp->partial_size[
|
|
|
|
sftp->partial_size_len],
|
|
|
|
4 - sftp->partial_size_len);
|
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN)
|
|
|
|
return rc;
|
|
|
|
else if (rc < 0)
|
|
|
|
return _libssh2_error(session, rc, "channel read");
|
|
|
|
|
|
|
|
sftp->partial_size_len += rc;
|
|
|
|
|
|
|
|
if(4 != sftp->partial_size_len)
|
|
|
|
/* we got a short read for the length part */
|
|
|
|
return LIBSSH2_ERROR_EAGAIN;
|
|
|
|
|
|
|
|
packet_len = _libssh2_ntohu32(sftp->partial_size);
|
|
|
|
/* make sure we don't proceed if the packet size is unreasonably
|
|
|
|
large */
|
|
|
|
if (packet_len > LIBSSH2_SFTP_PACKET_MAXLEN)
|
|
|
|
return _libssh2_error(session,
|
|
|
|
LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED,
|
|
|
|
"SFTP packet too large");
|
|
|
|
|
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP,
|
|
|
|
"Data begin - Packet Length: %lu", packet_len);
|
|
|
|
packet = LIBSSH2_ALLOC(session, packet_len);
|
|
|
|
if (!packet)
|
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
|
|
|
"Unable to allocate SFTP packet");
|
|
|
|
sftp->partial_size_len = 0;
|
|
|
|
packet_received = 0;
|
2010-11-02 19:14:33 +01:00
|
|
|
|
2012-03-12 22:49:25 +01:00
|
|
|
sftp->partial_len = packet_len;
|
|
|
|
sftp->partial_received = packet_received;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2012-03-12 22:49:25 +01:00
|
|
|
window_adjust:
|
|
|
|
recv_window = libssh2_channel_window_read_ex(channel, NULL, NULL);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2012-03-12 22:49:25 +01:00
|
|
|
/* how much data to ask for */
|
|
|
|
adjust = sftp->partial_len - sftp->partial_received;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2012-03-12 22:49:25 +01:00
|
|
|
if(adjust > recv_window) {
|
|
|
|
/* ask for twice the data amount we need at once */
|
|
|
|
rc = _libssh2_channel_receive_window_adjust(channel, adjust*2,
|
|
|
|
0, NULL);
|
|
|
|
/* store the state so that we continue with the correct
|
|
|
|
operation at next invoke */
|
|
|
|
sftp->packet_state = (rc == LIBSSH2_ERROR_EAGAIN)?
|
|
|
|
libssh2_NB_state_sent:
|
|
|
|
libssh2_NB_state_idle;
|
|
|
|
|
|
|
|
if(rc == LIBSSH2_ERROR_EAGAIN)
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read as much of the packet as we can */
|
|
|
|
while (packet_len > packet_received) {
|
|
|
|
rc = _libssh2_channel_read(channel, 0,
|
|
|
|
(char *) packet + packet_received,
|
|
|
|
packet_len - packet_received);
|
|
|
|
|
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN) {
|
|
|
|
/*
|
|
|
|
* We received EAGAIN, save what we have and return EAGAIN to
|
|
|
|
* the caller. Set 'partial_packet' so that this function
|
|
|
|
* knows how to continue on the next invoke.
|
|
|
|
*/
|
|
|
|
sftp->partial_packet = packet;
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
else if (rc < 0) {
|
|
|
|
LIBSSH2_FREE(session, packet);
|
|
|
|
return _libssh2_error(session, rc,
|
|
|
|
"Error waiting for SFTP packet");
|
|
|
|
}
|
|
|
|
packet_received += rc;
|
2008-12-16 12:31:58 +00:00
|
|
|
}
|
2012-03-12 22:49:25 +01:00
|
|
|
|
|
|
|
sftp->partial_packet = NULL;
|
|
|
|
|
|
|
|
rc = sftp_packet_add(sftp, packet, packet_len);
|
|
|
|
if (rc) {
|
2007-04-21 18:16:23 +00:00
|
|
|
LIBSSH2_FREE(session, packet);
|
2012-03-12 22:49:25 +01:00
|
|
|
return rc;
|
2007-04-21 18:16:23 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2012-03-12 22:49:25 +01:00
|
|
|
return packet[0];
|
2010-11-02 19:14:33 +01:00
|
|
|
|
2007-04-21 18:16:23 +00:00
|
|
|
}
|
2004-12-22 00:20:02 +00:00
|
|
|
}
|
2010-12-14 11:46:53 +01:00
|
|
|
/*
|
|
|
|
* sftp_packetlist_flush
|
|
|
|
*
|
|
|
|
* Remove all pending packets in the packet_list and the corresponding one(s)
|
|
|
|
* in the SFTP packet brigade.
|
|
|
|
*/
|
|
|
|
static void sftp_packetlist_flush(LIBSSH2_SFTP_HANDLE *handle)
|
|
|
|
{
|
2010-12-14 13:29:41 +01:00
|
|
|
struct sftp_pipeline_chunk *chunk;
|
2010-12-14 11:46:53 +01:00
|
|
|
LIBSSH2_SFTP *sftp = handle->sftp;
|
|
|
|
LIBSSH2_SESSION *session = sftp->channel->session;
|
|
|
|
|
|
|
|
/* remove pending packets, if any */
|
|
|
|
chunk = _libssh2_list_first(&handle->packet_list);
|
|
|
|
while(chunk) {
|
|
|
|
unsigned char *data;
|
|
|
|
size_t data_len;
|
|
|
|
int rc;
|
2010-12-14 13:29:41 +01:00
|
|
|
struct sftp_pipeline_chunk *next = _libssh2_list_next(&chunk->node);
|
2010-12-14 11:46:53 +01:00
|
|
|
|
|
|
|
rc = sftp_packet_ask(sftp, SSH_FXP_STATUS,
|
|
|
|
chunk->request_id, &data, &data_len);
|
|
|
|
if(rc)
|
|
|
|
rc = sftp_packet_ask(sftp, SSH_FXP_DATA,
|
|
|
|
chunk->request_id, &data, &data_len);
|
|
|
|
|
|
|
|
if(!rc)
|
|
|
|
/* we found a packet, free it */
|
|
|
|
LIBSSH2_FREE(session, data);
|
|
|
|
|
|
|
|
_libssh2_list_remove(&chunk->node);
|
|
|
|
LIBSSH2_FREE(session, chunk);
|
|
|
|
chunk = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-13 22:15:27 +00:00
|
|
|
/*
|
|
|
|
* sftp_packet_ask()
|
|
|
|
*
|
|
|
|
* Checks if there's a matching SFTP packet available.
|
2004-12-22 00:20:02 +00:00
|
|
|
*/
|
2007-08-06 20:48:04 +00:00
|
|
|
static int
|
2009-03-26 15:41:14 +00:00
|
|
|
sftp_packet_ask(LIBSSH2_SFTP *sftp, unsigned char packet_type,
|
2010-12-15 09:27:00 +01:00
|
|
|
uint32_t request_id, unsigned char **data,
|
2010-04-15 20:36:31 +02:00
|
|
|
size_t *data_len)
|
2004-12-22 00:20:02 +00:00
|
|
|
{
|
2007-04-21 18:16:23 +00:00
|
|
|
LIBSSH2_SESSION *session = sftp->channel->session;
|
2010-12-14 23:49:51 +01:00
|
|
|
LIBSSH2_SFTP_PACKET *packet = _libssh2_list_first(&sftp->packets);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-12-14 11:46:53 +01:00
|
|
|
if(!packet)
|
|
|
|
return -1;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-12-14 11:46:53 +01:00
|
|
|
/* Special consideration when getting VERSION packet */
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-04-21 18:16:23 +00:00
|
|
|
while (packet) {
|
2010-12-14 23:49:51 +01:00
|
|
|
if((packet->data[0] == packet_type) &&
|
|
|
|
((packet_type == SSH_FXP_VERSION) ||
|
|
|
|
(packet->request_id == request_id))) {
|
2009-03-13 22:15:27 +00:00
|
|
|
|
|
|
|
/* Match! Fetch the data */
|
2007-04-21 18:16:23 +00:00
|
|
|
*data = packet->data;
|
|
|
|
*data_len = packet->data_len;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-13 22:15:27 +00:00
|
|
|
/* unlink and free this struct */
|
2009-08-20 00:56:05 +02:00
|
|
|
_libssh2_list_remove(&packet->node);
|
2007-04-21 18:16:23 +00:00
|
|
|
LIBSSH2_FREE(session, packet);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-04-21 18:16:23 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2009-03-13 22:15:27 +00:00
|
|
|
/* check next struct in the list */
|
2009-08-20 00:56:05 +02:00
|
|
|
packet = _libssh2_list_next(&packet->node);
|
2007-04-21 18:16:23 +00:00
|
|
|
}
|
|
|
|
return -1;
|
2004-12-22 00:20:02 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-23 13:35:20 +00:00
|
|
|
/* sftp_packet_require
|
2004-12-22 00:20:02 +00:00
|
|
|
* A la libssh2_packet_require
|
|
|
|
*/
|
2007-08-06 20:48:04 +00:00
|
|
|
static int
|
2009-03-26 15:41:14 +00:00
|
|
|
sftp_packet_require(LIBSSH2_SFTP *sftp, unsigned char packet_type,
|
2010-12-15 09:27:00 +01:00
|
|
|
uint32_t request_id, unsigned char **data,
|
2010-04-15 20:36:31 +02:00
|
|
|
size_t *data_len)
|
2004-12-22 00:20:02 +00:00
|
|
|
{
|
2007-05-28 17:56:08 +00:00
|
|
|
LIBSSH2_SESSION *session = sftp->channel->session;
|
2010-11-01 09:33:36 +01:00
|
|
|
int rc;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-12-08 08:52:03 +01:00
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Requiring packet %d id %ld",
|
2009-03-13 22:15:27 +00:00
|
|
|
(int) packet_type, request_id);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-13 22:15:27 +00:00
|
|
|
if (sftp_packet_ask(sftp, packet_type, request_id, data, data_len) == 0) {
|
2007-05-28 17:56:08 +00:00
|
|
|
/* The right packet was available in the packet brigade */
|
2009-12-08 08:52:03 +01:00
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Got %d",
|
2009-03-13 22:15:27 +00:00
|
|
|
(int) packet_type);
|
2010-11-01 09:33:36 +01:00
|
|
|
return LIBSSH2_ERROR_NONE;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
|
2010-11-01 09:33:36 +01:00
|
|
|
rc = sftp_packet_read(sftp);
|
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN)
|
|
|
|
return rc;
|
|
|
|
else if (rc <= 0)
|
|
|
|
/* TODO: isn't this supposed to be < 0 only? */
|
|
|
|
return rc;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-13 22:15:27 +00:00
|
|
|
/* data was read, check the queue again */
|
|
|
|
if (!sftp_packet_ask(sftp, packet_type, request_id, data, data_len)) {
|
|
|
|
/* The right packet was available in the packet brigade */
|
2009-12-08 08:52:03 +01:00
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Got %d",
|
2009-03-13 22:15:27 +00:00
|
|
|
(int) packet_type);
|
2010-11-01 09:33:36 +01:00
|
|
|
return LIBSSH2_ERROR_NONE;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
/* Only reached if the socket died */
|
2009-08-25 00:23:34 +02:00
|
|
|
return LIBSSH2_ERROR_SOCKET_DISCONNECT;
|
2004-12-22 00:20:02 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-23 13:35:20 +00:00
|
|
|
/* sftp_packet_requirev
|
2008-12-16 15:35:33 +00:00
|
|
|
* Require one of N possible reponses
|
2004-12-22 00:20:02 +00:00
|
|
|
*/
|
2007-08-06 20:48:04 +00:00
|
|
|
static int
|
2009-03-26 15:41:14 +00:00
|
|
|
sftp_packet_requirev(LIBSSH2_SFTP *sftp, int num_valid_responses,
|
2008-12-15 22:58:07 +00:00
|
|
|
const unsigned char *valid_responses,
|
2010-12-15 09:27:00 +01:00
|
|
|
uint32_t request_id, unsigned char **data,
|
2010-04-15 20:36:31 +02:00
|
|
|
size_t *data_len)
|
2004-12-22 00:20:02 +00:00
|
|
|
{
|
2007-04-21 18:16:23 +00:00
|
|
|
int i;
|
2010-11-01 09:33:36 +01:00
|
|
|
int rc;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2008-12-15 22:58:07 +00:00
|
|
|
/* If no timeout is active, start a new one */
|
2010-11-01 09:33:36 +01:00
|
|
|
if (sftp->requirev_start == 0)
|
2007-04-21 18:16:23 +00:00
|
|
|
sftp->requirev_start = time(NULL);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-04-21 18:16:23 +00:00
|
|
|
while (sftp->channel->session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
|
|
|
|
for(i = 0; i < num_valid_responses; i++) {
|
2008-12-16 15:35:33 +00:00
|
|
|
if (sftp_packet_ask(sftp, valid_responses[i], request_id,
|
2009-03-13 22:15:27 +00:00
|
|
|
data, data_len) == 0) {
|
2007-04-21 18:16:23 +00:00
|
|
|
/*
|
2007-08-06 20:48:04 +00:00
|
|
|
* Set to zero before all returns to say
|
2007-04-21 18:16:23 +00:00
|
|
|
* the timeout is not active
|
|
|
|
*/
|
|
|
|
sftp->requirev_start = 0;
|
2010-11-01 09:33:36 +01:00
|
|
|
return LIBSSH2_ERROR_NONE;
|
2007-04-21 18:16:23 +00:00
|
|
|
}
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-11-01 09:33:36 +01:00
|
|
|
rc = sftp_packet_read(sftp);
|
|
|
|
if ((rc < 0) && (rc != LIBSSH2_ERROR_EAGAIN)) {
|
2007-04-21 18:16:23 +00:00
|
|
|
sftp->requirev_start = 0;
|
2010-11-01 09:33:36 +01:00
|
|
|
return rc;
|
|
|
|
} else if (rc <= 0) {
|
2007-04-21 18:16:23 +00:00
|
|
|
/* prevent busy-looping */
|
2007-08-06 20:48:04 +00:00
|
|
|
long left =
|
2009-07-07 13:44:17 +02:00
|
|
|
LIBSSH2_READ_TIMEOUT - (long)(time(NULL) - sftp->requirev_start);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-18 22:38:32 +00:00
|
|
|
if (left <= 0) {
|
|
|
|
sftp->requirev_start = 0;
|
2010-04-17 13:18:15 +02:00
|
|
|
return LIBSSH2_ERROR_TIMEOUT;
|
2009-03-26 15:41:14 +00:00
|
|
|
}
|
2010-11-01 09:33:36 +01:00
|
|
|
else if (rc == LIBSSH2_ERROR_EAGAIN) {
|
|
|
|
return rc;
|
2007-04-21 18:16:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-04-21 18:16:23 +00:00
|
|
|
sftp->requirev_start = 0;
|
2010-11-01 09:33:36 +01:00
|
|
|
|
|
|
|
/* Only reached if the socket died */
|
|
|
|
return LIBSSH2_ERROR_SOCKET_DISCONNECT;
|
2004-12-22 00:20:02 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-23 13:35:20 +00:00
|
|
|
/* sftp_attr2bin
|
2004-12-22 00:20:02 +00:00
|
|
|
* Populate attributes into an SFTP block
|
|
|
|
*/
|
2010-12-29 23:36:45 +01:00
|
|
|
static ssize_t
|
2009-03-23 13:35:20 +00:00
|
|
|
sftp_attr2bin(unsigned char *p, const LIBSSH2_SFTP_ATTRIBUTES * attrs)
|
2004-12-22 00:20:02 +00:00
|
|
|
{
|
2007-05-28 17:56:08 +00:00
|
|
|
unsigned char *s = p;
|
2010-04-15 20:36:31 +02:00
|
|
|
uint32_t flag_mask =
|
2007-08-06 20:48:04 +00:00
|
|
|
LIBSSH2_SFTP_ATTR_SIZE | LIBSSH2_SFTP_ATTR_UIDGID |
|
2007-06-07 16:01:12 +00:00
|
|
|
LIBSSH2_SFTP_ATTR_PERMISSIONS | LIBSSH2_SFTP_ATTR_ACMODTIME;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-23 13:35:20 +00:00
|
|
|
/* TODO: When we add SFTP4+ functionality flag_mask can get additional
|
|
|
|
bits */
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
if (!attrs) {
|
2009-03-17 13:48:35 +00:00
|
|
|
_libssh2_htonu32(s, 0);
|
2007-05-28 17:56:08 +00:00
|
|
|
return 4;
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-04-17 13:18:15 +02:00
|
|
|
_libssh2_store_u32(&s, attrs->flags & flag_mask);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
if (attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) {
|
2010-04-17 13:18:15 +02:00
|
|
|
_libssh2_store_u64(&s, attrs->filesize);
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
if (attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) {
|
2010-04-17 13:18:15 +02:00
|
|
|
_libssh2_store_u32(&s, attrs->uid);
|
|
|
|
_libssh2_store_u32(&s, attrs->gid);
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
if (attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) {
|
2010-04-17 13:18:15 +02:00
|
|
|
_libssh2_store_u32(&s, attrs->permissions);
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
if (attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) {
|
2010-04-17 13:18:15 +02:00
|
|
|
_libssh2_store_u32(&s, attrs->atime);
|
|
|
|
_libssh2_store_u32(&s, attrs->mtime);
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
return (s - p);
|
2004-12-22 00:20:02 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-23 13:35:20 +00:00
|
|
|
/* sftp_bin2attr
|
2004-12-22 00:20:02 +00:00
|
|
|
*/
|
2007-08-06 20:48:04 +00:00
|
|
|
static int
|
2009-03-23 13:35:20 +00:00
|
|
|
sftp_bin2attr(LIBSSH2_SFTP_ATTRIBUTES * attrs, const unsigned char *p)
|
2004-12-22 00:20:02 +00:00
|
|
|
{
|
2007-05-28 17:56:08 +00:00
|
|
|
const unsigned char *s = p;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
|
2009-03-17 13:48:35 +00:00
|
|
|
attrs->flags = _libssh2_ntohu32(s);
|
2007-08-06 20:48:04 +00:00
|
|
|
s += 4;
|
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
if (attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) {
|
2009-03-17 13:48:35 +00:00
|
|
|
attrs->filesize = _libssh2_ntohu64(s);
|
2007-08-06 20:48:04 +00:00
|
|
|
s += 8;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
if (attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) {
|
2009-03-17 13:48:35 +00:00
|
|
|
attrs->uid = _libssh2_ntohu32(s);
|
2007-08-06 20:48:04 +00:00
|
|
|
s += 4;
|
2009-03-17 13:48:35 +00:00
|
|
|
attrs->gid = _libssh2_ntohu32(s);
|
2007-08-06 20:48:04 +00:00
|
|
|
s += 4;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
if (attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) {
|
2009-03-17 13:48:35 +00:00
|
|
|
attrs->permissions = _libssh2_ntohu32(s);
|
2007-08-06 20:48:04 +00:00
|
|
|
s += 4;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
if (attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) {
|
2009-03-17 13:48:35 +00:00
|
|
|
attrs->atime = _libssh2_ntohu32(s);
|
2007-08-06 20:48:04 +00:00
|
|
|
s += 4;
|
2009-03-17 13:48:35 +00:00
|
|
|
attrs->mtime = _libssh2_ntohu32(s);
|
2007-08-06 20:48:04 +00:00
|
|
|
s += 4;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
return (s - p);
|
2004-12-22 00:20:02 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2004-12-22 00:20:02 +00:00
|
|
|
/* ************
|
2010-04-16 00:18:51 +02:00
|
|
|
* SFTP API *
|
|
|
|
************ */
|
2004-12-22 00:20:02 +00:00
|
|
|
|
2007-02-02 16:21:20 +00:00
|
|
|
LIBSSH2_CHANNEL_CLOSE_FUNC(libssh2_sftp_dtor);
|
|
|
|
|
2009-03-23 13:35:20 +00:00
|
|
|
/* libssh2_sftp_dtor
|
2004-12-22 00:20:02 +00:00
|
|
|
* Shutdown an SFTP stream when the channel closes
|
|
|
|
*/
|
2007-02-02 16:21:20 +00:00
|
|
|
LIBSSH2_CHANNEL_CLOSE_FUNC(libssh2_sftp_dtor)
|
|
|
|
{
|
2007-08-06 20:48:04 +00:00
|
|
|
LIBSSH2_SFTP *sftp = (LIBSSH2_SFTP *) (*channel_abstract);
|
|
|
|
|
|
|
|
(void) session_abstract;
|
|
|
|
(void) channel;
|
|
|
|
|
2008-12-16 12:31:58 +00:00
|
|
|
/* Free the partial packet storage for sftp_packet_read */
|
2007-04-21 18:16:23 +00:00
|
|
|
if (sftp->partial_packet) {
|
|
|
|
LIBSSH2_FREE(session, sftp->partial_packet);
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-04-21 18:16:23 +00:00
|
|
|
/* Free the packet storage for _libssh2_sftp_packet_readdir */
|
|
|
|
if (sftp->readdir_packet) {
|
|
|
|
LIBSSH2_FREE(session, sftp->readdir_packet);
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-04-21 18:16:23 +00:00
|
|
|
LIBSSH2_FREE(session, sftp);
|
2004-12-22 00:20:02 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-13 22:15:27 +00:00
|
|
|
/*
|
2009-03-26 15:41:14 +00:00
|
|
|
* sftp_init
|
2009-03-13 22:15:27 +00:00
|
|
|
*
|
2004-12-22 00:20:02 +00:00
|
|
|
* Startup an SFTP session
|
|
|
|
*/
|
2009-03-26 15:41:14 +00:00
|
|
|
static LIBSSH2_SFTP *sftp_init(LIBSSH2_SESSION *session)
|
2004-12-22 00:20:02 +00:00
|
|
|
{
|
2007-06-06 12:34:06 +00:00
|
|
|
unsigned char *data, *s;
|
2010-04-15 20:36:31 +02:00
|
|
|
size_t data_len;
|
2010-12-29 23:36:45 +01:00
|
|
|
ssize_t rc;
|
2009-05-25 10:50:49 +02:00
|
|
|
LIBSSH2_SFTP *sftp_handle;
|
2007-06-12 18:27:37 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (session->sftpInit_state == libssh2_NB_state_idle) {
|
2009-12-08 08:52:03 +01:00
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP,
|
2007-08-06 20:48:04 +00:00
|
|
|
"Initializing SFTP subsystem");
|
2007-06-12 18:27:37 +00:00
|
|
|
|
2009-05-25 10:50:49 +02:00
|
|
|
/*
|
|
|
|
* The 'sftpInit_sftp' and 'sftpInit_channel' struct fields within the
|
2009-05-25 10:58:13 +02:00
|
|
|
* session struct are only to be used during the setup phase. As soon
|
2009-05-25 10:50:49 +02:00
|
|
|
* as the SFTP session is created they are cleared and can thus be
|
|
|
|
* re-used again to allow any amount of SFTP handles per sessions.
|
|
|
|
*
|
2009-08-19 13:58:15 +02:00
|
|
|
* Note that you MUST NOT try to call libssh2_sftp_init() again to get
|
|
|
|
* another handle until the previous call has finished and either
|
2009-05-25 10:50:49 +02:00
|
|
|
* succesffully made a handle or failed and returned error (not
|
|
|
|
* including *EAGAIN).
|
|
|
|
*/
|
2009-03-27 22:24:09 +00:00
|
|
|
|
2009-05-25 10:50:49 +02:00
|
|
|
assert(session->sftpInit_sftp == NULL);
|
2007-06-12 18:27:37 +00:00
|
|
|
session->sftpInit_sftp = NULL;
|
2007-06-06 12:34:06 +00:00
|
|
|
session->sftpInit_state = libssh2_NB_state_created;
|
2007-04-21 18:16:23 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-05-25 10:50:49 +02:00
|
|
|
sftp_handle = session->sftpInit_sftp;
|
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (session->sftpInit_state == libssh2_NB_state_created) {
|
2007-08-06 20:48:04 +00:00
|
|
|
session->sftpInit_channel =
|
2009-03-29 22:09:29 +00:00
|
|
|
_libssh2_channel_open(session, "session", sizeof("session") - 1,
|
|
|
|
LIBSSH2_CHANNEL_WINDOW_DEFAULT,
|
|
|
|
LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL, 0);
|
2007-06-06 12:34:06 +00:00
|
|
|
if (!session->sftpInit_channel) {
|
|
|
|
if (libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN) {
|
2010-04-16 00:18:51 +02:00
|
|
|
_libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
|
|
|
|
"Would block starting up channel");
|
2009-03-27 22:24:09 +00:00
|
|
|
}
|
|
|
|
else {
|
2010-04-16 00:18:51 +02:00
|
|
|
_libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
|
|
|
|
"Unable to startup channel");
|
2007-06-06 12:34:06 +00:00
|
|
|
session->sftpInit_state = libssh2_NB_state_idle;
|
|
|
|
}
|
2009-03-27 22:24:09 +00:00
|
|
|
return NULL;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->sftpInit_state = libssh2_NB_state_sent;
|
2007-04-21 18:16:23 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (session->sftpInit_state == libssh2_NB_state_sent) {
|
2010-12-29 23:36:45 +01:00
|
|
|
int ret = _libssh2_channel_process_startup(session->sftpInit_channel,
|
|
|
|
"subsystem",
|
|
|
|
sizeof("subsystem") - 1, "sftp",
|
|
|
|
strlen("sftp"));
|
|
|
|
if (ret == LIBSSH2_ERROR_EAGAIN) {
|
2010-04-16 00:18:51 +02:00
|
|
|
_libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
|
|
|
|
"Would block to request SFTP subsystem");
|
2007-06-06 12:34:06 +00:00
|
|
|
return NULL;
|
2010-12-29 23:36:45 +01:00
|
|
|
} else if (ret) {
|
2010-04-16 00:18:51 +02:00
|
|
|
_libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
|
|
|
|
"Unable to request SFTP subsystem");
|
2007-06-12 18:27:37 +00:00
|
|
|
goto sftp_init_error;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->sftpInit_state = libssh2_NB_state_sent1;
|
2007-04-21 18:16:23 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (session->sftpInit_state == libssh2_NB_state_sent1) {
|
2009-03-27 22:24:09 +00:00
|
|
|
rc = _libssh2_channel_extended_data(session->sftpInit_channel,
|
|
|
|
LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE);
|
2010-04-17 13:18:15 +02:00
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN) {
|
2010-04-16 00:18:51 +02:00
|
|
|
_libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
|
|
|
|
"Would block requesting handle extended data");
|
2007-06-06 12:34:06 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-05-25 10:50:49 +02:00
|
|
|
sftp_handle =
|
|
|
|
session->sftpInit_sftp =
|
|
|
|
LIBSSH2_ALLOC(session, sizeof(LIBSSH2_SFTP));
|
|
|
|
if (!sftp_handle) {
|
2010-04-16 00:18:51 +02:00
|
|
|
_libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
|
|
|
"Unable to allocate a new SFTP structure");
|
2007-06-12 18:27:37 +00:00
|
|
|
goto sftp_init_error;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
2009-05-25 10:50:49 +02:00
|
|
|
memset(sftp_handle, 0, sizeof(LIBSSH2_SFTP));
|
|
|
|
sftp_handle->channel = session->sftpInit_channel;
|
|
|
|
sftp_handle->request_id = 0;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-17 13:48:35 +00:00
|
|
|
_libssh2_htonu32(session->sftpInit_buffer, 5);
|
2007-06-06 12:34:06 +00:00
|
|
|
session->sftpInit_buffer[4] = SSH_FXP_INIT;
|
2009-03-17 13:48:35 +00:00
|
|
|
_libssh2_htonu32(session->sftpInit_buffer + 5, LIBSSH2_SFTP_VERSION);
|
2010-04-01 13:20:55 +02:00
|
|
|
session->sftpInit_sent = 0; /* nothing's sent yet */
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-12-08 08:52:03 +01:00
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP,
|
2007-08-06 20:48:04 +00:00
|
|
|
"Sending FXP_INIT packet advertising version %d support",
|
|
|
|
(int) LIBSSH2_SFTP_VERSION);
|
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->sftpInit_state = libssh2_NB_state_sent2;
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
if (session->sftpInit_state == libssh2_NB_state_sent2) {
|
2010-04-01 13:20:55 +02:00
|
|
|
/* sent off what's left of the init buffer to send */
|
2009-03-29 22:09:29 +00:00
|
|
|
rc = _libssh2_channel_write(session->sftpInit_channel, 0,
|
2010-10-23 00:11:59 +02:00
|
|
|
session->sftpInit_buffer +
|
2010-04-01 13:20:55 +02:00
|
|
|
session->sftpInit_sent,
|
|
|
|
9 - session->sftpInit_sent);
|
2010-04-17 13:18:15 +02:00
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN) {
|
2010-04-16 00:18:51 +02:00
|
|
|
_libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
|
|
|
|
"Would block sending SSH_FXP_INIT");
|
2007-06-06 12:34:06 +00:00
|
|
|
return NULL;
|
2010-04-01 13:20:55 +02:00
|
|
|
}
|
|
|
|
else if(rc < 0) {
|
2010-04-16 00:18:51 +02:00
|
|
|
_libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
|
|
|
|
"Unable to send SSH_FXP_INIT");
|
2007-06-12 18:27:37 +00:00
|
|
|
goto sftp_init_error;
|
2007-06-06 12:34:06 +00:00
|
|
|
}
|
2010-04-01 13:20:55 +02:00
|
|
|
else {
|
|
|
|
/* add up the number of bytes sent */
|
|
|
|
session->sftpInit_sent += rc;
|
|
|
|
|
|
|
|
if(session->sftpInit_sent == 9)
|
|
|
|
/* move on */
|
|
|
|
session->sftpInit_state = libssh2_NB_state_sent3;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-04-01 13:20:55 +02:00
|
|
|
/* if less than 9, we remain in this state to send more later on */
|
|
|
|
}
|
2007-04-21 18:16:23 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-05-25 10:50:49 +02:00
|
|
|
rc = sftp_packet_require(sftp_handle, SSH_FXP_VERSION,
|
2008-12-16 15:35:33 +00:00
|
|
|
0, &data, &data_len);
|
2012-03-12 22:49:25 +01:00
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN)
|
2007-06-06 12:34:06 +00:00
|
|
|
return NULL;
|
2012-03-12 22:49:25 +01:00
|
|
|
else if (rc) {
|
|
|
|
_libssh2_error(session, rc,
|
2010-04-16 00:18:51 +02:00
|
|
|
"Timeout waiting for response from SFTP subsystem");
|
2007-06-12 18:27:37 +00:00
|
|
|
goto sftp_init_error;
|
2007-04-21 18:16:23 +00:00
|
|
|
}
|
|
|
|
if (data_len < 5) {
|
2010-04-16 00:18:51 +02:00
|
|
|
_libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
|
|
|
|
"Invalid SSH_FXP_VERSION response");
|
2007-06-12 18:27:37 +00:00
|
|
|
goto sftp_init_error;
|
2007-04-21 18:16:23 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-04-21 18:16:23 +00:00
|
|
|
s = data + 1;
|
2009-05-25 10:50:49 +02:00
|
|
|
sftp_handle->version = _libssh2_ntohu32(s);
|
2007-08-06 20:48:04 +00:00
|
|
|
s += 4;
|
2009-05-25 10:50:49 +02:00
|
|
|
if (sftp_handle->version > LIBSSH2_SFTP_VERSION) {
|
2009-12-08 08:52:03 +01:00
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP,
|
2007-08-06 20:48:04 +00:00
|
|
|
"Truncating remote SFTP version from %lu",
|
2009-05-25 10:50:49 +02:00
|
|
|
sftp_handle->version);
|
|
|
|
sftp_handle->version = LIBSSH2_SFTP_VERSION;
|
2007-04-21 18:16:23 +00:00
|
|
|
}
|
2009-12-08 08:52:03 +01:00
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP,
|
2007-08-06 20:48:04 +00:00
|
|
|
"Enabling SFTP version %lu compatability",
|
2009-05-25 10:50:49 +02:00
|
|
|
sftp_handle->version);
|
2007-04-21 18:16:23 +00:00
|
|
|
while (s < (data + data_len)) {
|
2010-04-15 20:36:31 +02:00
|
|
|
size_t extname_len, extdata_len;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-17 13:48:35 +00:00
|
|
|
extname_len = _libssh2_ntohu32(s);
|
2007-08-06 20:48:04 +00:00
|
|
|
s += 4;
|
2010-04-17 19:05:53 +02:00
|
|
|
/* the extension name starts here */
|
2007-08-06 20:48:04 +00:00
|
|
|
s += extname_len;
|
|
|
|
|
2009-03-17 13:48:35 +00:00
|
|
|
extdata_len = _libssh2_ntohu32(s);
|
2007-08-06 20:48:04 +00:00
|
|
|
s += 4;
|
|
|
|
|
2007-04-21 18:16:23 +00:00
|
|
|
/* TODO: Actually process extensions */
|
2010-04-17 19:05:53 +02:00
|
|
|
s += extdata_len;
|
|
|
|
|
2007-04-21 18:16:23 +00:00
|
|
|
}
|
|
|
|
LIBSSH2_FREE(session, data);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-27 20:20:48 +00:00
|
|
|
/* Make sure that when the channel gets closed, the SFTP service is shut
|
|
|
|
down too */
|
2009-05-25 10:50:49 +02:00
|
|
|
sftp_handle->channel->abstract = sftp_handle;
|
|
|
|
sftp_handle->channel->close_cb = libssh2_sftp_dtor;
|
2007-04-21 18:16:23 +00:00
|
|
|
|
2007-06-06 12:34:06 +00:00
|
|
|
session->sftpInit_state = libssh2_NB_state_idle;
|
2009-05-25 10:50:49 +02:00
|
|
|
|
|
|
|
/* clear the sftp and channel pointers in this session struct now */
|
|
|
|
session->sftpInit_sftp = NULL;
|
|
|
|
session->sftpInit_channel = NULL;
|
|
|
|
|
2009-08-19 13:58:15 +02:00
|
|
|
_libssh2_list_init(&sftp_handle->sftp_handles);
|
|
|
|
|
2009-05-25 10:50:49 +02:00
|
|
|
return sftp_handle;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
|
|
|
sftp_init_error:
|
2010-06-11 13:05:55 +02:00
|
|
|
while (_libssh2_channel_free(session->sftpInit_channel) ==
|
|
|
|
LIBSSH2_ERROR_EAGAIN);
|
2007-06-12 18:27:37 +00:00
|
|
|
session->sftpInit_channel = NULL;
|
|
|
|
if (session->sftpInit_sftp) {
|
|
|
|
LIBSSH2_FREE(session, session->sftpInit_sftp);
|
|
|
|
session->sftpInit_sftp = NULL;
|
|
|
|
}
|
|
|
|
session->sftpInit_state = libssh2_NB_state_idle;
|
|
|
|
return NULL;
|
2004-12-22 00:20:02 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-26 15:41:14 +00:00
|
|
|
/*
|
|
|
|
* libssh2_sftp_init
|
|
|
|
*
|
|
|
|
* Startup an SFTP session
|
|
|
|
*/
|
|
|
|
LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session)
|
|
|
|
{
|
|
|
|
LIBSSH2_SFTP *ptr;
|
2010-06-07 13:10:51 +02:00
|
|
|
|
2010-06-11 13:05:55 +02:00
|
|
|
if(!session)
|
|
|
|
return NULL;
|
|
|
|
|
2010-06-07 13:10:51 +02:00
|
|
|
if(!(session->state & LIBSSH2_STATE_AUTHENTICATED)) {
|
|
|
|
_libssh2_error(session, LIBSSH2_ERROR_INVAL,
|
|
|
|
"session not authenticated yet");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-03-26 15:41:14 +00:00
|
|
|
BLOCK_ADJUST_ERRNO(ptr, session, sftp_init(session));
|
|
|
|
return ptr;
|
|
|
|
}
|
|
|
|
|
2009-03-27 20:20:48 +00:00
|
|
|
/*
|
|
|
|
* sftp_shutdown
|
|
|
|
*
|
2004-12-22 00:20:02 +00:00
|
|
|
* Shutsdown the SFTP subsystem
|
|
|
|
*/
|
2009-03-27 20:20:48 +00:00
|
|
|
static int
|
|
|
|
sftp_shutdown(LIBSSH2_SFTP *sftp)
|
2007-02-02 16:21:20 +00:00
|
|
|
{
|
2009-03-27 20:20:48 +00:00
|
|
|
int rc;
|
|
|
|
LIBSSH2_SESSION *session = sftp->channel->session;
|
2007-06-07 16:43:20 +00:00
|
|
|
/*
|
|
|
|
* Make sure all memory used in the state variables are free
|
|
|
|
*/
|
|
|
|
if (sftp->partial_packet) {
|
2009-03-27 20:20:48 +00:00
|
|
|
LIBSSH2_FREE(session, sftp->partial_packet);
|
2007-06-12 18:27:37 +00:00
|
|
|
sftp->partial_packet = NULL;
|
2007-06-07 16:43:20 +00:00
|
|
|
}
|
|
|
|
if (sftp->open_packet) {
|
2009-03-27 20:20:48 +00:00
|
|
|
LIBSSH2_FREE(session, sftp->open_packet);
|
2007-06-12 18:27:37 +00:00
|
|
|
sftp->open_packet = NULL;
|
2007-06-07 16:43:20 +00:00
|
|
|
}
|
|
|
|
if (sftp->readdir_packet) {
|
2009-03-27 20:20:48 +00:00
|
|
|
LIBSSH2_FREE(session, sftp->readdir_packet);
|
2007-06-12 18:27:37 +00:00
|
|
|
sftp->readdir_packet = NULL;
|
2007-06-07 16:43:20 +00:00
|
|
|
}
|
|
|
|
if (sftp->fstat_packet) {
|
2009-03-27 20:20:48 +00:00
|
|
|
LIBSSH2_FREE(session, sftp->fstat_packet);
|
2007-06-12 18:27:37 +00:00
|
|
|
sftp->fstat_packet = NULL;
|
2007-06-07 16:43:20 +00:00
|
|
|
}
|
|
|
|
if (sftp->unlink_packet) {
|
2009-03-27 20:20:48 +00:00
|
|
|
LIBSSH2_FREE(session, sftp->unlink_packet);
|
2007-06-12 18:27:37 +00:00
|
|
|
sftp->unlink_packet = NULL;
|
2007-06-07 16:43:20 +00:00
|
|
|
}
|
|
|
|
if (sftp->rename_packet) {
|
2009-03-27 20:20:48 +00:00
|
|
|
LIBSSH2_FREE(session, sftp->rename_packet);
|
2007-06-12 18:27:37 +00:00
|
|
|
sftp->rename_packet = NULL;
|
2007-06-07 16:43:20 +00:00
|
|
|
}
|
2010-05-22 01:48:04 -07:00
|
|
|
if (sftp->fstatvfs_packet) {
|
|
|
|
LIBSSH2_FREE(session, sftp->fstatvfs_packet);
|
|
|
|
sftp->fstatvfs_packet = NULL;
|
|
|
|
}
|
|
|
|
if (sftp->statvfs_packet) {
|
|
|
|
LIBSSH2_FREE(session, sftp->statvfs_packet);
|
|
|
|
sftp->statvfs_packet = NULL;
|
|
|
|
}
|
2007-06-07 16:43:20 +00:00
|
|
|
if (sftp->mkdir_packet) {
|
2009-03-27 20:20:48 +00:00
|
|
|
LIBSSH2_FREE(session, sftp->mkdir_packet);
|
2007-06-12 18:27:37 +00:00
|
|
|
sftp->mkdir_packet = NULL;
|
2007-06-07 16:43:20 +00:00
|
|
|
}
|
|
|
|
if (sftp->rmdir_packet) {
|
2009-03-27 20:20:48 +00:00
|
|
|
LIBSSH2_FREE(session, sftp->rmdir_packet);
|
2007-06-12 18:27:37 +00:00
|
|
|
sftp->rmdir_packet = NULL;
|
2007-06-07 16:43:20 +00:00
|
|
|
}
|
|
|
|
if (sftp->stat_packet) {
|
2009-03-27 20:20:48 +00:00
|
|
|
LIBSSH2_FREE(session, sftp->stat_packet);
|
2007-06-12 18:27:37 +00:00
|
|
|
sftp->stat_packet = NULL;
|
2007-06-07 16:43:20 +00:00
|
|
|
}
|
|
|
|
if (sftp->symlink_packet) {
|
2009-03-27 20:20:48 +00:00
|
|
|
LIBSSH2_FREE(session, sftp->symlink_packet);
|
2007-06-12 18:27:37 +00:00
|
|
|
sftp->symlink_packet = NULL;
|
2007-06-07 16:43:20 +00:00
|
|
|
}
|
2009-05-24 23:44:52 +02:00
|
|
|
|
2010-12-14 11:46:53 +01:00
|
|
|
sftp_packet_flush(sftp);
|
|
|
|
|
2009-08-19 13:58:15 +02:00
|
|
|
/* TODO: We should consider walking over the sftp_handles list and kill
|
|
|
|
* any remaining sftp handles ... */
|
|
|
|
|
2009-03-27 20:20:48 +00:00
|
|
|
rc = _libssh2_channel_free(sftp->channel);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* libssh2_sftp_shutdown
|
|
|
|
* Shutsdown the SFTP subsystem
|
|
|
|
*/
|
|
|
|
LIBSSH2_API int
|
|
|
|
libssh2_sftp_shutdown(LIBSSH2_SFTP *sftp)
|
|
|
|
{
|
|
|
|
int rc;
|
2010-06-11 13:05:55 +02:00
|
|
|
if(!sftp)
|
|
|
|
return LIBSSH2_ERROR_BAD_USE;
|
2009-08-30 17:00:49 +02:00
|
|
|
BLOCK_ADJUST(rc, sftp->channel->session, sftp_shutdown(sftp));
|
2009-03-27 20:20:48 +00:00
|
|
|
return rc;
|
2004-12-22 00:20:02 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2004-12-22 00:20:02 +00:00
|
|
|
/* *******************************
|
2010-04-16 00:18:51 +02:00
|
|
|
* SFTP File and Directory Ops *
|
|
|
|
******************************* */
|
2004-12-22 00:20:02 +00:00
|
|
|
|
2009-03-26 15:41:14 +00:00
|
|
|
/* sftp_open
|
2004-12-22 00:20:02 +00:00
|
|
|
*/
|
2009-03-26 15:41:14 +00:00
|
|
|
static LIBSSH2_SFTP_HANDLE *
|
|
|
|
sftp_open(LIBSSH2_SFTP *sftp, const char *filename,
|
2010-04-15 20:36:31 +02:00
|
|
|
size_t filename_len, uint32_t flags, long mode,
|
2009-03-26 15:41:14 +00:00
|
|
|
int open_type)
|
2004-12-22 00:20:02 +00:00
|
|
|
{
|
2007-05-28 17:56:08 +00:00
|
|
|
LIBSSH2_CHANNEL *channel = sftp->channel;
|
|
|
|
LIBSSH2_SESSION *session = channel->session;
|
|
|
|
LIBSSH2_SFTP_HANDLE *fp;
|
|
|
|
LIBSSH2_SFTP_ATTRIBUTES attrs = {
|
|
|
|
LIBSSH2_SFTP_ATTR_PERMISSIONS, 0, 0, 0, 0, 0, 0
|
|
|
|
};
|
2010-04-27 23:59:55 +02:00
|
|
|
unsigned char *s;
|
2010-12-29 23:36:45 +01:00
|
|
|
ssize_t rc;
|
2010-04-27 23:59:55 +02:00
|
|
|
int open_file = (open_type == LIBSSH2_SFTP_OPENFILE)?1:0;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
if (sftp->open_state == libssh2_NB_state_idle) {
|
2008-12-16 15:35:33 +00:00
|
|
|
/* packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) +
|
|
|
|
flags(4) */
|
2007-08-06 20:48:04 +00:00
|
|
|
sftp->open_packet_len = filename_len + 13 +
|
2010-04-28 00:23:09 +02:00
|
|
|
(open_file? (4 + sftp_attrsize(LIBSSH2_SFTP_ATTR_PERMISSIONS)) : 0);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-04-28 00:12:47 +02:00
|
|
|
/* surprise! this starts out with nothing sent */
|
|
|
|
sftp->open_packet_sent = 0;
|
2007-06-07 16:01:12 +00:00
|
|
|
s = sftp->open_packet = LIBSSH2_ALLOC(session, sftp->open_packet_len);
|
|
|
|
if (!sftp->open_packet) {
|
2010-04-16 00:18:51 +02:00
|
|
|
_libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
|
|
|
"Unable to allocate memory for FXP_OPEN or "
|
|
|
|
"FXP_OPENDIR packet");
|
2007-06-07 16:01:12 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* Filetype in SFTP 3 and earlier */
|
2007-08-06 20:48:04 +00:00
|
|
|
attrs.permissions = mode |
|
2010-04-27 23:59:55 +02:00
|
|
|
(open_file ? LIBSSH2_SFTP_ATTR_PFILETYPE_FILE :
|
2007-08-06 20:48:04 +00:00
|
|
|
LIBSSH2_SFTP_ATTR_PFILETYPE_DIR);
|
|
|
|
|
2010-04-17 13:18:15 +02:00
|
|
|
_libssh2_store_u32(&s, sftp->open_packet_len - 4);
|
2010-04-27 23:59:55 +02:00
|
|
|
*(s++) = open_file? SSH_FXP_OPEN : SSH_FXP_OPENDIR;
|
2007-06-07 16:01:12 +00:00
|
|
|
sftp->open_request_id = sftp->request_id++;
|
2010-04-17 13:18:15 +02:00
|
|
|
_libssh2_store_u32(&s, sftp->open_request_id);
|
|
|
|
_libssh2_store_str(&s, filename, filename_len);
|
|
|
|
|
2010-04-27 23:59:55 +02:00
|
|
|
if (open_file) {
|
2010-04-17 13:18:15 +02:00
|
|
|
_libssh2_store_u32(&s, flags);
|
2009-03-27 21:52:49 +00:00
|
|
|
s += sftp_attr2bin(s, &attrs);
|
2007-06-07 16:01:12 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-12-08 08:52:03 +01:00
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Sending %s open request",
|
2010-04-27 23:59:55 +02:00
|
|
|
open_file? "file" : "directory");
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
sftp->open_state = libssh2_NB_state_created;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
if (sftp->open_state == libssh2_NB_state_created) {
|
2010-10-23 00:11:59 +02:00
|
|
|
rc = _libssh2_channel_write(channel, 0, sftp->open_packet+
|
2010-04-28 00:12:47 +02:00
|
|
|
sftp->open_packet_sent,
|
|
|
|
sftp->open_packet_len -
|
|
|
|
sftp->open_packet_sent);
|
2010-04-17 13:18:15 +02:00
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN) {
|
2010-04-16 00:18:51 +02:00
|
|
|
_libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
|
|
|
|
"Would block sending FXP_OPEN or FXP_OPENDIR command");
|
2007-06-07 16:01:12 +00:00
|
|
|
return NULL;
|
2008-12-16 15:35:33 +00:00
|
|
|
}
|
2010-04-28 00:12:47 +02:00
|
|
|
else if(rc < 0) {
|
|
|
|
_libssh2_error(session, rc, "Unable to send FXP_OPEN*");
|
2007-06-07 16:01:12 +00:00
|
|
|
LIBSSH2_FREE(session, sftp->open_packet);
|
|
|
|
sftp->open_packet = NULL;
|
|
|
|
sftp->open_state = libssh2_NB_state_idle;
|
|
|
|
return NULL;
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-04-28 00:12:47 +02:00
|
|
|
/* bump the sent counter and remain in this state until the whole
|
|
|
|
data is off */
|
|
|
|
sftp->open_packet_sent += rc;
|
|
|
|
|
|
|
|
if(sftp->open_packet_len == sftp->open_packet_sent) {
|
|
|
|
LIBSSH2_FREE(session, sftp->open_packet);
|
|
|
|
sftp->open_packet = NULL;
|
|
|
|
|
|
|
|
sftp->open_state = libssh2_NB_state_sent;
|
|
|
|
}
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
if (sftp->open_state == libssh2_NB_state_sent) {
|
2010-04-27 23:59:55 +02:00
|
|
|
size_t data_len;
|
|
|
|
unsigned char *data;
|
|
|
|
static const unsigned char fopen_responses[2] =
|
|
|
|
{ SSH_FXP_HANDLE, SSH_FXP_STATUS };
|
2008-12-15 22:58:07 +00:00
|
|
|
rc = sftp_packet_requirev(sftp, 2, fopen_responses,
|
|
|
|
sftp->open_request_id, &data,
|
|
|
|
&data_len);
|
2010-04-17 13:18:15 +02:00
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN) {
|
2010-04-16 00:18:51 +02:00
|
|
|
_libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
|
|
|
|
"Would block waiting for status message");
|
2007-06-07 16:01:12 +00:00
|
|
|
return NULL;
|
2008-12-16 15:35:33 +00:00
|
|
|
}
|
2010-04-27 23:59:55 +02:00
|
|
|
sftp->open_state = libssh2_NB_state_idle;
|
|
|
|
if (rc) {
|
2010-04-16 00:18:51 +02:00
|
|
|
_libssh2_error(session, rc, "Timeout waiting for status message");
|
2007-06-07 16:01:12 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-04-27 23:59:55 +02:00
|
|
|
/* OPEN can basically get STATUS or HANDLE back, where HANDLE implies
|
|
|
|
a fine response while STATUS means error. It seems though that at
|
|
|
|
times we get an SSH_FX_OK back in a STATUS, followed the "real"
|
|
|
|
HANDLE so we need to properly deal with that. */
|
|
|
|
if (data[0] == SSH_FXP_STATUS) {
|
|
|
|
int badness = 1;
|
|
|
|
|
|
|
|
if(data_len < 9) {
|
|
|
|
_libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
|
|
|
|
"Too small FXP_STATUS");
|
|
|
|
LIBSSH2_FREE(session, data);
|
|
|
|
return NULL;
|
|
|
|
}
|
2008-12-16 15:35:33 +00:00
|
|
|
|
2010-04-27 23:59:55 +02:00
|
|
|
sftp->last_errno = _libssh2_ntohu32(data + 5);
|
|
|
|
|
|
|
|
if(LIBSSH2_FX_OK == sftp->last_errno) {
|
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP, "got HANDLE FXOK!");
|
|
|
|
|
|
|
|
LIBSSH2_FREE(session, data);
|
|
|
|
|
|
|
|
/* silly situation, but check for a HANDLE */
|
|
|
|
rc = sftp_packet_require(sftp, SSH_FXP_HANDLE,
|
|
|
|
sftp->open_request_id, &data,
|
|
|
|
&data_len);
|
|
|
|
if(rc == LIBSSH2_ERROR_EAGAIN) {
|
|
|
|
/* go back to sent state and wait for something else */
|
|
|
|
sftp->open_state = libssh2_NB_state_sent;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
else if(!rc)
|
|
|
|
/* we got the handle so this is not a bad situation */
|
|
|
|
badness = 0;
|
|
|
|
}
|
2008-12-16 15:35:33 +00:00
|
|
|
|
2010-04-27 23:59:55 +02:00
|
|
|
if(badness) {
|
|
|
|
_libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
|
|
|
|
"Failed opening remote file");
|
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP, "got FXP_STATUS %d",
|
|
|
|
sftp->last_errno);
|
|
|
|
LIBSSH2_FREE(session, data);
|
2008-12-16 15:35:33 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-27 23:59:55 +02:00
|
|
|
if(data_len < 10) {
|
2010-04-16 00:18:51 +02:00
|
|
|
_libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
|
2010-04-27 23:59:55 +02:00
|
|
|
"Too small FXP_HANDLE");
|
2008-12-16 15:35:33 +00:00
|
|
|
LIBSSH2_FREE(session, data);
|
|
|
|
return NULL;
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-04-27 23:59:55 +02:00
|
|
|
fp = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_SFTP_HANDLE));
|
|
|
|
if (!fp) {
|
|
|
|
_libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
|
|
|
"Unable to allocate new SFTP handle structure");
|
|
|
|
LIBSSH2_FREE(session, data);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
memset(fp, 0, sizeof(LIBSSH2_SFTP_HANDLE));
|
|
|
|
fp->handle_type = open_file ? LIBSSH2_SFTP_HANDLE_FILE :
|
|
|
|
LIBSSH2_SFTP_HANDLE_DIR;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-04-27 23:59:55 +02:00
|
|
|
fp->handle_len = _libssh2_ntohu32(data + 5);
|
|
|
|
if (fp->handle_len > SFTP_HANDLE_MAXLEN)
|
|
|
|
/* SFTP doesn't allow handles longer than 256 characters */
|
|
|
|
fp->handle_len = SFTP_HANDLE_MAXLEN;
|
2008-12-17 10:45:20 +00:00
|
|
|
|
2010-04-27 23:59:55 +02:00
|
|
|
if(fp->handle_len > (data_len - 9))
|
|
|
|
/* do not reach beyond the end of the data we got */
|
|
|
|
fp->handle_len = data_len - 9;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-04-27 23:59:55 +02:00
|
|
|
memcpy(fp->handle, data + 9, fp->handle_len);
|
2009-08-19 13:58:15 +02:00
|
|
|
|
2010-04-27 23:59:55 +02:00
|
|
|
LIBSSH2_FREE(session, data);
|
|
|
|
|
|
|
|
/* add this file handle to the list kept in the sftp session */
|
|
|
|
_libssh2_list_add(&sftp->sftp_handles, &fp->node);
|
|
|
|
|
|
|
|
fp->sftp = sftp; /* point to the parent struct */
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-04-27 23:59:55 +02:00
|
|
|
fp->u.file.offset = 0;
|
2010-10-25 01:43:54 +02:00
|
|
|
fp->u.file.offset_sent = 0;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-04-27 23:59:55 +02:00
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Open command successful");
|
|
|
|
return fp;
|
|
|
|
}
|
|
|
|
return NULL;
|
2004-12-22 00:20:02 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-26 15:41:14 +00:00
|
|
|
/* libssh2_sftp_open_ex
|
|
|
|
*/
|
|
|
|
LIBSSH2_API LIBSSH2_SFTP_HANDLE *
|
|
|
|
libssh2_sftp_open_ex(LIBSSH2_SFTP *sftp, const char *filename,
|
|
|
|
unsigned int filename_len, unsigned long flags, long mode,
|
|
|
|
int open_type)
|
|
|
|
{
|
|
|
|
LIBSSH2_SFTP_HANDLE *hnd;
|
2010-06-11 13:05:55 +02:00
|
|
|
|
|
|
|
if(!sftp)
|
|
|
|
return NULL;
|
|
|
|
|
2009-03-26 15:41:14 +00:00
|
|
|
BLOCK_ADJUST_ERRNO(hnd, sftp->channel->session,
|
|
|
|
sftp_open(sftp, filename, filename_len, flags, mode,
|
|
|
|
open_type));
|
|
|
|
return hnd;
|
|
|
|
}
|
|
|
|
|
2010-12-14 09:30:25 +01:00
|
|
|
/*
|
|
|
|
* sftp_read
|
|
|
|
*
|
2007-06-07 16:01:12 +00:00
|
|
|
* Read from an SFTP file handle
|
2010-12-14 09:30:25 +01:00
|
|
|
*
|
2004-12-22 00:20:02 +00:00
|
|
|
*/
|
2009-03-26 15:41:14 +00:00
|
|
|
static ssize_t sftp_read(LIBSSH2_SFTP_HANDLE * handle, char *buffer,
|
2010-12-14 09:30:25 +01:00
|
|
|
size_t buffer_size)
|
2004-12-22 00:20:02 +00:00
|
|
|
{
|
2007-08-06 20:48:04 +00:00
|
|
|
LIBSSH2_SFTP *sftp = handle->sftp;
|
2007-04-22 13:13:51 +00:00
|
|
|
LIBSSH2_CHANNEL *channel = sftp->channel;
|
|
|
|
LIBSSH2_SESSION *session = channel->session;
|
2011-01-17 22:39:47 +01:00
|
|
|
size_t count = 0;
|
2010-12-14 13:29:41 +01:00
|
|
|
struct sftp_pipeline_chunk *chunk;
|
|
|
|
struct sftp_pipeline_chunk *next;
|
2010-12-29 23:36:45 +01:00
|
|
|
ssize_t rc;
|
2010-12-14 09:30:25 +01:00
|
|
|
struct _libssh2_sftp_handle_file_data *filep =
|
|
|
|
&handle->u.file;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
/* This function can be interrupted in three different places where it
|
|
|
|
might need to wait for data from the network. It returns EAGAIN to
|
|
|
|
allow non-blocking clients to do other work but these client are
|
|
|
|
expected to call this function again (possibly many times) to finish
|
|
|
|
the operation.
|
|
|
|
|
|
|
|
The tricky part is that if we previously aborted a sftp_read due to
|
|
|
|
EAGAIN, we must continue at the same spot to continue the previously
|
|
|
|
interrupted operation. This is done using a state machine to record
|
|
|
|
what phase of execution we were at. The state is stored in
|
|
|
|
sftp->read_state.
|
|
|
|
|
|
|
|
libssh2_NB_state_idle: The first phase is where we prepare multiple
|
|
|
|
FXP_READ packets to do optimistic read-ahead. We send off as many as
|
|
|
|
possible in the second phase without waiting for a response to each
|
|
|
|
one; this is the key to fast reads. But we may have to adjust the
|
|
|
|
channel window size to do this which may interrupt this function while
|
|
|
|
waiting. The state machine saves the phase as libssh2_NB_state_idle so
|
|
|
|
it returns here on the next call.
|
|
|
|
|
|
|
|
libssh2_NB_state_sent: The second phase is where we send the FXP_READ
|
|
|
|
packets. Writing them to the channel can be interrupted with EAGAIN
|
|
|
|
but the state machine ensures we skip the first phase on the next call
|
|
|
|
and resume sending.
|
|
|
|
|
|
|
|
libssh2_NB_state_sent2: In the third phase (indicated by ) we read the
|
|
|
|
data from the responses that have arrived so far. Reading can be
|
|
|
|
interrupted with EAGAIN but the state machine ensures we skip the first
|
|
|
|
and second phases on the next call and resume sending.
|
|
|
|
*/
|
|
|
|
|
|
|
|
switch (sftp->read_state) {
|
|
|
|
case libssh2_NB_state_idle:
|
2010-12-14 09:30:25 +01:00
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
/* Some data may already have been read from the server in the
|
2012-02-09 01:31:39 +00:00
|
|
|
previous call but didn't fit in the buffer at the time. If so, we
|
|
|
|
return that now as we can't risk being interrupted later with data
|
|
|
|
partially written to the buffer. */
|
2012-02-07 16:23:11 +00:00
|
|
|
if(filep->data_left) {
|
|
|
|
size_t copy = MIN(buffer_size, filep->data_left);
|
2010-12-14 09:30:25 +01:00
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
memcpy(buffer, &filep->data[ filep->data_len - filep->data_left],
|
|
|
|
copy);
|
2010-12-14 09:30:25 +01:00
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
filep->data_left -= copy;
|
|
|
|
filep->offset += copy;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2012-02-09 01:31:39 +00:00
|
|
|
if(!filep->data_left) {
|
|
|
|
LIBSSH2_FREE(session, filep->data);
|
|
|
|
filep->data = NULL;
|
|
|
|
}
|
2011-09-09 13:54:12 +02:00
|
|
|
|
2012-02-09 01:31:39 +00:00
|
|
|
return copy;
|
2011-09-09 13:54:12 +02:00
|
|
|
}
|
2008-12-19 22:21:36 +00:00
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
/* We allow a number of bytes being requested at any given time
|
|
|
|
without having been acked - until we reach EOF. */
|
|
|
|
if(!filep->eof) {
|
2012-02-09 01:31:39 +00:00
|
|
|
/* Number of bytes asked for that haven't been acked yet */
|
|
|
|
size_t already = (filep->offset_sent - filep->offset);
|
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
size_t max_read_ahead = buffer_size*4;
|
|
|
|
unsigned long recv_window;
|
|
|
|
|
|
|
|
if(max_read_ahead > LIBSSH2_CHANNEL_WINDOW_DEFAULT*4)
|
|
|
|
max_read_ahead = LIBSSH2_CHANNEL_WINDOW_DEFAULT*4;
|
|
|
|
|
|
|
|
/* if the buffer_size passed in now is smaller than what has
|
|
|
|
already been sent, we risk getting count become a very large
|
|
|
|
number */
|
|
|
|
if(max_read_ahead > already)
|
|
|
|
count = max_read_ahead - already;
|
|
|
|
|
|
|
|
/* 'count' is how much more data to ask for, and 'already' is how
|
|
|
|
much data that already has been asked for but not yet returned.
|
|
|
|
Specificly, 'count' means how much data that have or will be
|
|
|
|
asked for by the nodes that are already added to the linked
|
|
|
|
list. Some of those read requests may not actually have been
|
|
|
|
sent off successfully yet.
|
|
|
|
|
|
|
|
If 'already' is very large it should be perfectly fine to have
|
|
|
|
count set to 0 as then we don't have to ask for more data
|
|
|
|
(right now).
|
|
|
|
|
|
|
|
buffer_size*4 is just picked more or less out of the air. The
|
|
|
|
idea is that when reading SFTP from a remote server, we send
|
|
|
|
away multiple read requests guessing that the client will read
|
|
|
|
more than only this 'buffer_size' amount of memory. So we ask
|
|
|
|
for maximum buffer_size*4 amount of data so that we can return
|
|
|
|
them very fast in subsequent calls.
|
|
|
|
*/
|
|
|
|
|
|
|
|
recv_window = libssh2_channel_window_read_ex(sftp->channel,
|
|
|
|
NULL, NULL);
|
|
|
|
if(max_read_ahead > recv_window) {
|
|
|
|
/* more data will be asked for than what the window currently
|
|
|
|
allows, expand it! */
|
|
|
|
|
|
|
|
rc = _libssh2_channel_receive_window_adjust(sftp->channel,
|
|
|
|
max_read_ahead*8,
|
|
|
|
0, NULL);
|
|
|
|
/* if this returns EAGAIN, we will get back to this function
|
|
|
|
at next call */
|
|
|
|
assert(rc != LIBSSH2_ERROR_EAGAIN || !filep->data_left);
|
|
|
|
assert(rc != LIBSSH2_ERROR_EAGAIN || !filep->eof);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
}
|
2008-12-19 22:21:36 +00:00
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
while(count > 0) {
|
|
|
|
unsigned char *s;
|
|
|
|
uint32_t size = MIN(MAX_SFTP_READ_SIZE, count);
|
|
|
|
|
|
|
|
/* 25 = packet_len(4) + packet_type(1) + request_id(4) +
|
|
|
|
handle_len(4) + offset(8) + count(4) */
|
|
|
|
uint32_t packet_len = (uint32_t)handle->handle_len + 25;
|
|
|
|
uint32_t request_id;
|
|
|
|
|
|
|
|
chunk = LIBSSH2_ALLOC(session, packet_len +
|
|
|
|
sizeof(struct sftp_pipeline_chunk));
|
|
|
|
if (!chunk)
|
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
|
|
|
"malloc fail for FXP_WRITE");
|
|
|
|
|
|
|
|
chunk->len = size;
|
|
|
|
chunk->lefttosend = packet_len;
|
|
|
|
chunk->sent = 0;
|
|
|
|
|
|
|
|
s = chunk->packet;
|
|
|
|
|
|
|
|
_libssh2_store_u32(&s, packet_len - 4);
|
|
|
|
*s++ = SSH_FXP_READ;
|
|
|
|
request_id = sftp->request_id++;
|
|
|
|
chunk->request_id = request_id;
|
|
|
|
_libssh2_store_u32(&s, request_id);
|
|
|
|
_libssh2_store_str(&s, handle->handle, handle->handle_len);
|
|
|
|
_libssh2_store_u64(&s, filep->offset_sent);
|
|
|
|
filep->offset_sent += size; /* advance offset at once */
|
|
|
|
_libssh2_store_u32(&s, size);
|
|
|
|
|
|
|
|
/* add this new entry LAST in the list */
|
|
|
|
_libssh2_list_add(&handle->packet_list, &chunk->node);
|
|
|
|
count -= size; /* deduct the size we used, as we might have
|
|
|
|
to create more packets */
|
|
|
|
}
|
2008-12-19 22:21:36 +00:00
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
case libssh2_NB_state_sent:
|
2010-12-14 09:30:25 +01:00
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
sftp->read_state = libssh2_NB_state_idle;
|
2011-09-13 20:16:59 +02:00
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
/* move through the READ packets that haven't been sent and send as
|
|
|
|
many as possible - remember that we don't block */
|
|
|
|
chunk = _libssh2_list_first(&handle->packet_list);
|
2010-12-14 09:30:25 +01:00
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
while(chunk) {
|
|
|
|
if(chunk->lefttosend) {
|
2011-09-21 16:16:27 +02:00
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
rc = _libssh2_channel_write(channel, 0,
|
|
|
|
&chunk->packet[chunk->sent],
|
|
|
|
chunk->lefttosend);
|
|
|
|
if(rc < 0) {
|
|
|
|
sftp->read_state = libssh2_NB_state_sent;
|
|
|
|
return rc;
|
|
|
|
}
|
2012-02-06 23:27:12 +01:00
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
/* remember where to continue sending the next time */
|
|
|
|
chunk->lefttosend -= rc;
|
|
|
|
chunk->sent += rc;
|
2010-12-14 09:30:25 +01:00
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
if(chunk->lefttosend)
|
|
|
|
/* data left to send, get out of loop */
|
|
|
|
break;
|
|
|
|
}
|
2010-12-14 09:30:25 +01:00
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
/* move on to the next chunk with data to send */
|
|
|
|
chunk = _libssh2_list_next(&chunk->node);
|
2007-04-22 13:13:51 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
case libssh2_NB_state_sent2:
|
2010-12-14 09:30:25 +01:00
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
sftp->read_state = libssh2_NB_state_idle;
|
2010-12-14 09:30:25 +01:00
|
|
|
|
|
|
|
/*
|
2012-02-07 16:23:11 +00:00
|
|
|
* Count all ACKed packets and act on the contents of them.
|
2010-12-14 09:30:25 +01:00
|
|
|
*/
|
2012-02-07 16:23:11 +00:00
|
|
|
chunk = _libssh2_list_first(&handle->packet_list);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
while(chunk) {
|
|
|
|
unsigned char *data;
|
|
|
|
size_t data_len;
|
|
|
|
uint32_t rc32;
|
|
|
|
static const unsigned char read_responses[2] = {
|
|
|
|
SSH_FXP_DATA, SSH_FXP_STATUS
|
|
|
|
};
|
2010-12-14 09:30:25 +01:00
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
if(chunk->lefttosend)
|
|
|
|
/* if the chunk still has data left to send, we shouldn't wait
|
|
|
|
for an ACK for it just yet */
|
|
|
|
break;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
rc = sftp_packet_requirev(sftp, 2, read_responses,
|
|
|
|
chunk->request_id, &data, &data_len);
|
|
|
|
if (rc < 0) {
|
|
|
|
sftp->read_state = libssh2_NB_state_sent2;
|
|
|
|
return rc;
|
2011-09-06 22:43:26 +02:00
|
|
|
}
|
2010-12-14 09:30:25 +01:00
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
/*
|
|
|
|
* We get DATA or STATUS back. STATUS can be error, or it is
|
|
|
|
* FX_EOF when we reach the end of the file.
|
|
|
|
*/
|
2010-12-14 09:30:25 +01:00
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
switch (data[0]) {
|
|
|
|
case SSH_FXP_STATUS:
|
|
|
|
/* we must remove all outstanding READ requests, as either we
|
|
|
|
got an error or we're at end of file */
|
|
|
|
sftp_packetlist_flush(handle);
|
2010-12-14 09:30:25 +01:00
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
rc32 = _libssh2_ntohu32(data + 5);
|
|
|
|
LIBSSH2_FREE(session, data);
|
|
|
|
|
|
|
|
if (rc32 == LIBSSH2_FX_EOF) {
|
|
|
|
filep->eof = TRUE;
|
2012-02-09 01:31:39 +00:00
|
|
|
return 0;
|
2012-02-07 16:23:11 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
sftp->last_errno = rc32;
|
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
|
|
|
|
"SFTP READ error");
|
|
|
|
}
|
|
|
|
break;
|
2010-12-14 09:30:25 +01:00
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
case SSH_FXP_DATA:
|
|
|
|
rc32 = _libssh2_ntohu32(data + 5);
|
|
|
|
if (rc32 > (data_len - 9))
|
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
|
|
|
|
"SFTP Protocol badness");
|
|
|
|
|
|
|
|
if(rc32 != chunk->len) {
|
|
|
|
/* a short read does not imply end of file, but we must
|
|
|
|
adjust the offset_sent since it was advanced with a
|
|
|
|
full chunk->len before */
|
|
|
|
filep->offset_sent -= (chunk->len - rc32);
|
|
|
|
}
|
2010-12-14 09:30:25 +01:00
|
|
|
|
2012-02-09 01:31:39 +00:00
|
|
|
if(rc32 > buffer_size) {
|
2012-02-07 16:23:11 +00:00
|
|
|
/* figure out the overlap amount */
|
2012-02-09 01:31:39 +00:00
|
|
|
filep->data_left = rc32 - buffer_size;
|
2010-12-14 09:30:25 +01:00
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
/* getting the full packet would overflow the buffer, so
|
|
|
|
only get the correct amount and keep the remainder */
|
2012-02-09 01:31:39 +00:00
|
|
|
rc32 = (uint32_t)buffer_size;
|
2012-02-07 16:23:11 +00:00
|
|
|
|
|
|
|
/* store data to keep for next call */
|
|
|
|
filep->data = data;
|
|
|
|
filep->data_len = data_len;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
filep->data_len = 0;
|
|
|
|
|
|
|
|
/* copy the received data from the received FXP_DATA packet to
|
|
|
|
the buffer at the correct index */
|
2012-02-09 01:31:39 +00:00
|
|
|
memcpy(buffer, data + 9, rc32);
|
2012-02-07 16:23:11 +00:00
|
|
|
filep->offset += rc32;
|
|
|
|
|
2012-02-09 01:31:39 +00:00
|
|
|
if(filep->data_len == 0)
|
2012-02-07 16:23:11 +00:00
|
|
|
/* free the allocated data if not stored to keep */
|
|
|
|
LIBSSH2_FREE(session, data);
|
|
|
|
|
2012-02-09 01:31:39 +00:00
|
|
|
|
|
|
|
/* remove the chunk we just processed keeping track of the
|
|
|
|
* next one in case we need it */
|
|
|
|
next = _libssh2_list_next(&chunk->node);
|
|
|
|
_libssh2_list_remove(&chunk->node);
|
|
|
|
LIBSSH2_FREE(session, chunk);
|
|
|
|
chunk = NULL;
|
|
|
|
|
|
|
|
if(rc32 > 0) {
|
|
|
|
/* we must return as we wrote some data to the buffer */
|
|
|
|
return rc32;
|
|
|
|
} else {
|
|
|
|
/* A zero-byte read is not necessarily EOF so we must not
|
|
|
|
* return 0 (that would signal EOF to the caller) so
|
|
|
|
* instead we carry on to the next chunk */
|
|
|
|
chunk = next;
|
2012-02-07 16:23:11 +00:00
|
|
|
}
|
2012-02-09 01:31:39 +00:00
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
break;
|
2012-02-09 01:31:39 +00:00
|
|
|
default:
|
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
|
|
|
|
"SFTP Protocol badness: unrecognised "
|
|
|
|
"read request response");
|
2010-12-14 09:30:25 +01:00
|
|
|
}
|
2012-02-07 16:23:11 +00:00
|
|
|
}
|
2012-02-09 01:31:39 +00:00
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
break;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2012-02-07 16:23:11 +00:00
|
|
|
default:
|
|
|
|
assert(!"State machine error; unrecognised read state");
|
2012-02-06 23:27:12 +01:00
|
|
|
}
|
|
|
|
|
2012-02-09 01:31:39 +00:00
|
|
|
return 0;
|
2007-02-02 16:21:20 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-26 15:41:14 +00:00
|
|
|
/* libssh2_sftp_read
|
|
|
|
* Read from an SFTP file handle
|
|
|
|
*/
|
|
|
|
LIBSSH2_API ssize_t
|
|
|
|
libssh2_sftp_read(LIBSSH2_SFTP_HANDLE *hnd, char *buffer,
|
|
|
|
size_t buffer_maxlen)
|
|
|
|
{
|
|
|
|
ssize_t rc;
|
2010-06-11 13:05:55 +02:00
|
|
|
if(!hnd)
|
|
|
|
return LIBSSH2_ERROR_BAD_USE;
|
2009-03-26 15:41:14 +00:00
|
|
|
BLOCK_ADJUST(rc, hnd->sftp->channel->session,
|
|
|
|
sftp_read(hnd, buffer, buffer_maxlen));
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sftp_readdir
|
2007-06-07 16:01:12 +00:00
|
|
|
* Read from an SFTP directory handle
|
2007-02-02 16:21:20 +00:00
|
|
|
*/
|
2010-12-29 23:36:45 +01:00
|
|
|
static ssize_t sftp_readdir(LIBSSH2_SFTP_HANDLE *handle, char *buffer,
|
|
|
|
size_t buffer_maxlen, char *longentry,
|
|
|
|
size_t longentry_maxlen,
|
|
|
|
LIBSSH2_SFTP_ATTRIBUTES *attrs)
|
2007-02-02 16:21:20 +00:00
|
|
|
{
|
2007-08-06 20:48:04 +00:00
|
|
|
LIBSSH2_SFTP *sftp = handle->sftp;
|
2007-05-28 17:56:08 +00:00
|
|
|
LIBSSH2_CHANNEL *channel = sftp->channel;
|
|
|
|
LIBSSH2_SESSION *session = channel->session;
|
2011-01-04 14:30:04 +01:00
|
|
|
size_t data_len;
|
2010-12-15 09:31:37 +01:00
|
|
|
uint32_t num_names;
|
2007-04-21 18:16:23 +00:00
|
|
|
/* 13 = packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */
|
2010-12-29 23:36:45 +01:00
|
|
|
uint32_t packet_len = handle->handle_len + 13;
|
2007-06-17 23:34:35 +00:00
|
|
|
unsigned char *s, *data;
|
2010-04-28 00:31:46 +02:00
|
|
|
static const unsigned char read_responses[2] = {
|
|
|
|
SSH_FXP_NAME, SSH_FXP_STATUS };
|
2010-12-29 23:36:45 +01:00
|
|
|
ssize_t retcode;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 19:52:11 +00:00
|
|
|
if (sftp->readdir_state == libssh2_NB_state_idle) {
|
2007-02-02 16:21:20 +00:00
|
|
|
if (handle->u.dir.names_left) {
|
2007-04-21 18:16:23 +00:00
|
|
|
/*
|
2007-08-06 20:48:04 +00:00
|
|
|
* A prior request returned more than one directory entry,
|
|
|
|
* feed it back from the buffer
|
2007-04-21 18:16:23 +00:00
|
|
|
*/
|
2010-04-15 01:15:35 +02:00
|
|
|
LIBSSH2_SFTP_ATTRIBUTES attrs_dummy;
|
2010-04-15 20:36:31 +02:00
|
|
|
size_t real_longentry_len;
|
2010-11-10 13:05:08 +01:00
|
|
|
size_t real_filename_len;
|
2011-01-04 14:30:04 +01:00
|
|
|
size_t filename_len;
|
|
|
|
size_t longentry_len;
|
2010-11-10 13:05:08 +01:00
|
|
|
|
|
|
|
s = (unsigned char *) handle->u.dir.next_name;
|
|
|
|
real_filename_len = _libssh2_ntohu32(s);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-04-21 18:16:23 +00:00
|
|
|
s += 4;
|
2010-04-15 01:15:35 +02:00
|
|
|
|
|
|
|
filename_len = real_filename_len;
|
2011-01-04 14:30:04 +01:00
|
|
|
if (filename_len >= buffer_maxlen) {
|
|
|
|
filename_len = LIBSSH2_ERROR_BUFFER_TOO_SMALL;
|
|
|
|
goto end;
|
|
|
|
}
|
2010-04-15 01:15:35 +02:00
|
|
|
|
2007-04-21 18:16:23 +00:00
|
|
|
memcpy(buffer, s, filename_len);
|
2010-04-15 01:15:35 +02:00
|
|
|
buffer[filename_len] = '\0'; /* zero terminate */
|
2007-04-21 18:16:23 +00:00
|
|
|
s += real_filename_len;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-04-15 01:15:35 +02:00
|
|
|
real_longentry_len = _libssh2_ntohu32(s);
|
|
|
|
s += 4;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-04-15 01:15:35 +02:00
|
|
|
if (longentry && (longentry_maxlen>1)) {
|
2007-06-15 17:22:49 +00:00
|
|
|
longentry_len = real_longentry_len;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2011-01-04 14:30:04 +01:00
|
|
|
if (longentry_len >= longentry_maxlen) {
|
|
|
|
filename_len = LIBSSH2_ERROR_BUFFER_TOO_SMALL;
|
|
|
|
goto end;
|
|
|
|
}
|
2010-04-15 01:15:35 +02:00
|
|
|
|
|
|
|
memcpy(longentry, s, longentry_len);
|
|
|
|
longentry[longentry_len] = '\0'; /* zero terminate */
|
2007-06-15 17:22:49 +00:00
|
|
|
}
|
2010-04-15 01:15:35 +02:00
|
|
|
s += real_longentry_len;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-04-15 01:15:35 +02:00
|
|
|
if (attrs)
|
2007-04-21 18:16:23 +00:00
|
|
|
memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
|
2010-04-15 01:15:35 +02:00
|
|
|
|
2009-03-23 13:35:20 +00:00
|
|
|
s += sftp_bin2attr(attrs ? attrs : &attrs_dummy, s);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
|
|
|
handle->u.dir.next_name = (char *) s;
|
2011-01-04 14:30:04 +01:00
|
|
|
end:
|
|
|
|
|
2010-04-15 01:15:35 +02:00
|
|
|
if ((--handle->u.dir.names_left) == 0)
|
2007-04-21 18:16:23 +00:00
|
|
|
LIBSSH2_FREE(session, handle->u.dir.names_packet);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-12-08 08:52:03 +01:00
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP,
|
2007-08-06 20:48:04 +00:00
|
|
|
"libssh2_sftp_readdir_ex() return %d",
|
|
|
|
filename_len);
|
2010-12-29 23:36:45 +01:00
|
|
|
return (ssize_t)filename_len;
|
2007-02-02 16:21:20 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-02-02 16:21:20 +00:00
|
|
|
/* Request another entry(entries?) */
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-17 23:34:35 +00:00
|
|
|
s = sftp->readdir_packet = LIBSSH2_ALLOC(session, packet_len);
|
2010-04-15 01:15:35 +02:00
|
|
|
if (!sftp->readdir_packet)
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
|
|
|
"Unable to allocate memory for "
|
|
|
|
"FXP_READDIR packet");
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-04-17 13:18:15 +02:00
|
|
|
_libssh2_store_u32(&s, packet_len - 4);
|
2007-02-02 16:21:20 +00:00
|
|
|
*(s++) = SSH_FXP_READDIR;
|
2007-06-17 23:34:35 +00:00
|
|
|
sftp->readdir_request_id = sftp->request_id++;
|
2010-04-17 13:18:15 +02:00
|
|
|
_libssh2_store_u32(&s, sftp->readdir_request_id);
|
|
|
|
_libssh2_store_str(&s, handle->handle, handle->handle_len);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 19:52:11 +00:00
|
|
|
sftp->readdir_state = libssh2_NB_state_created;
|
2007-04-21 18:16:23 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
if (sftp->readdir_state == libssh2_NB_state_created) {
|
2009-12-08 08:52:03 +01:00
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP,
|
2007-08-06 20:48:04 +00:00
|
|
|
"Reading entries from directory handle");
|
2010-10-23 00:11:59 +02:00
|
|
|
retcode = _libssh2_channel_write(channel, 0, sftp->readdir_packet,
|
2009-03-29 22:09:29 +00:00
|
|
|
packet_len);
|
2010-04-17 13:18:15 +02:00
|
|
|
if (retcode == LIBSSH2_ERROR_EAGAIN) {
|
2009-08-25 00:54:47 +02:00
|
|
|
return retcode;
|
2009-03-13 22:15:27 +00:00
|
|
|
}
|
2010-12-29 23:36:45 +01:00
|
|
|
else if ((ssize_t)packet_len != retcode) {
|
2007-06-17 23:34:35 +00:00
|
|
|
LIBSSH2_FREE(session, sftp->readdir_packet);
|
|
|
|
sftp->readdir_packet = NULL;
|
2007-06-07 16:01:12 +00:00
|
|
|
sftp->readdir_state = libssh2_NB_state_idle;
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
|
|
|
|
"_libssh2_channel_write() failed");
|
2007-02-02 16:21:20 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-17 23:34:35 +00:00
|
|
|
LIBSSH2_FREE(session, sftp->readdir_packet);
|
2007-04-21 18:16:23 +00:00
|
|
|
sftp->readdir_packet = NULL;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-17 23:34:35 +00:00
|
|
|
sftp->readdir_state = libssh2_NB_state_sent;
|
2007-04-21 18:16:23 +00:00
|
|
|
}
|
2007-02-02 16:21:20 +00:00
|
|
|
|
2010-04-15 01:15:35 +02:00
|
|
|
retcode = sftp_packet_requirev(sftp, 2, read_responses,
|
|
|
|
sftp->readdir_request_id, &data,
|
|
|
|
&data_len);
|
2010-04-17 13:18:15 +02:00
|
|
|
if (retcode == LIBSSH2_ERROR_EAGAIN)
|
2009-08-25 00:54:47 +02:00
|
|
|
return retcode;
|
2010-04-15 01:15:35 +02:00
|
|
|
else if (retcode) {
|
2007-06-06 19:52:11 +00:00
|
|
|
sftp->readdir_state = libssh2_NB_state_idle;
|
2012-03-12 22:52:49 +01:00
|
|
|
return _libssh2_error(session, retcode,
|
2010-04-16 00:18:51 +02:00
|
|
|
"Timeout waiting for status message");
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-02-02 16:21:20 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
if (data[0] == SSH_FXP_STATUS) {
|
2009-03-17 13:48:35 +00:00
|
|
|
retcode = _libssh2_ntohu32(data + 5);
|
2007-05-28 17:56:08 +00:00
|
|
|
LIBSSH2_FREE(session, data);
|
|
|
|
if (retcode == LIBSSH2_FX_EOF) {
|
2007-06-06 19:52:11 +00:00
|
|
|
sftp->readdir_state = libssh2_NB_state_idle;
|
2007-05-28 17:56:08 +00:00
|
|
|
return 0;
|
2010-04-15 01:15:35 +02:00
|
|
|
}
|
|
|
|
else {
|
2007-05-28 17:56:08 +00:00
|
|
|
sftp->last_errno = retcode;
|
2007-06-06 19:52:11 +00:00
|
|
|
sftp->readdir_state = libssh2_NB_state_idle;
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
|
|
|
|
"SFTP Protocol Error");
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-04-15 01:15:35 +02:00
|
|
|
sftp->readdir_state = libssh2_NB_state_idle;
|
|
|
|
|
2009-03-17 13:48:35 +00:00
|
|
|
num_names = _libssh2_ntohu32(data + 5);
|
2009-12-08 08:52:03 +01:00
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP, "%lu entries returned",
|
2007-08-06 20:48:04 +00:00
|
|
|
num_names);
|
2010-12-15 09:31:37 +01:00
|
|
|
if (!num_names) {
|
2007-05-28 17:56:08 +00:00
|
|
|
LIBSSH2_FREE(session, data);
|
2010-12-15 09:31:37 +01:00
|
|
|
return 0;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
handle->u.dir.names_left = num_names;
|
|
|
|
handle->u.dir.names_packet = data;
|
2007-08-06 20:48:04 +00:00
|
|
|
handle->u.dir.next_name = (char *) data + 9;
|
2007-02-02 16:21:20 +00:00
|
|
|
|
2010-04-15 01:15:35 +02:00
|
|
|
/* use the name popping mechanism from the start of the function */
|
|
|
|
return sftp_readdir(handle, buffer, buffer_maxlen, longentry,
|
|
|
|
longentry_maxlen, attrs);
|
2007-04-21 18:16:23 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-26 15:41:14 +00:00
|
|
|
/* libssh2_sftp_readdir_ex
|
|
|
|
* Read from an SFTP directory handle
|
|
|
|
*/
|
|
|
|
LIBSSH2_API int
|
|
|
|
libssh2_sftp_readdir_ex(LIBSSH2_SFTP_HANDLE *hnd, char *buffer,
|
|
|
|
size_t buffer_maxlen, char *longentry,
|
|
|
|
size_t longentry_maxlen,
|
|
|
|
LIBSSH2_SFTP_ATTRIBUTES *attrs)
|
|
|
|
{
|
|
|
|
int rc;
|
2010-06-11 13:05:55 +02:00
|
|
|
if(!hnd)
|
|
|
|
return LIBSSH2_ERROR_BAD_USE;
|
2009-03-26 15:41:14 +00:00
|
|
|
BLOCK_ADJUST(rc, hnd->sftp->channel->session,
|
|
|
|
sftp_readdir(hnd, buffer, buffer_maxlen, longentry,
|
|
|
|
longentry_maxlen, attrs));
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2009-05-20 14:31:46 +02:00
|
|
|
/*
|
|
|
|
* sftp_write
|
|
|
|
*
|
2009-08-24 22:28:27 +02:00
|
|
|
* Write data to an SFTP handle. Returns the number of bytes written, or
|
|
|
|
* a negative error code.
|
2010-10-25 01:43:54 +02:00
|
|
|
*
|
|
|
|
* We recommend sending very large data buffers to this function!
|
|
|
|
*
|
|
|
|
* Concept:
|
|
|
|
*
|
|
|
|
* - Detect how much of the given buffer that was already sent in a previous
|
2011-01-20 09:01:22 +01:00
|
|
|
* call by inspecting the linked list of outgoing chunks. Make sure to skip
|
|
|
|
* passed the data that has already been taken care of.
|
|
|
|
*
|
|
|
|
* - Split all (new) outgoing data in chunks no larger than N.
|
|
|
|
*
|
|
|
|
* - Each N bytes chunk gets created as a separate SFTP packet.
|
|
|
|
*
|
|
|
|
* - Add all created outgoing packets to the linked list.
|
|
|
|
*
|
|
|
|
* - Walk through the list and send the chunks that haven't been sent,
|
|
|
|
* as many as possible until EAGAIN. Some of the chunks may have been put
|
|
|
|
* in the list in a previous invoke.
|
|
|
|
*
|
|
|
|
* - For all the chunks in the list that have been completely sent off, check
|
|
|
|
* for ACKs. If a chunk has been ACKed, it is removed from the linked
|
|
|
|
* list and the "acked" counter gets increased with that data amount.
|
|
|
|
*
|
2010-10-25 01:43:54 +02:00
|
|
|
* - Return TOTAL bytes acked so far.
|
|
|
|
*
|
|
|
|
* Caveats:
|
|
|
|
* - be careful: we must not return a higher number than what was given!
|
|
|
|
*
|
2010-10-25 16:04:00 +02:00
|
|
|
* TODO:
|
|
|
|
* Introduce an option that disables this sort of "speculative" ahead writing
|
|
|
|
* as there's a risk that it will do harm to some app.
|
2007-02-02 16:21:20 +00:00
|
|
|
*/
|
2010-10-25 01:43:54 +02:00
|
|
|
|
2009-05-20 14:31:46 +02:00
|
|
|
static ssize_t sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer,
|
2009-03-26 15:41:14 +00:00
|
|
|
size_t count)
|
2007-02-02 16:21:20 +00:00
|
|
|
{
|
2007-08-06 20:48:04 +00:00
|
|
|
LIBSSH2_SFTP *sftp = handle->sftp;
|
2007-04-22 15:56:06 +00:00
|
|
|
LIBSSH2_CHANNEL *channel = sftp->channel;
|
|
|
|
LIBSSH2_SESSION *session = channel->session;
|
2010-12-29 23:36:45 +01:00
|
|
|
size_t data_len;
|
|
|
|
uint32_t retcode;
|
|
|
|
uint32_t packet_len;
|
2007-06-06 19:52:11 +00:00
|
|
|
unsigned char *s, *data;
|
2010-12-29 23:36:45 +01:00
|
|
|
ssize_t rc;
|
2010-12-14 13:29:41 +01:00
|
|
|
struct sftp_pipeline_chunk *chunk;
|
|
|
|
struct sftp_pipeline_chunk *next;
|
2010-10-25 01:43:54 +02:00
|
|
|
size_t acked = 0;
|
|
|
|
size_t org_count = count;
|
2012-02-07 00:35:51 +01:00
|
|
|
size_t already;
|
2010-10-25 01:43:54 +02:00
|
|
|
|
2012-02-07 00:35:51 +01:00
|
|
|
switch(sftp->write_state) {
|
|
|
|
default:
|
|
|
|
case libssh2_NB_state_idle:
|
2010-11-11 11:43:30 +01:00
|
|
|
|
2012-02-07 00:35:51 +01:00
|
|
|
/* Number of bytes sent off that haven't been acked and therefor we
|
|
|
|
will get passed in here again.
|
2010-11-11 11:43:30 +01:00
|
|
|
|
2012-02-07 00:35:51 +01:00
|
|
|
Also, add up the number of bytes that actually already have been
|
|
|
|
acked but we haven't been able to return as such yet, so we will
|
|
|
|
get that data as well passed in here again.
|
|
|
|
*/
|
|
|
|
already = (handle->u.file.offset_sent - handle->u.file.offset)+
|
|
|
|
handle->u.file.acked;
|
2010-10-25 01:43:54 +02:00
|
|
|
|
2012-02-07 00:35:51 +01:00
|
|
|
if(count >= already) {
|
|
|
|
/* skip the part already made into packets */
|
|
|
|
buffer += already;
|
|
|
|
count -= already;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
/* there is more data already fine than what we got in this call */
|
|
|
|
count = 0;
|
|
|
|
|
|
|
|
sftp->write_state = libssh2_NB_state_idle;
|
|
|
|
while(count) {
|
|
|
|
/* TODO: Possibly this should have some logic to prevent a very
|
|
|
|
very small fraction to be left but lets ignore that for now */
|
|
|
|
uint32_t size = MIN(MAX_SFTP_OUTGOING_SIZE, count);
|
|
|
|
uint32_t request_id;
|
2010-10-25 01:43:54 +02:00
|
|
|
|
2012-02-07 00:35:51 +01:00
|
|
|
/* 25 = packet_len(4) + packet_type(1) + request_id(4) +
|
|
|
|
handle_len(4) + offset(8) + count(4) */
|
|
|
|
packet_len = handle->handle_len + size + 25;
|
2010-04-17 13:18:15 +02:00
|
|
|
|
2012-02-07 00:35:51 +01:00
|
|
|
chunk = LIBSSH2_ALLOC(session, packet_len +
|
|
|
|
sizeof(struct sftp_pipeline_chunk));
|
|
|
|
if (!chunk)
|
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
|
|
|
"malloc fail for FXP_WRITE");
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2012-02-07 00:35:51 +01:00
|
|
|
chunk->len = size;
|
|
|
|
chunk->sent = 0;
|
|
|
|
chunk->lefttosend = packet_len;
|
2010-10-25 01:43:54 +02:00
|
|
|
|
2012-02-07 00:35:51 +01:00
|
|
|
s = chunk->packet;
|
|
|
|
_libssh2_store_u32(&s, packet_len - 4);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2012-02-07 00:35:51 +01:00
|
|
|
*(s++) = SSH_FXP_WRITE;
|
|
|
|
request_id = sftp->request_id++;
|
|
|
|
chunk->request_id = request_id;
|
|
|
|
_libssh2_store_u32(&s, request_id);
|
|
|
|
_libssh2_store_str(&s, handle->handle, handle->handle_len);
|
|
|
|
_libssh2_store_u64(&s, handle->u.file.offset_sent);
|
|
|
|
handle->u.file.offset_sent += size; /* advance offset at once */
|
|
|
|
_libssh2_store_str(&s, buffer, size);
|
2010-10-25 01:43:54 +02:00
|
|
|
|
2012-02-07 00:35:51 +01:00
|
|
|
/* add this new entry LAST in the list */
|
|
|
|
_libssh2_list_add(&handle->packet_list, &chunk->node);
|
|
|
|
|
|
|
|
buffer += size;
|
|
|
|
count -= size; /* deduct the size we used, as we might have
|
|
|
|
to create more packets */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* move through the WRITE packets that haven't been sent and send as many
|
|
|
|
as possible - remember that we don't block */
|
|
|
|
chunk = _libssh2_list_first(&handle->packet_list);
|
|
|
|
|
|
|
|
while(chunk) {
|
|
|
|
if(chunk->lefttosend) {
|
|
|
|
rc = _libssh2_channel_write(channel, 0,
|
|
|
|
&chunk->packet[chunk->sent],
|
|
|
|
chunk->lefttosend);
|
|
|
|
if(rc < 0)
|
|
|
|
/* remain in idle state */
|
2010-10-25 01:43:54 +02:00
|
|
|
return rc;
|
2012-02-07 00:35:51 +01:00
|
|
|
|
|
|
|
/* remember where to continue sending the next time */
|
|
|
|
chunk->lefttosend -= rc;
|
|
|
|
chunk->sent += rc;
|
|
|
|
|
|
|
|
if(chunk->lefttosend)
|
|
|
|
/* data left to send, get out of loop */
|
|
|
|
break;
|
2010-10-25 01:43:54 +02:00
|
|
|
}
|
|
|
|
|
2012-02-07 00:35:51 +01:00
|
|
|
/* move on to the next chunk with data to send */
|
|
|
|
chunk = _libssh2_list_next(&chunk->node);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* fall-through */
|
|
|
|
case libssh2_NB_state_sent:
|
|
|
|
|
|
|
|
sftp->write_state = libssh2_NB_state_idle;
|
|
|
|
/*
|
|
|
|
* Count all ACKed packets
|
|
|
|
*/
|
|
|
|
chunk = _libssh2_list_first(&handle->packet_list);
|
2010-10-25 01:43:54 +02:00
|
|
|
|
2012-02-07 00:35:51 +01:00
|
|
|
while(chunk) {
|
2010-10-25 01:43:54 +02:00
|
|
|
if(chunk->lefttosend)
|
2012-02-07 00:35:51 +01:00
|
|
|
/* if the chunk still has data left to send, we shouldn't wait
|
|
|
|
for an ACK for it just yet */
|
2010-10-25 01:43:54 +02:00
|
|
|
break;
|
2010-11-10 23:29:16 +01:00
|
|
|
|
2012-02-07 00:35:51 +01:00
|
|
|
else if(acked)
|
|
|
|
/* if we have sent data that is acked, we must return that
|
|
|
|
info before we call a function that might return EAGAIN */
|
|
|
|
break;
|
2010-10-25 01:43:54 +02:00
|
|
|
|
2012-02-07 00:35:51 +01:00
|
|
|
/* we check the packets in order */
|
|
|
|
rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
|
|
|
|
chunk->request_id, &data, &data_len);
|
|
|
|
if (rc < 0) {
|
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN)
|
|
|
|
sftp->write_state = libssh2_NB_state_sent;
|
|
|
|
return rc;
|
|
|
|
}
|
2010-11-10 23:29:16 +01:00
|
|
|
|
2012-02-07 00:35:51 +01:00
|
|
|
retcode = _libssh2_ntohu32(data + 5);
|
|
|
|
LIBSSH2_FREE(session, data);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2012-02-07 00:35:51 +01:00
|
|
|
sftp->last_errno = retcode;
|
|
|
|
if (retcode == LIBSSH2_FX_OK) {
|
|
|
|
acked += chunk->len; /* number of payload data that was acked
|
|
|
|
here */
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2012-02-07 00:35:51 +01:00
|
|
|
/* we increase the offset value for all acks */
|
|
|
|
handle->u.file.offset += chunk->len;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2012-02-07 00:35:51 +01:00
|
|
|
next = _libssh2_list_next(&chunk->node);
|
2010-10-25 01:43:54 +02:00
|
|
|
|
2012-02-07 00:35:51 +01:00
|
|
|
_libssh2_list_remove(&chunk->node); /* remove from list */
|
|
|
|
LIBSSH2_FREE(session, chunk); /* free memory */
|
2010-10-25 01:43:54 +02:00
|
|
|
|
2012-02-07 00:35:51 +01:00
|
|
|
chunk = next;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* flush all pending packets from the outgoing list */
|
|
|
|
sftp_packetlist_flush(handle);
|
2011-04-13 10:30:30 +02:00
|
|
|
|
2012-02-07 00:35:51 +01:00
|
|
|
/* since we return error now, the applicaton will not get any
|
|
|
|
outstanding data acked, so we need to rewind the offset to
|
|
|
|
where the application knows it has reached with acked data */
|
|
|
|
handle->u.file.offset -= handle->u.file.acked;
|
2011-04-13 13:53:07 +02:00
|
|
|
|
2012-02-07 00:35:51 +01:00
|
|
|
/* then reset the offset_sent to be the same as the offset */
|
|
|
|
handle->u.file.offset_sent = handle->u.file.offset;
|
2011-04-13 13:53:07 +02:00
|
|
|
|
2012-02-07 00:35:51 +01:00
|
|
|
/* clear the acked counter since we can have no pending data to
|
|
|
|
ack after an error */
|
|
|
|
handle->u.file.acked = 0;
|
2011-04-13 13:53:07 +02:00
|
|
|
|
2012-02-07 00:35:51 +01:00
|
|
|
/* the server returned an error for that written chunk, propagate
|
|
|
|
this back to our parent function */
|
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
|
|
|
|
"FXP write failed");
|
|
|
|
}
|
2011-04-13 10:30:30 +02:00
|
|
|
}
|
2012-02-07 00:35:51 +01:00
|
|
|
break;
|
2007-04-22 15:56:06 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-11-11 11:43:30 +01:00
|
|
|
/* if there were acked data in a previous call that wasn't returned then,
|
|
|
|
add that up and try to return it all now. This can happen if the app
|
|
|
|
first sends a huge buffer of data, and then in a second call it sends a
|
|
|
|
smaller one. */
|
|
|
|
acked += handle->u.file.acked;
|
|
|
|
|
|
|
|
if(acked) {
|
|
|
|
ssize_t ret = MIN(acked, org_count);
|
2010-10-25 01:43:54 +02:00
|
|
|
/* we got data acked so return that amount, but no more than what
|
|
|
|
was asked to get sent! */
|
2010-11-11 11:43:30 +01:00
|
|
|
|
|
|
|
/* store the remainder. 'ret' is always equal to or less than 'acked'
|
|
|
|
here */
|
|
|
|
handle->u.file.acked = acked - ret;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2012-02-07 00:35:51 +01:00
|
|
|
|
2010-11-09 13:09:08 +01:00
|
|
|
else
|
|
|
|
return 0; /* nothing was acked, and no EAGAIN was received! */
|
2007-02-02 16:21:20 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-26 15:41:14 +00:00
|
|
|
/* libssh2_sftp_write
|
|
|
|
* Write data to a file handle
|
|
|
|
*/
|
|
|
|
LIBSSH2_API ssize_t
|
|
|
|
libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *hnd, const char *buffer,
|
|
|
|
size_t count)
|
|
|
|
{
|
|
|
|
ssize_t rc;
|
2010-06-11 13:05:55 +02:00
|
|
|
if(!hnd)
|
|
|
|
return LIBSSH2_ERROR_BAD_USE;
|
2009-03-26 15:41:14 +00:00
|
|
|
BLOCK_ADJUST(rc, hnd->sftp->channel->session,
|
|
|
|
sftp_write(hnd, buffer, count));
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* sftp_fstat
|
|
|
|
*
|
2004-12-22 00:20:02 +00:00
|
|
|
* Get or Set stat on a file
|
|
|
|
*/
|
2009-03-26 15:41:14 +00:00
|
|
|
static int sftp_fstat(LIBSSH2_SFTP_HANDLE *handle,
|
|
|
|
LIBSSH2_SFTP_ATTRIBUTES *attrs, int setstat)
|
2004-12-22 00:20:02 +00:00
|
|
|
{
|
2007-08-06 20:48:04 +00:00
|
|
|
LIBSSH2_SFTP *sftp = handle->sftp;
|
2007-05-28 17:56:08 +00:00
|
|
|
LIBSSH2_CHANNEL *channel = sftp->channel;
|
|
|
|
LIBSSH2_SESSION *session = channel->session;
|
2010-04-15 20:36:31 +02:00
|
|
|
size_t data_len;
|
2007-06-07 16:01:12 +00:00
|
|
|
/* 13 = packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */
|
2010-12-29 23:36:45 +01:00
|
|
|
uint32_t packet_len =
|
2010-04-28 00:23:09 +02:00
|
|
|
handle->handle_len + 13 + (setstat ? sftp_attrsize(attrs->flags) : 0);
|
2007-06-07 16:01:12 +00:00
|
|
|
unsigned char *s, *data;
|
2007-08-06 20:48:04 +00:00
|
|
|
static const unsigned char fstat_responses[2] =
|
|
|
|
{ SSH_FXP_ATTRS, SSH_FXP_STATUS };
|
2010-12-29 23:36:45 +01:00
|
|
|
ssize_t rc;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
if (sftp->fstat_state == libssh2_NB_state_idle) {
|
2009-12-08 08:52:03 +01:00
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Issuing %s command",
|
2007-08-06 20:48:04 +00:00
|
|
|
setstat ? "set-stat" : "stat");
|
2007-06-07 16:01:12 +00:00
|
|
|
s = sftp->fstat_packet = LIBSSH2_ALLOC(session, packet_len);
|
|
|
|
if (!sftp->fstat_packet) {
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
|
|
|
"Unable to allocate memory for "
|
|
|
|
"FSTAT/FSETSTAT packet");
|
2007-06-07 16:01:12 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-04-17 13:18:15 +02:00
|
|
|
_libssh2_store_u32(&s, packet_len - 4);
|
2007-06-07 16:01:12 +00:00
|
|
|
*(s++) = setstat ? SSH_FXP_FSETSTAT : SSH_FXP_FSTAT;
|
|
|
|
sftp->fstat_request_id = sftp->request_id++;
|
2010-04-17 13:18:15 +02:00
|
|
|
_libssh2_store_u32(&s, sftp->fstat_request_id);
|
|
|
|
_libssh2_store_str(&s, handle->handle, handle->handle_len);
|
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
if (setstat) {
|
2009-03-23 13:35:20 +00:00
|
|
|
s += sftp_attr2bin(s, attrs);
|
2007-06-07 16:01:12 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
sftp->fstat_state = libssh2_NB_state_created;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
if (sftp->fstat_state == libssh2_NB_state_created) {
|
2010-10-23 00:11:59 +02:00
|
|
|
rc = _libssh2_channel_write(channel, 0, sftp->fstat_packet,
|
2009-03-29 22:09:29 +00:00
|
|
|
packet_len);
|
2010-04-17 13:18:15 +02:00
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN) {
|
2009-08-25 00:54:47 +02:00
|
|
|
return rc;
|
2010-12-29 23:36:45 +01:00
|
|
|
}
|
|
|
|
else if ((ssize_t)packet_len != rc) {
|
2007-06-07 16:01:12 +00:00
|
|
|
LIBSSH2_FREE(session, sftp->fstat_packet);
|
|
|
|
sftp->fstat_packet = NULL;
|
|
|
|
sftp->fstat_state = libssh2_NB_state_idle;
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
|
|
|
|
(setstat ? "Unable to send FXP_FSETSTAT"
|
|
|
|
: "Unable to send FXP_FSTAT command"));
|
2007-06-07 16:01:12 +00:00
|
|
|
}
|
|
|
|
LIBSSH2_FREE(session, sftp->fstat_packet);
|
|
|
|
sftp->fstat_packet = NULL;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
sftp->fstat_state = libssh2_NB_state_sent;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2008-12-15 22:58:07 +00:00
|
|
|
rc = sftp_packet_requirev(sftp, 2, fstat_responses,
|
|
|
|
sftp->fstat_request_id, &data,
|
|
|
|
&data_len);
|
2012-03-12 22:52:49 +01:00
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN)
|
2009-08-25 00:54:47 +02:00
|
|
|
return rc;
|
2012-03-12 22:52:49 +01:00
|
|
|
else if (rc) {
|
2007-06-07 16:01:12 +00:00
|
|
|
sftp->fstat_state = libssh2_NB_state_idle;
|
2012-03-12 22:52:49 +01:00
|
|
|
return _libssh2_error(session, rc,
|
2010-04-16 00:18:51 +02:00
|
|
|
"Timeout waiting for status message");
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
sftp->fstat_state = libssh2_NB_state_idle;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
if (data[0] == SSH_FXP_STATUS) {
|
2010-12-29 23:36:45 +01:00
|
|
|
uint32_t retcode;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-17 13:48:35 +00:00
|
|
|
retcode = _libssh2_ntohu32(data + 5);
|
2007-05-28 17:56:08 +00:00
|
|
|
LIBSSH2_FREE(session, data);
|
|
|
|
if (retcode == LIBSSH2_FX_OK) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
sftp->last_errno = retcode;
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
|
|
|
|
"SFTP Protocol Error");
|
2007-02-02 16:21:20 +00:00
|
|
|
}
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-23 13:35:20 +00:00
|
|
|
sftp_bin2attr(attrs, data + 5);
|
2009-11-24 14:54:10 +00:00
|
|
|
LIBSSH2_FREE(session, data);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
return 0;
|
2004-12-22 00:20:02 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-26 15:41:14 +00:00
|
|
|
/* libssh2_sftp_fstat_ex
|
|
|
|
* Get or Set stat on a file
|
|
|
|
*/
|
|
|
|
LIBSSH2_API int
|
2010-06-11 13:05:55 +02:00
|
|
|
libssh2_sftp_fstat_ex(LIBSSH2_SFTP_HANDLE *hnd,
|
2009-03-26 15:41:14 +00:00
|
|
|
LIBSSH2_SFTP_ATTRIBUTES *attrs, int setstat)
|
|
|
|
{
|
|
|
|
int rc;
|
2010-06-11 13:05:55 +02:00
|
|
|
if(!hnd || !attrs)
|
|
|
|
return LIBSSH2_ERROR_BAD_USE;
|
2009-03-26 15:41:14 +00:00
|
|
|
BLOCK_ADJUST(rc, hnd->sftp->channel->session,
|
|
|
|
sftp_fstat(hnd, attrs, setstat));
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2010-12-10 23:51:20 +01:00
|
|
|
|
|
|
|
/* libssh2_sftp_seek64
|
2004-12-22 00:20:02 +00:00
|
|
|
* Set the read/write pointer to an arbitrary position within the file
|
|
|
|
*/
|
2007-08-06 20:48:04 +00:00
|
|
|
LIBSSH2_API void
|
2010-12-10 23:51:20 +01:00
|
|
|
libssh2_sftp_seek64(LIBSSH2_SFTP_HANDLE *handle, libssh2_uint64_t offset)
|
2004-12-22 00:20:02 +00:00
|
|
|
{
|
2011-08-26 13:47:35 +02:00
|
|
|
if(handle) {
|
2010-12-10 23:51:20 +01:00
|
|
|
handle->u.file.offset = handle->u.file.offset_sent = offset;
|
2011-08-26 13:47:35 +02:00
|
|
|
/* discard all pending requests and currently read data */
|
|
|
|
sftp_packetlist_flush(handle);
|
|
|
|
|
|
|
|
/* free the left received buffered data */
|
|
|
|
if (handle->u.file.data_left) {
|
|
|
|
LIBSSH2_FREE(handle->sftp->channel->session, handle->u.file.data);
|
|
|
|
handle->u.file.data_left = handle->u.file.data_len = 0;
|
|
|
|
handle->u.file.data = NULL;
|
|
|
|
}
|
2012-01-26 12:21:12 -10:00
|
|
|
|
|
|
|
/* reset EOF to False */
|
|
|
|
handle->u.file.eof = FALSE;
|
2011-08-26 13:47:35 +02:00
|
|
|
}
|
2004-12-22 00:20:02 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-12-10 23:51:20 +01:00
|
|
|
/* libssh2_sftp_seek
|
2008-09-29 14:11:29 +00:00
|
|
|
* Set the read/write pointer to an arbitrary position within the file
|
|
|
|
*/
|
|
|
|
LIBSSH2_API void
|
2010-12-10 23:51:20 +01:00
|
|
|
libssh2_sftp_seek(LIBSSH2_SFTP_HANDLE *handle, size_t offset)
|
2008-09-29 14:11:29 +00:00
|
|
|
{
|
2010-12-10 23:51:20 +01:00
|
|
|
libssh2_sftp_seek64(handle, (libssh2_uint64_t)offset);
|
2008-09-29 14:11:29 +00:00
|
|
|
}
|
|
|
|
|
2009-03-23 13:35:20 +00:00
|
|
|
/* libssh2_sftp_tell
|
2004-12-22 00:20:02 +00:00
|
|
|
* Return the current read/write pointer's offset
|
|
|
|
*/
|
2007-08-06 20:48:04 +00:00
|
|
|
LIBSSH2_API size_t
|
2010-06-11 13:05:55 +02:00
|
|
|
libssh2_sftp_tell(LIBSSH2_SFTP_HANDLE *handle)
|
2004-12-22 00:20:02 +00:00
|
|
|
{
|
2010-06-11 13:05:55 +02:00
|
|
|
if(!handle)
|
|
|
|
return 0; /* no handle, no size */
|
|
|
|
|
2009-07-07 13:44:17 +02:00
|
|
|
/* NOTE: this may very well truncate the size if it is larger than what
|
|
|
|
size_t can hold, so libssh2_sftp_tell64() is really the function you
|
|
|
|
should use */
|
|
|
|
return (size_t)(handle->u.file.offset);
|
2004-12-22 00:20:02 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-23 13:35:20 +00:00
|
|
|
/* libssh2_sftp_tell64
|
2008-12-22 12:46:45 +00:00
|
|
|
* Return the current read/write pointer's offset
|
|
|
|
*/
|
|
|
|
LIBSSH2_API libssh2_uint64_t
|
2010-06-11 13:05:55 +02:00
|
|
|
libssh2_sftp_tell64(LIBSSH2_SFTP_HANDLE *handle)
|
2008-12-22 12:46:45 +00:00
|
|
|
{
|
2010-06-11 13:05:55 +02:00
|
|
|
if(!handle)
|
|
|
|
return 0; /* no handle, no size */
|
|
|
|
|
2008-12-22 12:46:45 +00:00
|
|
|
return handle->u.file.offset;
|
|
|
|
}
|
|
|
|
|
2010-12-14 11:46:53 +01:00
|
|
|
/*
|
|
|
|
* Flush all remaining incoming SFTP packets.
|
|
|
|
*/
|
|
|
|
static void sftp_packet_flush(LIBSSH2_SFTP *sftp)
|
|
|
|
{
|
|
|
|
LIBSSH2_CHANNEL *channel = sftp->channel;
|
|
|
|
LIBSSH2_SESSION *session = channel->session;
|
2010-12-14 23:49:51 +01:00
|
|
|
LIBSSH2_SFTP_PACKET *packet = _libssh2_list_first(&sftp->packets);
|
2010-12-14 11:46:53 +01:00
|
|
|
|
|
|
|
while(packet) {
|
2010-12-14 23:49:51 +01:00
|
|
|
LIBSSH2_SFTP_PACKET *next;
|
2010-12-14 11:46:53 +01:00
|
|
|
|
|
|
|
/* check next struct in the list */
|
|
|
|
next = _libssh2_list_next(&packet->node);
|
|
|
|
_libssh2_list_remove(&packet->node);
|
|
|
|
LIBSSH2_FREE(session, packet->data);
|
|
|
|
LIBSSH2_FREE(session, packet);
|
|
|
|
|
|
|
|
packet = next;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2009-03-26 15:41:14 +00:00
|
|
|
/* sftp_close_handle
|
|
|
|
*
|
2004-12-22 00:20:02 +00:00
|
|
|
* Close a file or directory handle
|
|
|
|
* Also frees handle resource and unlinks it from the SFTP structure
|
|
|
|
*/
|
2009-03-26 15:41:14 +00:00
|
|
|
static int
|
|
|
|
sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle)
|
2004-12-22 00:20:02 +00:00
|
|
|
{
|
2007-08-06 20:48:04 +00:00
|
|
|
LIBSSH2_SFTP *sftp = handle->sftp;
|
2007-05-28 17:56:08 +00:00
|
|
|
LIBSSH2_CHANNEL *channel = sftp->channel;
|
|
|
|
LIBSSH2_SESSION *session = channel->session;
|
2010-04-15 20:36:31 +02:00
|
|
|
size_t data_len;
|
|
|
|
int retcode;
|
2007-06-07 16:01:12 +00:00
|
|
|
/* 13 = packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */
|
2010-12-29 23:36:45 +01:00
|
|
|
uint32_t packet_len = handle->handle_len + 13;
|
2010-04-24 13:03:27 +02:00
|
|
|
unsigned char *s, *data = NULL;
|
2007-05-28 17:56:08 +00:00
|
|
|
int rc;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
if (handle->close_state == libssh2_NB_state_idle) {
|
2009-12-08 08:52:03 +01:00
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Closing handle");
|
2007-07-09 15:11:37 +00:00
|
|
|
s = handle->close_packet = LIBSSH2_ALLOC(session, packet_len);
|
|
|
|
if (!handle->close_packet) {
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
|
|
|
"Unable to allocate memory for FXP_CLOSE "
|
|
|
|
"packet");
|
2007-06-07 16:01:12 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-04-17 13:18:15 +02:00
|
|
|
_libssh2_store_u32(&s, packet_len - 4);
|
2007-06-07 16:01:12 +00:00
|
|
|
*(s++) = SSH_FXP_CLOSE;
|
|
|
|
handle->close_request_id = sftp->request_id++;
|
2010-04-17 13:18:15 +02:00
|
|
|
_libssh2_store_u32(&s, handle->close_request_id);
|
|
|
|
_libssh2_store_str(&s, handle->handle, handle->handle_len);
|
2007-06-07 16:01:12 +00:00
|
|
|
handle->close_state = libssh2_NB_state_created;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
if (handle->close_state == libssh2_NB_state_created) {
|
2010-10-23 00:11:59 +02:00
|
|
|
rc = _libssh2_channel_write(channel, 0, handle->close_packet,
|
2009-03-29 22:09:29 +00:00
|
|
|
packet_len);
|
2010-04-17 13:18:15 +02:00
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN) {
|
2009-08-25 00:54:47 +02:00
|
|
|
return rc;
|
2010-12-29 23:36:45 +01:00
|
|
|
} else if ((ssize_t)packet_len != rc) {
|
2007-07-09 15:11:37 +00:00
|
|
|
LIBSSH2_FREE(session, handle->close_packet);
|
|
|
|
handle->close_packet = NULL;
|
2007-06-07 16:01:12 +00:00
|
|
|
handle->close_state = libssh2_NB_state_idle;
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
|
|
|
|
"Unable to send FXP_CLOSE command");
|
2007-06-07 16:01:12 +00:00
|
|
|
}
|
2007-07-09 15:11:37 +00:00
|
|
|
LIBSSH2_FREE(session, handle->close_packet);
|
|
|
|
handle->close_packet = NULL;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
handle->close_state = libssh2_NB_state_sent;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
if (handle->close_state == libssh2_NB_state_sent) {
|
2008-12-16 15:35:33 +00:00
|
|
|
rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
|
|
|
|
handle->close_request_id, &data,
|
|
|
|
&data_len);
|
2010-04-17 13:18:15 +02:00
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN) {
|
2009-08-25 00:54:47 +02:00
|
|
|
return rc;
|
2007-08-06 20:48:04 +00:00
|
|
|
} else if (rc) {
|
2007-06-07 16:01:12 +00:00
|
|
|
handle->close_state = libssh2_NB_state_idle;
|
2012-03-12 22:52:49 +01:00
|
|
|
return _libssh2_error(session, rc,
|
|
|
|
"Error waiting for status message");
|
2007-06-07 16:01:12 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
handle->close_state = libssh2_NB_state_sent1;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-04-24 13:03:27 +02:00
|
|
|
if(!data)
|
|
|
|
/* if it reaches this point with data unset, something unwanted
|
|
|
|
happened (like this function is called again when in
|
|
|
|
libssh2_NB_state_sent1 state) and we just bail out */
|
|
|
|
return LIBSSH2_ERROR_INVAL;
|
|
|
|
|
2009-03-17 13:48:35 +00:00
|
|
|
retcode = _libssh2_ntohu32(data + 5);
|
2007-05-28 17:56:08 +00:00
|
|
|
LIBSSH2_FREE(session, data);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
if (retcode != LIBSSH2_FX_OK) {
|
|
|
|
sftp->last_errno = retcode;
|
2007-06-07 16:01:12 +00:00
|
|
|
handle->close_state = libssh2_NB_state_idle;
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
|
|
|
|
"SFTP Protocol Error");
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-08-19 13:58:15 +02:00
|
|
|
/* remove this handle from the parent's list */
|
|
|
|
_libssh2_list_remove(&handle->node);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
|
|
|
if ((handle->handle_type == LIBSSH2_SFTP_HANDLE_DIR)
|
|
|
|
&& handle->u.dir.names_left) {
|
2007-05-28 17:56:08 +00:00
|
|
|
LIBSSH2_FREE(session, handle->u.dir.names_packet);
|
|
|
|
}
|
2010-12-14 09:30:25 +01:00
|
|
|
else {
|
|
|
|
if(handle->u.file.data)
|
|
|
|
LIBSSH2_FREE(session, handle->u.file.data);
|
|
|
|
}
|
2010-12-14 11:46:53 +01:00
|
|
|
|
|
|
|
sftp_packetlist_flush(handle);
|
2011-09-21 16:16:27 +02:00
|
|
|
sftp->read_state = libssh2_NB_state_idle;
|
2010-10-25 16:30:09 +02:00
|
|
|
|
2007-06-15 01:41:58 +00:00
|
|
|
handle->close_state = libssh2_NB_state_idle;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
LIBSSH2_FREE(session, handle);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
return 0;
|
2004-12-22 00:20:02 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-26 15:41:14 +00:00
|
|
|
/* libssh2_sftp_close_handle
|
|
|
|
*
|
|
|
|
* Close a file or directory handle
|
|
|
|
* Also frees handle resource and unlinks it from the SFTP structure
|
|
|
|
*/
|
|
|
|
LIBSSH2_API int
|
|
|
|
libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE *hnd)
|
|
|
|
{
|
|
|
|
int rc;
|
2010-06-11 13:05:55 +02:00
|
|
|
if(!hnd)
|
|
|
|
return LIBSSH2_ERROR_BAD_USE;
|
2009-03-26 15:41:14 +00:00
|
|
|
BLOCK_ADJUST(rc, hnd->sftp->channel->session, sftp_close_handle(hnd));
|
|
|
|
return rc;
|
|
|
|
}
|
2004-12-22 00:20:02 +00:00
|
|
|
|
2009-03-26 15:41:14 +00:00
|
|
|
/* sftp_unlink
|
2004-12-22 00:20:02 +00:00
|
|
|
* Delete a file from the remote server
|
|
|
|
*/
|
2009-03-26 15:41:14 +00:00
|
|
|
static int sftp_unlink(LIBSSH2_SFTP *sftp, const char *filename,
|
2010-12-29 23:36:45 +01:00
|
|
|
size_t filename_len)
|
2004-12-22 00:20:02 +00:00
|
|
|
{
|
2007-05-28 17:56:08 +00:00
|
|
|
LIBSSH2_CHANNEL *channel = sftp->channel;
|
|
|
|
LIBSSH2_SESSION *session = channel->session;
|
2010-04-15 20:36:31 +02:00
|
|
|
size_t data_len;
|
|
|
|
int retcode;
|
2007-06-07 16:01:12 +00:00
|
|
|
/* 13 = packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) */
|
2010-12-29 23:36:45 +01:00
|
|
|
uint32_t packet_len = filename_len + 13;
|
2007-06-07 16:01:12 +00:00
|
|
|
unsigned char *s, *data;
|
2007-05-28 17:56:08 +00:00
|
|
|
int rc;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
if (sftp->unlink_state == libssh2_NB_state_idle) {
|
2009-12-08 08:52:03 +01:00
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Unlinking %s", filename);
|
2007-06-07 16:01:12 +00:00
|
|
|
s = sftp->unlink_packet = LIBSSH2_ALLOC(session, packet_len);
|
|
|
|
if (!sftp->unlink_packet) {
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
|
|
|
"Unable to allocate memory for FXP_REMOVE "
|
|
|
|
"packet");
|
2007-06-07 16:01:12 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-04-17 13:18:15 +02:00
|
|
|
_libssh2_store_u32(&s, packet_len - 4);
|
2007-06-07 16:01:12 +00:00
|
|
|
*(s++) = SSH_FXP_REMOVE;
|
|
|
|
sftp->unlink_request_id = sftp->request_id++;
|
2010-04-17 13:18:15 +02:00
|
|
|
_libssh2_store_u32(&s, sftp->unlink_request_id);
|
|
|
|
_libssh2_store_str(&s, filename, filename_len);
|
2007-06-07 16:01:12 +00:00
|
|
|
sftp->unlink_state = libssh2_NB_state_created;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
if (sftp->unlink_state == libssh2_NB_state_created) {
|
2010-10-23 00:11:59 +02:00
|
|
|
rc = _libssh2_channel_write(channel, 0, sftp->unlink_packet,
|
2009-03-29 22:09:29 +00:00
|
|
|
packet_len);
|
2010-04-17 13:18:15 +02:00
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN) {
|
2009-08-25 00:54:47 +02:00
|
|
|
return rc;
|
2010-12-29 23:36:45 +01:00
|
|
|
} else if ((ssize_t)packet_len != rc) {
|
2007-06-07 16:01:12 +00:00
|
|
|
LIBSSH2_FREE(session, sftp->unlink_packet);
|
|
|
|
sftp->unlink_packet = NULL;
|
|
|
|
sftp->unlink_state = libssh2_NB_state_idle;
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
|
|
|
|
"Unable to send FXP_REMOVE command");
|
2007-06-07 16:01:12 +00:00
|
|
|
}
|
|
|
|
LIBSSH2_FREE(session, sftp->unlink_packet);
|
|
|
|
sftp->unlink_packet = NULL;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
sftp->unlink_state = libssh2_NB_state_sent;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2008-12-16 15:35:33 +00:00
|
|
|
rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
|
|
|
|
sftp->unlink_request_id, &data,
|
|
|
|
&data_len);
|
2010-04-17 13:18:15 +02:00
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN) {
|
2009-08-25 00:54:47 +02:00
|
|
|
return rc;
|
2008-12-16 15:35:33 +00:00
|
|
|
}
|
|
|
|
else if (rc) {
|
2007-06-07 16:01:12 +00:00
|
|
|
sftp->unlink_state = libssh2_NB_state_idle;
|
2012-03-12 22:52:49 +01:00
|
|
|
return _libssh2_error(session, rc,
|
|
|
|
"Error waiting for FXP STATUS");
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
sftp->unlink_state = libssh2_NB_state_idle;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-17 13:48:35 +00:00
|
|
|
retcode = _libssh2_ntohu32(data + 5);
|
2007-05-28 17:56:08 +00:00
|
|
|
LIBSSH2_FREE(session, data);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
if (retcode == LIBSSH2_FX_OK) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
sftp->last_errno = retcode;
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
|
|
|
|
"SFTP Protocol Error");
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2004-12-22 00:20:02 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-26 15:41:14 +00:00
|
|
|
/* libssh2_sftp_unlink_ex
|
|
|
|
* Delete a file from the remote server
|
2004-12-22 00:20:02 +00:00
|
|
|
*/
|
2007-08-06 20:48:04 +00:00
|
|
|
LIBSSH2_API int
|
2009-03-26 15:41:14 +00:00
|
|
|
libssh2_sftp_unlink_ex(LIBSSH2_SFTP *sftp, const char *filename,
|
|
|
|
unsigned int filename_len)
|
|
|
|
{
|
|
|
|
int rc;
|
2010-06-11 13:05:55 +02:00
|
|
|
if(!sftp)
|
|
|
|
return LIBSSH2_ERROR_BAD_USE;
|
2009-03-26 15:41:14 +00:00
|
|
|
BLOCK_ADJUST(rc, sftp->channel->session,
|
|
|
|
sftp_unlink(sftp, filename, filename_len));
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* sftp_rename
|
|
|
|
*
|
|
|
|
* Rename a file on the remote server
|
|
|
|
*/
|
|
|
|
static int sftp_rename(LIBSSH2_SFTP *sftp, const char *source_filename,
|
2007-08-06 20:48:04 +00:00
|
|
|
unsigned int source_filename_len,
|
|
|
|
const char *dest_filename,
|
|
|
|
unsigned int dest_filename_len, long flags)
|
2004-12-22 00:20:02 +00:00
|
|
|
{
|
2007-05-28 17:56:08 +00:00
|
|
|
LIBSSH2_CHANNEL *channel = sftp->channel;
|
|
|
|
LIBSSH2_SESSION *session = channel->session;
|
2010-04-15 20:36:31 +02:00
|
|
|
size_t data_len;
|
2009-04-06 14:50:29 +00:00
|
|
|
int retcode;
|
2010-12-29 23:36:45 +01:00
|
|
|
uint32_t packet_len =
|
2007-08-06 20:48:04 +00:00
|
|
|
source_filename_len + dest_filename_len + 17 + (sftp->version >=
|
|
|
|
5 ? 4 : 0);
|
2007-05-28 17:56:08 +00:00
|
|
|
/* packet_len(4) + packet_type(1) + request_id(4) +
|
2007-08-06 20:48:04 +00:00
|
|
|
source_filename_len(4) + dest_filename_len(4) + flags(4){SFTP5+) */
|
2007-06-07 16:01:12 +00:00
|
|
|
unsigned char *data;
|
2010-12-29 23:36:45 +01:00
|
|
|
ssize_t rc;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
if (sftp->version < 2) {
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
|
|
|
|
"Server does not support RENAME");
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
if (sftp->rename_state == libssh2_NB_state_idle) {
|
2009-12-08 08:52:03 +01:00
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Renaming %s to %s",
|
2007-08-06 20:48:04 +00:00
|
|
|
source_filename, dest_filename);
|
|
|
|
sftp->rename_s = sftp->rename_packet =
|
|
|
|
LIBSSH2_ALLOC(session, packet_len);
|
2007-06-07 16:01:12 +00:00
|
|
|
if (!sftp->rename_packet) {
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
|
|
|
"Unable to allocate memory for FXP_RENAME "
|
|
|
|
"packet");
|
2007-06-07 16:01:12 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-04-17 13:18:15 +02:00
|
|
|
_libssh2_store_u32(&sftp->rename_s, packet_len - 4);
|
2007-06-07 16:01:12 +00:00
|
|
|
*(sftp->rename_s++) = SSH_FXP_RENAME;
|
|
|
|
sftp->rename_request_id = sftp->request_id++;
|
2010-04-17 13:18:15 +02:00
|
|
|
_libssh2_store_u32(&sftp->rename_s, sftp->rename_request_id);
|
|
|
|
_libssh2_store_str(&sftp->rename_s, source_filename,
|
|
|
|
source_filename_len);
|
|
|
|
_libssh2_store_str(&sftp->rename_s, dest_filename, dest_filename_len);
|
|
|
|
|
|
|
|
if (sftp->version >= 5)
|
|
|
|
_libssh2_store_u32(&sftp->rename_s, flags);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
sftp->rename_state = libssh2_NB_state_created;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
if (sftp->rename_state == libssh2_NB_state_created) {
|
2010-10-23 00:11:59 +02:00
|
|
|
rc = _libssh2_channel_write(channel, 0, sftp->rename_packet,
|
2009-03-29 22:09:29 +00:00
|
|
|
sftp->rename_s - sftp->rename_packet);
|
2010-04-17 13:18:15 +02:00
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN) {
|
2009-08-25 00:54:47 +02:00
|
|
|
return rc;
|
2010-12-29 23:36:45 +01:00
|
|
|
} else if ((ssize_t)packet_len != rc) {
|
2007-06-07 16:01:12 +00:00
|
|
|
LIBSSH2_FREE(session, sftp->rename_packet);
|
|
|
|
sftp->rename_packet = NULL;
|
|
|
|
sftp->rename_state = libssh2_NB_state_idle;
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
|
|
|
|
"Unable to send FXP_RENAME command");
|
2007-06-07 16:01:12 +00:00
|
|
|
}
|
|
|
|
LIBSSH2_FREE(session, sftp->rename_packet);
|
|
|
|
sftp->rename_packet = NULL;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
sftp->rename_state = libssh2_NB_state_sent;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2008-12-16 15:35:33 +00:00
|
|
|
rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
|
|
|
|
sftp->rename_request_id, &data,
|
|
|
|
&data_len);
|
2010-04-17 13:18:15 +02:00
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN) {
|
2009-08-25 00:54:47 +02:00
|
|
|
return rc;
|
2007-08-06 20:48:04 +00:00
|
|
|
} else if (rc) {
|
2007-06-07 16:01:12 +00:00
|
|
|
sftp->rename_state = libssh2_NB_state_idle;
|
2012-03-12 22:52:49 +01:00
|
|
|
return _libssh2_error(session, rc,
|
|
|
|
"Error waiting for FXP STATUS");
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
sftp->rename_state = libssh2_NB_state_idle;
|
|
|
|
|
2009-03-17 13:48:35 +00:00
|
|
|
retcode = _libssh2_ntohu32(data + 5);
|
2007-05-28 17:56:08 +00:00
|
|
|
LIBSSH2_FREE(session, data);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-03-03 23:04:05 +01:00
|
|
|
sftp->last_errno = retcode;
|
|
|
|
|
|
|
|
/* now convert the SFTP error code to libssh2 return code or error
|
|
|
|
message */
|
2007-05-28 17:56:08 +00:00
|
|
|
switch (retcode) {
|
2007-08-06 20:48:04 +00:00
|
|
|
case LIBSSH2_FX_OK:
|
2010-03-03 23:04:05 +01:00
|
|
|
retcode = LIBSSH2_ERROR_NONE;
|
2007-08-06 20:48:04 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LIBSSH2_FX_FILE_ALREADY_EXISTS:
|
2010-04-16 00:18:51 +02:00
|
|
|
retcode = _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
|
|
|
|
"File already exists and "
|
|
|
|
"SSH_FXP_RENAME_OVERWRITE not specified");
|
2007-08-06 20:48:04 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LIBSSH2_FX_OP_UNSUPPORTED:
|
2010-04-16 00:18:51 +02:00
|
|
|
retcode = _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
|
|
|
|
"Operation Not Supported");
|
2007-08-06 20:48:04 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2010-04-16 00:18:51 +02:00
|
|
|
retcode = _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
|
|
|
|
"SFTP Protocol Error");
|
2010-03-03 23:04:05 +01:00
|
|
|
break;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
return retcode;
|
2004-12-22 00:20:02 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-26 15:41:14 +00:00
|
|
|
/* libssh2_sftp_rename_ex
|
|
|
|
* Rename a file on the remote server
|
2004-12-22 00:20:02 +00:00
|
|
|
*/
|
2007-08-06 20:48:04 +00:00
|
|
|
LIBSSH2_API int
|
2009-03-26 15:41:14 +00:00
|
|
|
libssh2_sftp_rename_ex(LIBSSH2_SFTP *sftp, const char *source_filename,
|
|
|
|
unsigned int source_filename_len,
|
|
|
|
const char *dest_filename,
|
|
|
|
unsigned int dest_filename_len, long flags)
|
|
|
|
{
|
|
|
|
int rc;
|
2010-06-11 13:05:55 +02:00
|
|
|
if(!sftp)
|
|
|
|
return LIBSSH2_ERROR_BAD_USE;
|
2009-03-26 15:41:14 +00:00
|
|
|
BLOCK_ADJUST(rc, sftp->channel->session,
|
|
|
|
sftp_rename(sftp, source_filename, source_filename_len,
|
|
|
|
dest_filename, dest_filename_len, flags));
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2010-05-22 01:48:04 -07:00
|
|
|
/*
|
|
|
|
* sftp_fstatvfs
|
|
|
|
*
|
|
|
|
* Get file system statistics
|
|
|
|
*/
|
|
|
|
static int sftp_fstatvfs(LIBSSH2_SFTP_HANDLE *handle, LIBSSH2_SFTP_STATVFS *st)
|
|
|
|
{
|
|
|
|
LIBSSH2_SFTP *sftp = handle->sftp;
|
|
|
|
LIBSSH2_CHANNEL *channel = sftp->channel;
|
|
|
|
LIBSSH2_SESSION *session = channel->session;
|
|
|
|
size_t data_len;
|
|
|
|
/* 17 = packet_len(4) + packet_type(1) + request_id(4) + ext_len(4)
|
|
|
|
+ handle_len (4) */
|
|
|
|
/* 20 = strlen ("fstatvfs@openssh.com") */
|
2010-12-29 23:36:45 +01:00
|
|
|
uint32_t packet_len = handle->handle_len + 20 + 17;
|
2010-05-22 01:48:04 -07:00
|
|
|
unsigned char *packet, *s, *data;
|
2010-12-29 23:36:45 +01:00
|
|
|
ssize_t rc;
|
2010-05-22 01:48:04 -07:00
|
|
|
unsigned int flag;
|
|
|
|
|
|
|
|
if (sftp->fstatvfs_state == libssh2_NB_state_idle) {
|
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP,
|
|
|
|
"Getting file system statistics");
|
|
|
|
s = packet = LIBSSH2_ALLOC(session, packet_len);
|
|
|
|
if (!packet) {
|
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
|
|
|
"Unable to allocate memory for FXP_EXTENDED "
|
|
|
|
"packet");
|
|
|
|
}
|
|
|
|
|
|
|
|
_libssh2_store_u32(&s, packet_len - 4);
|
|
|
|
*(s++) = SSH_FXP_EXTENDED;
|
|
|
|
sftp->fstatvfs_request_id = sftp->request_id++;
|
|
|
|
_libssh2_store_u32(&s, sftp->fstatvfs_request_id);
|
|
|
|
_libssh2_store_str(&s, "fstatvfs@openssh.com", 20);
|
|
|
|
_libssh2_store_str(&s, handle->handle, handle->handle_len);
|
|
|
|
|
|
|
|
sftp->fstatvfs_state = libssh2_NB_state_created;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
packet = sftp->fstatvfs_packet;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sftp->fstatvfs_state == libssh2_NB_state_created) {
|
2010-10-23 00:11:59 +02:00
|
|
|
rc = _libssh2_channel_write(channel, 0, packet, packet_len);
|
2010-12-29 23:36:45 +01:00
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN ||
|
|
|
|
(0 <= rc && rc < (ssize_t)packet_len)) {
|
2010-05-22 01:48:04 -07:00
|
|
|
sftp->fstatvfs_packet = packet;
|
|
|
|
return LIBSSH2_ERROR_EAGAIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
LIBSSH2_FREE(session, packet);
|
|
|
|
sftp->fstatvfs_packet = NULL;
|
|
|
|
|
|
|
|
if (rc < 0) {
|
|
|
|
sftp->fstatvfs_state = libssh2_NB_state_idle;
|
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
|
|
|
|
"_libssh2_channel_write() failed");
|
|
|
|
}
|
|
|
|
sftp->fstatvfs_state = libssh2_NB_state_sent;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = sftp_packet_require(sftp, SSH_FXP_EXTENDED_REPLY,
|
|
|
|
sftp->fstatvfs_request_id, &data, &data_len);
|
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN) {
|
|
|
|
return rc;
|
|
|
|
} else if (rc) {
|
|
|
|
sftp->fstatvfs_state = libssh2_NB_state_idle;
|
2012-03-12 22:52:49 +01:00
|
|
|
return _libssh2_error(session, rc,
|
|
|
|
"Error waiting for FXP EXTENDED REPLY");
|
2010-05-22 01:48:04 -07:00
|
|
|
} else if (data_len < 93) {
|
|
|
|
LIBSSH2_FREE(session, data);
|
|
|
|
sftp->fstatvfs_state = libssh2_NB_state_idle;
|
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
|
|
|
|
"SFTP Protocol Error: short response");
|
|
|
|
}
|
|
|
|
|
|
|
|
sftp->fstatvfs_state = libssh2_NB_state_idle;
|
|
|
|
|
|
|
|
st->f_bsize = _libssh2_ntohu64(data + 5);
|
|
|
|
st->f_frsize = _libssh2_ntohu64(data + 13);
|
|
|
|
st->f_blocks = _libssh2_ntohu64(data + 21);
|
|
|
|
st->f_bfree = _libssh2_ntohu64(data + 29);
|
|
|
|
st->f_bavail = _libssh2_ntohu64(data + 37);
|
|
|
|
st->f_files = _libssh2_ntohu64(data + 45);
|
|
|
|
st->f_ffree = _libssh2_ntohu64(data + 53);
|
|
|
|
st->f_favail = _libssh2_ntohu64(data + 61);
|
|
|
|
st->f_fsid = _libssh2_ntohu64(data + 69);
|
|
|
|
flag = _libssh2_ntohu64(data + 77);
|
|
|
|
st->f_namemax = _libssh2_ntohu64(data + 85);
|
|
|
|
|
|
|
|
st->f_flag = (flag & SSH_FXE_STATVFS_ST_RDONLY)
|
|
|
|
? LIBSSH2_SFTP_ST_RDONLY : 0;
|
|
|
|
st->f_flag |= (flag & SSH_FXE_STATVFS_ST_NOSUID)
|
|
|
|
? LIBSSH2_SFTP_ST_NOSUID : 0;
|
|
|
|
|
|
|
|
LIBSSH2_FREE(session, data);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* libssh2_sftp_fstatvfs
|
|
|
|
* Get filesystem space and inode utilization (requires fstatvfs@openssh.com
|
|
|
|
* support on the server)
|
|
|
|
*/
|
|
|
|
LIBSSH2_API int
|
|
|
|
libssh2_sftp_fstatvfs(LIBSSH2_SFTP_HANDLE *handle, LIBSSH2_SFTP_STATVFS *st)
|
|
|
|
{
|
|
|
|
int rc;
|
2010-06-11 13:05:55 +02:00
|
|
|
if(!handle || !st)
|
|
|
|
return LIBSSH2_ERROR_BAD_USE;
|
2010-05-22 01:48:04 -07:00
|
|
|
BLOCK_ADJUST(rc, handle->sftp->channel->session, sftp_fstatvfs(handle, st));
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* sftp_statvfs
|
|
|
|
*
|
|
|
|
* Get file system statistics
|
|
|
|
*/
|
|
|
|
static int sftp_statvfs(LIBSSH2_SFTP *sftp, const char *path,
|
|
|
|
unsigned int path_len, LIBSSH2_SFTP_STATVFS *st)
|
|
|
|
{
|
|
|
|
LIBSSH2_CHANNEL *channel = sftp->channel;
|
|
|
|
LIBSSH2_SESSION *session = channel->session;
|
|
|
|
size_t data_len;
|
|
|
|
/* 17 = packet_len(4) + packet_type(1) + request_id(4) + ext_len(4)
|
|
|
|
+ path_len (4) */
|
|
|
|
/* 19 = strlen ("statvfs@openssh.com") */
|
2010-12-29 23:36:45 +01:00
|
|
|
uint32_t packet_len = path_len + 19 + 17;
|
2010-05-22 01:48:04 -07:00
|
|
|
unsigned char *packet, *s, *data;
|
2010-12-29 23:36:45 +01:00
|
|
|
ssize_t rc;
|
2010-05-22 01:48:04 -07:00
|
|
|
unsigned int flag;
|
|
|
|
|
|
|
|
if (sftp->statvfs_state == libssh2_NB_state_idle) {
|
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP,
|
|
|
|
"Getting file system statistics of %s", path);
|
|
|
|
s = packet = LIBSSH2_ALLOC(session, packet_len);
|
|
|
|
if (!packet) {
|
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
|
|
|
"Unable to allocate memory for FXP_EXTENDED "
|
|
|
|
"packet");
|
|
|
|
}
|
|
|
|
|
|
|
|
_libssh2_store_u32(&s, packet_len - 4);
|
|
|
|
*(s++) = SSH_FXP_EXTENDED;
|
|
|
|
sftp->statvfs_request_id = sftp->request_id++;
|
|
|
|
_libssh2_store_u32(&s, sftp->statvfs_request_id);
|
|
|
|
_libssh2_store_str(&s, "statvfs@openssh.com", 19);
|
|
|
|
_libssh2_store_str(&s, path, path_len);
|
|
|
|
|
|
|
|
sftp->statvfs_state = libssh2_NB_state_created;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
packet = sftp->statvfs_packet;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sftp->statvfs_state == libssh2_NB_state_created) {
|
2010-10-23 00:11:59 +02:00
|
|
|
rc = _libssh2_channel_write(channel, 0, packet, packet_len);
|
2010-12-29 23:36:45 +01:00
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN ||
|
|
|
|
(0 <= rc && rc < (ssize_t)packet_len)) {
|
2010-05-22 01:48:04 -07:00
|
|
|
sftp->statvfs_packet = packet;
|
|
|
|
return LIBSSH2_ERROR_EAGAIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
LIBSSH2_FREE(session, packet);
|
|
|
|
sftp->statvfs_packet = NULL;
|
|
|
|
|
|
|
|
if (rc < 0) {
|
|
|
|
sftp->statvfs_state = libssh2_NB_state_idle;
|
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
|
|
|
|
"_libssh2_channel_write() failed");
|
|
|
|
}
|
|
|
|
sftp->statvfs_state = libssh2_NB_state_sent;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = sftp_packet_require(sftp, SSH_FXP_EXTENDED_REPLY,
|
|
|
|
sftp->statvfs_request_id, &data, &data_len);
|
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN) {
|
|
|
|
return rc;
|
|
|
|
} else if (rc) {
|
|
|
|
sftp->statvfs_state = libssh2_NB_state_idle;
|
2012-03-12 22:52:49 +01:00
|
|
|
return _libssh2_error(session, rc,
|
|
|
|
"Error waiting for FXP EXTENDED REPLY");
|
2010-05-22 01:48:04 -07:00
|
|
|
} else if (data_len < 93) {
|
|
|
|
LIBSSH2_FREE(session, data);
|
|
|
|
sftp->fstatvfs_state = libssh2_NB_state_idle;
|
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
|
|
|
|
"SFTP Protocol Error: short response");
|
|
|
|
}
|
|
|
|
|
|
|
|
sftp->statvfs_state = libssh2_NB_state_idle;
|
|
|
|
|
|
|
|
st->f_bsize = _libssh2_ntohu64(data + 5);
|
|
|
|
st->f_frsize = _libssh2_ntohu64(data + 13);
|
|
|
|
st->f_blocks = _libssh2_ntohu64(data + 21);
|
|
|
|
st->f_bfree = _libssh2_ntohu64(data + 29);
|
|
|
|
st->f_bavail = _libssh2_ntohu64(data + 37);
|
|
|
|
st->f_files = _libssh2_ntohu64(data + 45);
|
|
|
|
st->f_ffree = _libssh2_ntohu64(data + 53);
|
|
|
|
st->f_favail = _libssh2_ntohu64(data + 61);
|
|
|
|
st->f_fsid = _libssh2_ntohu64(data + 69);
|
|
|
|
flag = _libssh2_ntohu64(data + 77);
|
|
|
|
st->f_namemax = _libssh2_ntohu64(data + 85);
|
|
|
|
|
|
|
|
st->f_flag = (flag & SSH_FXE_STATVFS_ST_RDONLY)
|
|
|
|
? LIBSSH2_SFTP_ST_RDONLY : 0;
|
|
|
|
st->f_flag |= (flag & SSH_FXE_STATVFS_ST_NOSUID)
|
|
|
|
? LIBSSH2_SFTP_ST_NOSUID : 0;
|
|
|
|
|
|
|
|
LIBSSH2_FREE(session, data);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* libssh2_sftp_statvfs_ex
|
|
|
|
* Get filesystem space and inode utilization (requires statvfs@openssh.com
|
|
|
|
* support on the server)
|
|
|
|
*/
|
|
|
|
LIBSSH2_API int
|
2010-05-28 17:21:39 +02:00
|
|
|
libssh2_sftp_statvfs(LIBSSH2_SFTP *sftp, const char *path,
|
|
|
|
size_t path_len, LIBSSH2_SFTP_STATVFS *st)
|
2010-05-22 01:48:04 -07:00
|
|
|
{
|
|
|
|
int rc;
|
2010-06-11 13:05:55 +02:00
|
|
|
if(!sftp || !st)
|
|
|
|
return LIBSSH2_ERROR_BAD_USE;
|
2010-05-22 01:48:04 -07:00
|
|
|
BLOCK_ADJUST(rc, sftp->channel->session, sftp_statvfs(sftp, path, path_len,
|
2010-05-28 17:21:39 +02:00
|
|
|
st));
|
2010-05-22 01:48:04 -07:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-26 15:41:14 +00:00
|
|
|
/*
|
|
|
|
* sftp_mkdir
|
|
|
|
*
|
|
|
|
* Create an SFTP directory
|
|
|
|
*/
|
|
|
|
static int sftp_mkdir(LIBSSH2_SFTP *sftp, const char *path,
|
2007-08-06 20:48:04 +00:00
|
|
|
unsigned int path_len, long mode)
|
2004-12-22 00:20:02 +00:00
|
|
|
{
|
2007-04-22 19:51:24 +00:00
|
|
|
LIBSSH2_CHANNEL *channel = sftp->channel;
|
|
|
|
LIBSSH2_SESSION *session = channel->session;
|
2007-05-28 17:56:08 +00:00
|
|
|
LIBSSH2_SFTP_ATTRIBUTES attrs = {
|
|
|
|
LIBSSH2_SFTP_ATTR_PERMISSIONS, 0, 0, 0, 0, 0, 0
|
|
|
|
};
|
2010-04-15 20:36:31 +02:00
|
|
|
size_t data_len;
|
|
|
|
int retcode;
|
2007-06-07 16:01:12 +00:00
|
|
|
/* 13 = packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */
|
2010-04-28 00:23:09 +02:00
|
|
|
ssize_t packet_len = path_len + 13 +
|
|
|
|
sftp_attrsize(LIBSSH2_SFTP_ATTR_PERMISSIONS);
|
2007-04-22 19:51:24 +00:00
|
|
|
unsigned char *packet, *s, *data;
|
|
|
|
int rc;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 19:52:11 +00:00
|
|
|
if (sftp->mkdir_state == libssh2_NB_state_idle) {
|
2009-12-08 08:52:03 +01:00
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP,
|
2007-08-06 20:48:04 +00:00
|
|
|
"Creating directory %s with mode 0%lo", path, mode);
|
2007-02-02 16:21:20 +00:00
|
|
|
s = packet = LIBSSH2_ALLOC(session, packet_len);
|
|
|
|
if (!packet) {
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
|
|
|
"Unable to allocate memory for FXP_MKDIR "
|
|
|
|
"packet");
|
2007-02-02 16:21:20 +00:00
|
|
|
}
|
|
|
|
/* Filetype in SFTP 3 and earlier */
|
|
|
|
attrs.permissions = mode | LIBSSH2_SFTP_ATTR_PFILETYPE_DIR;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-04-17 13:18:15 +02:00
|
|
|
_libssh2_store_u32(&s, packet_len - 4);
|
2007-02-02 16:21:20 +00:00
|
|
|
*(s++) = SSH_FXP_MKDIR;
|
2009-03-13 22:15:27 +00:00
|
|
|
sftp->mkdir_request_id = sftp->request_id++;
|
2010-04-17 13:18:15 +02:00
|
|
|
_libssh2_store_u32(&s, sftp->mkdir_request_id);
|
|
|
|
_libssh2_store_str(&s, path, path_len);
|
|
|
|
|
2009-03-27 21:52:49 +00:00
|
|
|
s += sftp_attr2bin(s, &attrs);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 19:52:11 +00:00
|
|
|
sftp->mkdir_state = libssh2_NB_state_created;
|
2009-03-23 13:35:20 +00:00
|
|
|
}
|
|
|
|
else {
|
2007-04-22 19:51:24 +00:00
|
|
|
packet = sftp->mkdir_packet;
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
if (sftp->mkdir_state == libssh2_NB_state_created) {
|
2010-10-23 00:11:59 +02:00
|
|
|
rc = _libssh2_channel_write(channel, 0, packet, packet_len);
|
2010-04-17 13:18:15 +02:00
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN) {
|
2007-06-07 16:01:12 +00:00
|
|
|
sftp->mkdir_packet = packet;
|
2009-08-25 00:54:47 +02:00
|
|
|
return rc;
|
2007-06-07 16:01:12 +00:00
|
|
|
}
|
|
|
|
if (packet_len != rc) {
|
|
|
|
LIBSSH2_FREE(session, packet);
|
|
|
|
sftp->mkdir_state = libssh2_NB_state_idle;
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
|
|
|
|
"_libssh2_channel_write() failed");
|
2007-02-02 16:21:20 +00:00
|
|
|
}
|
2007-04-22 19:51:24 +00:00
|
|
|
LIBSSH2_FREE(session, packet);
|
2007-06-06 19:52:11 +00:00
|
|
|
sftp->mkdir_state = libssh2_NB_state_sent;
|
2007-04-22 19:51:24 +00:00
|
|
|
sftp->mkdir_packet = NULL;
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-13 22:15:27 +00:00
|
|
|
rc = sftp_packet_require(sftp, SSH_FXP_STATUS, sftp->mkdir_request_id,
|
|
|
|
&data, &data_len);
|
2010-04-17 13:18:15 +02:00
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN) {
|
2009-08-25 00:54:47 +02:00
|
|
|
return rc;
|
2007-08-06 20:48:04 +00:00
|
|
|
} else if (rc) {
|
2007-06-06 19:52:11 +00:00
|
|
|
sftp->mkdir_state = libssh2_NB_state_idle;
|
2012-03-12 22:52:49 +01:00
|
|
|
return _libssh2_error(session, rc,
|
|
|
|
"Error waiting for FXP STATUS");
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-06 19:52:11 +00:00
|
|
|
sftp->mkdir_state = libssh2_NB_state_idle;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-17 13:48:35 +00:00
|
|
|
retcode = _libssh2_ntohu32(data + 5);
|
2007-04-22 19:51:24 +00:00
|
|
|
LIBSSH2_FREE(session, data);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-04-22 19:51:24 +00:00
|
|
|
if (retcode == LIBSSH2_FX_OK) {
|
2009-12-08 08:52:03 +01:00
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP, "OK!");
|
2007-04-22 19:51:24 +00:00
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
sftp->last_errno = retcode;
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
|
|
|
|
"SFTP Protocol Error");
|
2007-04-22 19:51:24 +00:00
|
|
|
}
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-26 15:41:14 +00:00
|
|
|
/*
|
|
|
|
* libssh2_sftp_mkdir_ex
|
|
|
|
*
|
|
|
|
* Create an SFTP directory
|
2004-12-22 00:20:02 +00:00
|
|
|
*/
|
2007-08-06 20:48:04 +00:00
|
|
|
LIBSSH2_API int
|
2009-03-26 15:41:14 +00:00
|
|
|
libssh2_sftp_mkdir_ex(LIBSSH2_SFTP *sftp, const char *path,
|
|
|
|
unsigned int path_len, long mode)
|
|
|
|
{
|
|
|
|
int rc;
|
2010-06-11 13:05:55 +02:00
|
|
|
if(!sftp)
|
|
|
|
return LIBSSH2_ERROR_BAD_USE;
|
2009-03-26 15:41:14 +00:00
|
|
|
BLOCK_ADJUST(rc, sftp->channel->session,
|
|
|
|
sftp_mkdir(sftp, path, path_len, mode));
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sftp_rmdir
|
|
|
|
* Remove a directory
|
|
|
|
*/
|
|
|
|
static int sftp_rmdir(LIBSSH2_SFTP *sftp, const char *path,
|
2007-08-06 20:48:04 +00:00
|
|
|
unsigned int path_len)
|
2004-12-22 00:20:02 +00:00
|
|
|
{
|
2007-05-28 17:56:08 +00:00
|
|
|
LIBSSH2_CHANNEL *channel = sftp->channel;
|
|
|
|
LIBSSH2_SESSION *session = channel->session;
|
2010-04-15 20:36:31 +02:00
|
|
|
size_t data_len;
|
|
|
|
int retcode;
|
2007-06-07 16:01:12 +00:00
|
|
|
/* 13 = packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */
|
|
|
|
ssize_t packet_len = path_len + 13;
|
|
|
|
unsigned char *s, *data;
|
2007-05-28 17:56:08 +00:00
|
|
|
int rc;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
if (sftp->rmdir_state == libssh2_NB_state_idle) {
|
2009-12-08 08:52:03 +01:00
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Removing directory: %s",
|
2007-08-06 20:48:04 +00:00
|
|
|
path);
|
2007-06-07 16:01:12 +00:00
|
|
|
s = sftp->rmdir_packet = LIBSSH2_ALLOC(session, packet_len);
|
|
|
|
if (!sftp->rmdir_packet) {
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
|
|
|
"Unable to allocate memory for FXP_RMDIR "
|
|
|
|
"packet");
|
2007-06-07 16:01:12 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-04-17 13:18:15 +02:00
|
|
|
_libssh2_store_u32(&s, packet_len - 4);
|
2007-06-07 16:01:12 +00:00
|
|
|
*(s++) = SSH_FXP_RMDIR;
|
|
|
|
sftp->rmdir_request_id = sftp->request_id++;
|
2010-04-17 13:18:15 +02:00
|
|
|
_libssh2_store_u32(&s, sftp->rmdir_request_id);
|
|
|
|
_libssh2_store_str(&s, path, path_len);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
sftp->rmdir_state = libssh2_NB_state_created;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
if (sftp->rmdir_state == libssh2_NB_state_created) {
|
2010-10-23 00:11:59 +02:00
|
|
|
rc = _libssh2_channel_write(channel, 0, sftp->rmdir_packet,
|
2009-03-29 22:09:29 +00:00
|
|
|
packet_len);
|
2010-04-17 13:18:15 +02:00
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN) {
|
2009-08-25 00:54:47 +02:00
|
|
|
return rc;
|
2007-08-06 20:48:04 +00:00
|
|
|
} else if (packet_len != rc) {
|
2007-06-07 16:01:12 +00:00
|
|
|
LIBSSH2_FREE(session, sftp->rmdir_packet);
|
|
|
|
sftp->rmdir_packet = NULL;
|
|
|
|
sftp->rmdir_state = libssh2_NB_state_idle;
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
|
|
|
|
"Unable to send FXP_RMDIR command");
|
2007-06-07 16:01:12 +00:00
|
|
|
}
|
|
|
|
LIBSSH2_FREE(session, sftp->rmdir_packet);
|
|
|
|
sftp->rmdir_packet = NULL;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
sftp->rmdir_state = libssh2_NB_state_sent;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2008-12-16 15:35:33 +00:00
|
|
|
rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
|
|
|
|
sftp->rmdir_request_id, &data, &data_len);
|
2010-04-17 13:18:15 +02:00
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN) {
|
2009-08-25 00:54:47 +02:00
|
|
|
return rc;
|
2007-08-06 20:48:04 +00:00
|
|
|
} else if (rc) {
|
2007-06-07 16:01:12 +00:00
|
|
|
sftp->rmdir_state = libssh2_NB_state_idle;
|
2012-03-12 22:52:49 +01:00
|
|
|
return _libssh2_error(session, rc,
|
|
|
|
"Error waiting for FXP STATUS");
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-06-07 16:01:12 +00:00
|
|
|
|
|
|
|
sftp->rmdir_state = libssh2_NB_state_idle;
|
|
|
|
|
2009-03-17 13:48:35 +00:00
|
|
|
retcode = _libssh2_ntohu32(data + 5);
|
2007-05-28 17:56:08 +00:00
|
|
|
LIBSSH2_FREE(session, data);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
if (retcode == LIBSSH2_FX_OK) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
sftp->last_errno = retcode;
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
|
|
|
|
"SFTP Protocol Error");
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2004-12-22 00:20:02 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-26 15:41:14 +00:00
|
|
|
/* libssh2_sftp_rmdir_ex
|
|
|
|
* Remove a directory
|
2004-12-22 00:20:02 +00:00
|
|
|
*/
|
2007-08-06 20:48:04 +00:00
|
|
|
LIBSSH2_API int
|
2009-03-26 15:41:14 +00:00
|
|
|
libssh2_sftp_rmdir_ex(LIBSSH2_SFTP *sftp, const char *path,
|
|
|
|
unsigned int path_len)
|
|
|
|
{
|
|
|
|
int rc;
|
2010-06-11 13:05:55 +02:00
|
|
|
if(!sftp)
|
|
|
|
return LIBSSH2_ERROR_BAD_USE;
|
2009-03-26 15:41:14 +00:00
|
|
|
BLOCK_ADJUST(rc, sftp->channel->session,
|
|
|
|
sftp_rmdir(sftp, path, path_len));
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sftp_stat
|
|
|
|
* Stat a file or symbolic link
|
|
|
|
*/
|
|
|
|
static int sftp_stat(LIBSSH2_SFTP *sftp, const char *path,
|
2007-08-06 20:48:04 +00:00
|
|
|
unsigned int path_len, int stat_type,
|
|
|
|
LIBSSH2_SFTP_ATTRIBUTES * attrs)
|
2004-12-22 00:20:02 +00:00
|
|
|
{
|
2007-05-28 17:56:08 +00:00
|
|
|
LIBSSH2_CHANNEL *channel = sftp->channel;
|
|
|
|
LIBSSH2_SESSION *session = channel->session;
|
2010-04-15 20:36:31 +02:00
|
|
|
size_t data_len;
|
2007-06-07 16:01:12 +00:00
|
|
|
/* 13 = packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */
|
2007-08-06 20:48:04 +00:00
|
|
|
ssize_t packet_len =
|
|
|
|
path_len + 13 +
|
|
|
|
((stat_type ==
|
2010-04-28 00:23:09 +02:00
|
|
|
LIBSSH2_SFTP_SETSTAT) ? sftp_attrsize(attrs->flags) : 0);
|
2007-06-07 16:01:12 +00:00
|
|
|
unsigned char *s, *data;
|
2007-08-06 20:48:04 +00:00
|
|
|
static const unsigned char stat_responses[2] =
|
|
|
|
{ SSH_FXP_ATTRS, SSH_FXP_STATUS };
|
2007-05-28 17:56:08 +00:00
|
|
|
int rc;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
if (sftp->stat_state == libssh2_NB_state_idle) {
|
2009-12-08 08:52:03 +01:00
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP, "%s %s",
|
2007-06-07 16:01:12 +00:00
|
|
|
(stat_type == LIBSSH2_SFTP_SETSTAT) ? "Set-statting" :
|
2007-08-06 20:48:04 +00:00
|
|
|
(stat_type ==
|
|
|
|
LIBSSH2_SFTP_LSTAT ? "LStatting" : "Statting"), path);
|
2007-06-07 16:01:12 +00:00
|
|
|
s = sftp->stat_packet = LIBSSH2_ALLOC(session, packet_len);
|
|
|
|
if (!sftp->stat_packet) {
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
|
|
|
"Unable to allocate memory for FXP_*STAT "
|
|
|
|
"packet");
|
2007-06-07 16:01:12 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2010-04-17 13:18:15 +02:00
|
|
|
_libssh2_store_u32(&s, packet_len - 4);
|
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
switch (stat_type) {
|
2007-08-06 20:48:04 +00:00
|
|
|
case LIBSSH2_SFTP_SETSTAT:
|
|
|
|
*(s++) = SSH_FXP_SETSTAT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LIBSSH2_SFTP_LSTAT:
|
|
|
|
*(s++) = SSH_FXP_LSTAT;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LIBSSH2_SFTP_STAT:
|
|
|
|
default:
|
|
|
|
*(s++) = SSH_FXP_STAT;
|
2007-06-07 16:01:12 +00:00
|
|
|
}
|
|
|
|
sftp->stat_request_id = sftp->request_id++;
|
2010-04-17 13:18:15 +02:00
|
|
|
_libssh2_store_u32(&s, sftp->stat_request_id);
|
|
|
|
_libssh2_store_str(&s, path, path_len);
|
|
|
|
|
|
|
|
if (stat_type == LIBSSH2_SFTP_SETSTAT)
|
2009-03-23 13:35:20 +00:00
|
|
|
s += sftp_attr2bin(s, attrs);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
sftp->stat_state = libssh2_NB_state_created;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
if (sftp->stat_state == libssh2_NB_state_created) {
|
2010-10-23 00:11:59 +02:00
|
|
|
rc = _libssh2_channel_write(channel, 0, sftp->stat_packet, packet_len);
|
2010-04-17 13:18:15 +02:00
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN) {
|
2009-08-25 00:54:47 +02:00
|
|
|
return rc;
|
2007-08-06 20:48:04 +00:00
|
|
|
} else if (packet_len != rc) {
|
2007-06-07 16:01:12 +00:00
|
|
|
LIBSSH2_FREE(session, sftp->stat_packet);
|
|
|
|
sftp->stat_packet = NULL;
|
|
|
|
sftp->stat_state = libssh2_NB_state_idle;
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
|
|
|
|
"Unable to send STAT/LSTAT/SETSTAT command");
|
2007-06-07 16:01:12 +00:00
|
|
|
}
|
|
|
|
LIBSSH2_FREE(session, sftp->stat_packet);
|
|
|
|
sftp->stat_packet = NULL;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
sftp->stat_state = libssh2_NB_state_sent;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2008-12-15 22:58:07 +00:00
|
|
|
rc = sftp_packet_requirev(sftp, 2, stat_responses,
|
|
|
|
sftp->stat_request_id, &data, &data_len);
|
2012-03-12 22:52:49 +01:00
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN)
|
2009-08-25 00:54:47 +02:00
|
|
|
return rc;
|
2012-03-12 22:52:49 +01:00
|
|
|
else if (rc) {
|
2007-06-07 16:01:12 +00:00
|
|
|
sftp->stat_state = libssh2_NB_state_idle;
|
2012-03-12 22:52:49 +01:00
|
|
|
return _libssh2_error(session, rc,
|
2010-04-16 00:18:51 +02:00
|
|
|
"Timeout waiting for status message");
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
sftp->stat_state = libssh2_NB_state_idle;
|
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
if (data[0] == SSH_FXP_STATUS) {
|
|
|
|
int retcode;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-17 13:48:35 +00:00
|
|
|
retcode = _libssh2_ntohu32(data + 5);
|
2007-02-02 16:21:20 +00:00
|
|
|
LIBSSH2_FREE(session, data);
|
2007-05-28 17:56:08 +00:00
|
|
|
if (retcode == LIBSSH2_FX_OK) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
sftp->last_errno = retcode;
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
|
|
|
|
"SFTP Protocol Error");
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
|
2009-03-23 13:35:20 +00:00
|
|
|
sftp_bin2attr(attrs, data + 5);
|
2007-05-28 17:56:08 +00:00
|
|
|
LIBSSH2_FREE(session, data);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
return 0;
|
2004-12-22 00:20:02 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-26 15:41:14 +00:00
|
|
|
/* libssh2_sftp_stat_ex
|
|
|
|
* Stat a file or symbolic link
|
2004-12-22 00:20:02 +00:00
|
|
|
*/
|
2007-08-06 20:48:04 +00:00
|
|
|
LIBSSH2_API int
|
2009-03-26 15:41:14 +00:00
|
|
|
libssh2_sftp_stat_ex(LIBSSH2_SFTP *sftp, const char *path,
|
|
|
|
unsigned int path_len, int stat_type,
|
|
|
|
LIBSSH2_SFTP_ATTRIBUTES *attrs)
|
|
|
|
{
|
|
|
|
int rc;
|
2010-06-11 13:05:55 +02:00
|
|
|
if(!sftp)
|
|
|
|
return LIBSSH2_ERROR_BAD_USE;
|
2009-03-26 15:41:14 +00:00
|
|
|
BLOCK_ADJUST(rc, sftp->channel->session,
|
|
|
|
sftp_stat(sftp, path, path_len, stat_type, attrs));
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* sftp_symlink
|
|
|
|
* Read or set a symlink
|
|
|
|
*/
|
|
|
|
static int sftp_symlink(LIBSSH2_SFTP *sftp, const char *path,
|
2007-08-06 20:48:04 +00:00
|
|
|
unsigned int path_len, char *target,
|
|
|
|
unsigned int target_len, int link_type)
|
2004-12-22 00:20:02 +00:00
|
|
|
{
|
2007-05-28 17:56:08 +00:00
|
|
|
LIBSSH2_CHANNEL *channel = sftp->channel;
|
|
|
|
LIBSSH2_SESSION *session = channel->session;
|
2010-04-15 20:36:31 +02:00
|
|
|
size_t data_len, link_len;
|
2007-06-07 16:01:12 +00:00
|
|
|
/* 13 = packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */
|
2007-08-06 20:48:04 +00:00
|
|
|
ssize_t packet_len =
|
|
|
|
path_len + 13 +
|
|
|
|
((link_type == LIBSSH2_SFTP_SYMLINK) ? (4 + target_len) : 0);
|
2007-06-07 16:01:12 +00:00
|
|
|
unsigned char *s, *data;
|
2007-08-06 20:48:04 +00:00
|
|
|
static const unsigned char link_responses[2] =
|
|
|
|
{ SSH_FXP_NAME, SSH_FXP_STATUS };
|
2011-01-04 14:15:57 +01:00
|
|
|
int retcode;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
|
|
|
if ((sftp->version < 3) && (link_type != LIBSSH2_SFTP_REALPATH)) {
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
|
|
|
|
"Server does not support SYMLINK or READLINK");
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
if (sftp->symlink_state == libssh2_NB_state_idle) {
|
|
|
|
s = sftp->symlink_packet = LIBSSH2_ALLOC(session, packet_len);
|
|
|
|
if (!sftp->symlink_packet) {
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
|
|
|
|
"Unable to allocate memory for "
|
|
|
|
"SYMLINK/READLINK/REALPATH packet");
|
2007-06-07 16:01:12 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-12-08 08:52:03 +01:00
|
|
|
_libssh2_debug(session, LIBSSH2_TRACE_SFTP, "%s %s on %s",
|
2007-08-06 20:48:04 +00:00
|
|
|
(link_type ==
|
|
|
|
LIBSSH2_SFTP_SYMLINK) ? "Creating" : "Reading",
|
|
|
|
(link_type ==
|
|
|
|
LIBSSH2_SFTP_REALPATH) ? "realpath" : "symlink", path);
|
|
|
|
|
2010-04-17 13:18:15 +02:00
|
|
|
_libssh2_store_u32(&s, packet_len - 4);
|
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
switch (link_type) {
|
2007-08-06 20:48:04 +00:00
|
|
|
case LIBSSH2_SFTP_REALPATH:
|
|
|
|
*(s++) = SSH_FXP_REALPATH;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LIBSSH2_SFTP_SYMLINK:
|
|
|
|
*(s++) = SSH_FXP_SYMLINK;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LIBSSH2_SFTP_READLINK:
|
|
|
|
default:
|
|
|
|
*(s++) = SSH_FXP_READLINK;
|
2007-06-07 16:01:12 +00:00
|
|
|
}
|
|
|
|
sftp->symlink_request_id = sftp->request_id++;
|
2010-04-17 13:18:15 +02:00
|
|
|
_libssh2_store_u32(&s, sftp->symlink_request_id);
|
|
|
|
_libssh2_store_str(&s, path, path_len);
|
|
|
|
|
|
|
|
if (link_type == LIBSSH2_SFTP_SYMLINK)
|
|
|
|
_libssh2_store_str(&s, target, target_len);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
sftp->symlink_state = libssh2_NB_state_created;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
if (sftp->symlink_state == libssh2_NB_state_created) {
|
2011-01-04 14:15:57 +01:00
|
|
|
ssize_t rc = _libssh2_channel_write(channel, 0, sftp->symlink_packet,
|
|
|
|
packet_len);
|
|
|
|
if (rc == LIBSSH2_ERROR_EAGAIN)
|
2009-08-25 00:54:47 +02:00
|
|
|
return rc;
|
2011-01-04 14:15:57 +01:00
|
|
|
else if (packet_len != rc) {
|
2007-06-07 16:01:12 +00:00
|
|
|
LIBSSH2_FREE(session, sftp->symlink_packet);
|
|
|
|
sftp->symlink_packet = NULL;
|
|
|
|
sftp->symlink_state = libssh2_NB_state_idle;
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
|
|
|
|
"Unable to send SYMLINK/READLINK command");
|
2007-06-07 16:01:12 +00:00
|
|
|
}
|
|
|
|
LIBSSH2_FREE(session, sftp->symlink_packet);
|
|
|
|
sftp->symlink_packet = NULL;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
sftp->symlink_state = libssh2_NB_state_sent;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2011-01-04 14:15:57 +01:00
|
|
|
retcode = sftp_packet_requirev(sftp, 2, link_responses,
|
|
|
|
sftp->symlink_request_id, &data,
|
|
|
|
&data_len);
|
|
|
|
if (retcode == LIBSSH2_ERROR_EAGAIN)
|
|
|
|
return retcode;
|
|
|
|
else if (retcode) {
|
2007-06-07 16:01:12 +00:00
|
|
|
sftp->symlink_state = libssh2_NB_state_idle;
|
2012-03-12 22:52:49 +01:00
|
|
|
return _libssh2_error(session, retcode,
|
|
|
|
"Error waiting for status message");
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2007-06-07 16:01:12 +00:00
|
|
|
sftp->symlink_state = libssh2_NB_state_idle;
|
|
|
|
|
2007-05-28 17:56:08 +00:00
|
|
|
if (data[0] == SSH_FXP_STATUS) {
|
|
|
|
int retcode;
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-17 13:48:35 +00:00
|
|
|
retcode = _libssh2_ntohu32(data + 5);
|
2007-05-28 17:56:08 +00:00
|
|
|
LIBSSH2_FREE(session, data);
|
2011-01-04 14:15:57 +01:00
|
|
|
if (retcode == LIBSSH2_FX_OK)
|
|
|
|
return LIBSSH2_ERROR_NONE;
|
|
|
|
else {
|
2007-05-28 17:56:08 +00:00
|
|
|
sftp->last_errno = retcode;
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
|
|
|
|
"SFTP Protocol Error");
|
2007-02-02 16:21:20 +00:00
|
|
|
}
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-17 13:48:35 +00:00
|
|
|
if (_libssh2_ntohu32(data + 5) < 1) {
|
2007-02-02 16:21:20 +00:00
|
|
|
LIBSSH2_FREE(session, data);
|
2010-04-16 00:18:51 +02:00
|
|
|
return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
|
|
|
|
"Invalid READLINK/REALPATH response, "
|
|
|
|
"no name entries");
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2011-01-04 14:15:57 +01:00
|
|
|
/* this reads a u32 and stores it into a signed 32bit value */
|
2009-03-17 13:48:35 +00:00
|
|
|
link_len = _libssh2_ntohu32(data + 9);
|
2011-01-04 14:15:57 +01:00
|
|
|
if (link_len < target_len) {
|
|
|
|
memcpy(target, data + 13, link_len);
|
|
|
|
target[link_len] = 0;
|
|
|
|
retcode = (int)link_len;
|
2007-05-28 17:56:08 +00:00
|
|
|
}
|
2011-01-04 14:15:57 +01:00
|
|
|
else
|
|
|
|
retcode = LIBSSH2_ERROR_BUFFER_TOO_SMALL;
|
2007-05-28 17:56:08 +00:00
|
|
|
LIBSSH2_FREE(session, data);
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2011-01-04 14:15:57 +01:00
|
|
|
return retcode;
|
2004-12-22 00:20:02 +00:00
|
|
|
}
|
2007-08-06 20:48:04 +00:00
|
|
|
|
2009-03-26 15:41:14 +00:00
|
|
|
/* libssh2_sftp_symlink_ex
|
|
|
|
* Read or set a symlink
|
|
|
|
*/
|
|
|
|
LIBSSH2_API int
|
|
|
|
libssh2_sftp_symlink_ex(LIBSSH2_SFTP *sftp, const char *path,
|
|
|
|
unsigned int path_len, char *target,
|
|
|
|
unsigned int target_len, int link_type)
|
|
|
|
{
|
|
|
|
int rc;
|
2010-06-11 13:05:55 +02:00
|
|
|
if(!sftp)
|
|
|
|
return LIBSSH2_ERROR_BAD_USE;
|
2009-03-26 15:41:14 +00:00
|
|
|
BLOCK_ADJUST(rc, sftp->channel->session,
|
|
|
|
sftp_symlink(sftp, path, path_len, target, target_len,
|
|
|
|
link_type));
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2009-03-23 13:35:20 +00:00
|
|
|
/* libssh2_sftp_last_error
|
2004-12-22 21:55:08 +00:00
|
|
|
* Returns the last error code reported by SFTP
|
|
|
|
*/
|
2007-08-06 20:48:04 +00:00
|
|
|
LIBSSH2_API unsigned long
|
2009-03-26 15:41:14 +00:00
|
|
|
libssh2_sftp_last_error(LIBSSH2_SFTP *sftp)
|
2004-12-22 21:55:08 +00:00
|
|
|
{
|
2010-06-11 13:05:55 +02:00
|
|
|
if(!sftp)
|
|
|
|
return 0;
|
|
|
|
|
2007-04-21 18:16:23 +00:00
|
|
|
return sftp->last_errno;
|
|
|
|
}
|
2011-09-08 08:50:24 +02:00
|
|
|
|
|
|
|
/* libssh2_sftp_get_channel
|
|
|
|
* Return the channel of sftp, then caller can control the channel's behavior.
|
|
|
|
*/
|
|
|
|
LIBSSH2_API LIBSSH2_CHANNEL *
|
|
|
|
libssh2_sftp_get_channel(LIBSSH2_SFTP *sftp)
|
|
|
|
{
|
|
|
|
if (!sftp)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return sftp->channel;
|
|
|
|
}
|