|
- /* Copyright (c) 2004-2008, Sara Golemon <sarag@libssh2.org>
- * Copyright (c) 2007 Eli Fant <elifantu@mail.ru>
- * Copyright (c) 2009-2019 by Daniel Stenberg
- * 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.
- */
-
- #include <assert.h>
-
- #include "libssh2_priv.h"
- #include "libssh2_sftp.h"
- #include "channel.h"
- #include "session.h"
- #include "sftp.h"
-
- /* 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 */
- #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
- #define SSH_FXP_EXTENDED_REPLY 201
-
- /* S_IFREG */
- #define LIBSSH2_SFTP_ATTR_PFILETYPE_FILE 0100000
- /* S_IFDIR */
- #define LIBSSH2_SFTP_ATTR_PFILETYPE_DIR 0040000
-
- #define SSH_FXE_STATVFS_ST_RDONLY 0x00000001
- #define SSH_FXE_STATVFS_ST_NOSUID 0x00000002
-
- /* This is the maximum packet length to accept, as larger than this indicate
- some kind of server problem. */
- #define LIBSSH2_SFTP_PACKET_MAXLEN (256 * 1024)
-
- static int sftp_packet_ask(LIBSSH2_SFTP *sftp, unsigned char packet_type,
- uint32_t request_id, unsigned char **data,
- size_t *data_len);
- static void sftp_packet_flush(LIBSSH2_SFTP *sftp);
-
- /* sftp_attrsize
- * Size that attr with this flagset will occupy when turned into a bin struct
- */
- static int sftp_attrsize(unsigned long flags)
- {
- return (4 + /* flags(4) */
- ((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));
- /* atime + mtime as u32 */
- }
-
- /* _libssh2_store_u64
- */
- static void _libssh2_store_u64(unsigned char **ptr, libssh2_uint64_t value)
- {
- uint32_t msl = (uint32_t)(value >> 32);
- unsigned char *buf = *ptr;
-
- 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);
-
- 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);
-
- *ptr += 8;
- }
-
- /*
- * Search list of zombied FXP_READ request IDs.
- *
- * Returns NULL if ID not in list.
- */
- static struct sftp_zombie_requests *
- find_zombie_request(LIBSSH2_SFTP *sftp, uint32_t request_id)
- {
- struct sftp_zombie_requests *zombie =
- _libssh2_list_first(&sftp->zombie_requests);
-
- while(zombie) {
- if(zombie->request_id == request_id)
- break;
- else
- zombie = _libssh2_list_next(&zombie->node);
- }
-
- return zombie;
- }
-
- static void
- remove_zombie_request(LIBSSH2_SFTP *sftp, uint32_t request_id)
- {
- LIBSSH2_SESSION *session = sftp->channel->session;
-
- struct sftp_zombie_requests *zombie = find_zombie_request(sftp,
- request_id);
- if(zombie) {
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
- "Removing request ID %ld from the list of "
- "zombie requests",
- request_id);
-
- _libssh2_list_remove(&zombie->node);
- LIBSSH2_FREE(session, zombie);
- }
- }
-
- static int
- add_zombie_request(LIBSSH2_SFTP *sftp, uint32_t request_id)
- {
- LIBSSH2_SESSION *session = sftp->channel->session;
-
- struct sftp_zombie_requests *zombie;
-
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
- "Marking request ID %ld as a zombie request", request_id);
-
- zombie = LIBSSH2_ALLOC(sftp->channel->session,
- sizeof(struct sftp_zombie_requests));
- if(!zombie)
- return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
- "malloc fail for zombie request ID");
- else {
- zombie->request_id = request_id;
- _libssh2_list_add(&sftp->zombie_requests, &zombie->node);
- return LIBSSH2_ERROR_NONE;
- }
- }
-
- /*
- * sftp_packet_add
- *
- * Add a packet to the SFTP packet brigade
- */
- static int
- sftp_packet_add(LIBSSH2_SFTP *sftp, unsigned char *data,
- size_t data_len)
- {
- LIBSSH2_SESSION *session = sftp->channel->session;
- LIBSSH2_SFTP_PACKET *packet;
- uint32_t request_id;
-
- if(data_len < 5) {
- return LIBSSH2_ERROR_OUT_OF_BOUNDARY;
- }
-
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
- "Received packet type %d (len %d)",
- (int) data[0], data_len);
-
- /*
- * 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");
- }
-
- request_id = _libssh2_ntohu32(&data[1]);
-
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Received packet id %d",
- request_id);
-
- /* Don't add the packet if it answers a request we've given up on. */
- if((data[0] == SSH_FXP_STATUS || data[0] == SSH_FXP_DATA)
- && find_zombie_request(sftp, request_id)) {
-
- /* If we get here, the file ended before the response arrived. We
- are no longer interested in the request so we discard it */
-
- LIBSSH2_FREE(session, data);
-
- remove_zombie_request(sftp, request_id);
- return LIBSSH2_ERROR_NONE;
- }
-
- packet = LIBSSH2_ALLOC(session, sizeof(LIBSSH2_SFTP_PACKET));
- if(!packet) {
- return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
- "Unable to allocate datablock for SFTP packet");
- }
-
- packet->data = data;
- packet->data_len = data_len;
- packet->request_id = request_id;
-
- _libssh2_list_add(&sftp->packets, &packet->node);
-
- return LIBSSH2_ERROR_NONE;
- }
-
- /*
- * sftp_packet_read
- *
- * Frame an SFTP packet off the channel
- */
- static int
- sftp_packet_read(LIBSSH2_SFTP *sftp)
- {
- LIBSSH2_CHANNEL *channel = sftp->channel;
- LIBSSH2_SESSION *session = channel->session;
- unsigned char *packet = NULL;
- ssize_t rc;
- unsigned long recv_window;
- int packet_type;
-
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "recv packet");
-
- switch(sftp->packet_state) {
- case libssh2_NB_state_sent: /* EAGAIN from window adjusting */
- sftp->packet_state = libssh2_NB_state_idle;
-
- packet = sftp->partial_packet;
- goto window_adjust;
-
- case libssh2_NB_state_sent1: /* EAGAIN from channel read */
- sftp->packet_state = libssh2_NB_state_idle;
-
- packet = sftp->partial_packet;
-
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
- "partial read cont, len: %lu", sftp->partial_len);
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
- "partial read cont, already recvd: %lu",
- sftp->partial_received);
- /* fall-through */
- default:
- if(!packet) {
- /* only do this if there's not already a packet buffer allocated
- to use */
-
- /* 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;
-
- sftp->partial_len = _libssh2_ntohu32(sftp->partial_size);
- /* make sure we don't proceed if the packet size is unreasonably
- large */
- if(sftp->partial_len > LIBSSH2_SFTP_PACKET_MAXLEN) {
- libssh2_channel_flush(channel);
- sftp->partial_size_len = 0;
- return _libssh2_error(session,
- LIBSSH2_ERROR_CHANNEL_PACKET_EXCEEDED,
- "SFTP packet too large");
- }
-
- if(sftp->partial_len == 0)
- return _libssh2_error(session,
- LIBSSH2_ERROR_ALLOC,
- "Unable to allocate empty SFTP packet");
-
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
- "Data begin - Packet Length: %lu",
- sftp->partial_len);
- packet = LIBSSH2_ALLOC(session, sftp->partial_len);
- if(!packet)
- return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
- "Unable to allocate SFTP packet");
- sftp->partial_size_len = 0;
- sftp->partial_received = 0; /* how much of the packet already
- received */
- sftp->partial_packet = packet;
-
- window_adjust:
- recv_window = libssh2_channel_window_read_ex(channel, NULL, NULL);
-
- if(sftp->partial_len > recv_window) {
- /* ask for twice the data amount we need at once */
- rc = _libssh2_channel_receive_window_adjust(channel,
- sftp->partial_len
- * 2,
- 1, 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(sftp->partial_len > sftp->partial_received) {
- rc = _libssh2_channel_read(channel, 0,
- (char *)&packet[sftp->partial_received],
- sftp->partial_len -
- sftp->partial_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->packet_state = libssh2_NB_state_sent1;
- return rc;
- }
- else if(rc < 0) {
- LIBSSH2_FREE(session, packet);
- sftp->partial_packet = NULL;
- return _libssh2_error(session, rc,
- "Error waiting for SFTP packet");
- }
- sftp->partial_received += rc;
- }
-
- sftp->partial_packet = NULL;
-
- /* sftp_packet_add takes ownership of the packet and might free it
- so we take a copy of the packet type before we call it. */
- packet_type = packet[0];
- rc = sftp_packet_add(sftp, packet, sftp->partial_len);
- if(rc) {
- LIBSSH2_FREE(session, packet);
- return rc;
- }
- else {
- return packet_type;
- }
- }
- /* WON'T REACH */
- }
- /*
- * 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)
- {
- struct sftp_pipeline_chunk *chunk;
- 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;
- struct sftp_pipeline_chunk *next = _libssh2_list_next(&chunk->node);
-
- 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);
- else if(chunk->sent)
- /* there was no incoming packet for this request, mark this
- request as a zombie if it ever sent the request */
- add_zombie_request(sftp, chunk->request_id);
-
- _libssh2_list_remove(&chunk->node);
- LIBSSH2_FREE(session, chunk);
- chunk = next;
- }
- }
-
-
- /*
- * sftp_packet_ask()
- *
- * Checks if there's a matching SFTP packet available.
- */
- static int
- sftp_packet_ask(LIBSSH2_SFTP *sftp, unsigned char packet_type,
- uint32_t request_id, unsigned char **data,
- size_t *data_len)
- {
- LIBSSH2_SESSION *session = sftp->channel->session;
- LIBSSH2_SFTP_PACKET *packet = _libssh2_list_first(&sftp->packets);
-
- if(!packet)
- return -1;
-
- /* Special consideration when getting VERSION packet */
-
- while(packet) {
- if((packet->data[0] == packet_type) &&
- ((packet_type == SSH_FXP_VERSION) ||
- (packet->request_id == request_id))) {
-
- /* Match! Fetch the data */
- *data = packet->data;
- *data_len = packet->data_len;
-
- /* unlink and free this struct */
- _libssh2_list_remove(&packet->node);
- LIBSSH2_FREE(session, packet);
-
- return 0;
- }
- /* check next struct in the list */
- packet = _libssh2_list_next(&packet->node);
- }
- return -1;
- }
-
- /* sftp_packet_require
- * A la libssh2_packet_require
- */
- static int
- sftp_packet_require(LIBSSH2_SFTP *sftp, unsigned char packet_type,
- uint32_t request_id, unsigned char **data,
- size_t *data_len, size_t required_size)
- {
- LIBSSH2_SESSION *session = sftp->channel->session;
- int rc;
-
- if(data == NULL || data_len == NULL || required_size == 0) {
- return LIBSSH2_ERROR_BAD_USE;
- }
-
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Requiring packet %d id %ld",
- (int) packet_type, request_id);
-
- if(sftp_packet_ask(sftp, packet_type, request_id, data, data_len) == 0) {
- /* The right packet was available in the packet brigade */
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Got %d",
- (int) packet_type);
-
- if (*data_len < required_size) {
- return LIBSSH2_ERROR_BUFFER_TOO_SMALL;
- }
-
- return LIBSSH2_ERROR_NONE;
- }
-
- while(session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
- rc = sftp_packet_read(sftp);
- if(rc < 0)
- return rc;
-
- /* 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 */
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Got %d",
- (int) packet_type);
-
- if (*data_len < required_size) {
- return LIBSSH2_ERROR_BUFFER_TOO_SMALL;
- }
-
- return LIBSSH2_ERROR_NONE;
- }
- }
-
- /* Only reached if the socket died */
- return LIBSSH2_ERROR_SOCKET_DISCONNECT;
- }
-
- /* sftp_packet_requirev
- * Require one of N possible responses
- */
- static int
- sftp_packet_requirev(LIBSSH2_SFTP *sftp, int num_valid_responses,
- const unsigned char *valid_responses,
- uint32_t request_id, unsigned char **data,
- size_t *data_len, size_t required_size)
- {
- int i;
- int rc;
-
- if(data == NULL || data_len == NULL || required_size == 0) {
- return LIBSSH2_ERROR_BAD_USE;
- }
-
- /* If no timeout is active, start a new one */
- if(sftp->requirev_start == 0)
- sftp->requirev_start = time(NULL);
-
- while(sftp->channel->session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
- for(i = 0; i < num_valid_responses; i++) {
- if(sftp_packet_ask(sftp, valid_responses[i], request_id,
- data, data_len) == 0) {
- /*
- * Set to zero before all returns to say
- * the timeout is not active
- */
- sftp->requirev_start = 0;
-
- if (*data_len < required_size) {
- return LIBSSH2_ERROR_BUFFER_TOO_SMALL;
- }
-
- return LIBSSH2_ERROR_NONE;
- }
- }
-
- rc = sftp_packet_read(sftp);
- if((rc < 0) && (rc != LIBSSH2_ERROR_EAGAIN)) {
- sftp->requirev_start = 0;
- return rc;
- }
- else if(rc <= 0) {
- /* prevent busy-looping */
- long left =
- LIBSSH2_READ_TIMEOUT -
- (long)(time(NULL) - sftp->requirev_start);
-
- if(left <= 0) {
- sftp->requirev_start = 0;
- return LIBSSH2_ERROR_TIMEOUT;
- }
- else if(rc == LIBSSH2_ERROR_EAGAIN) {
- return rc;
- }
- }
- }
-
- sftp->requirev_start = 0;
-
- /* Only reached if the socket died */
- return LIBSSH2_ERROR_SOCKET_DISCONNECT;
- }
-
- /* sftp_attr2bin
- * Populate attributes into an SFTP block
- */
- static ssize_t
- sftp_attr2bin(unsigned char *p, const LIBSSH2_SFTP_ATTRIBUTES * attrs)
- {
- unsigned char *s = p;
- uint32_t flag_mask =
- LIBSSH2_SFTP_ATTR_SIZE | LIBSSH2_SFTP_ATTR_UIDGID |
- LIBSSH2_SFTP_ATTR_PERMISSIONS | LIBSSH2_SFTP_ATTR_ACMODTIME;
-
- /* TODO: When we add SFTP4+ functionality flag_mask can get additional
- bits */
-
- if(!attrs) {
- _libssh2_htonu32(s, 0);
- return 4;
- }
-
- _libssh2_store_u32(&s, attrs->flags & flag_mask);
-
- if(attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) {
- _libssh2_store_u64(&s, attrs->filesize);
- }
-
- if(attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) {
- _libssh2_store_u32(&s, attrs->uid);
- _libssh2_store_u32(&s, attrs->gid);
- }
-
- if(attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) {
- _libssh2_store_u32(&s, attrs->permissions);
- }
-
- if(attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) {
- _libssh2_store_u32(&s, attrs->atime);
- _libssh2_store_u32(&s, attrs->mtime);
- }
-
- return (s - p);
- }
-
- /* sftp_bin2attr
- */
- static int
- sftp_bin2attr(LIBSSH2_SFTP_ATTRIBUTES *attrs, const unsigned char *p,
- size_t data_len)
- {
- struct string_buf buf;
- uint32_t flags = 0;
- buf.data = (unsigned char *)p;
- buf.dataptr = buf.data;
- buf.len = data_len;
-
- if(_libssh2_get_u32(&buf, &flags) != 0) {
- return LIBSSH2_ERROR_BUFFER_TOO_SMALL;
- }
- attrs->flags = flags;
-
- if(attrs->flags & LIBSSH2_SFTP_ATTR_SIZE) {
- if(_libssh2_get_u64(&buf, &(attrs->filesize)) != 0) {
- return LIBSSH2_ERROR_BUFFER_TOO_SMALL;
- }
- }
-
- if(attrs->flags & LIBSSH2_SFTP_ATTR_UIDGID) {
- uint32_t uid = 0;
- uint32_t gid = 0;
- if(_libssh2_get_u32(&buf, &uid) != 0 ||
- _libssh2_get_u32(&buf, &gid) != 0) {
- return LIBSSH2_ERROR_BUFFER_TOO_SMALL;
- }
- attrs->uid = uid;
- attrs->gid = gid;
- }
-
- if(attrs->flags & LIBSSH2_SFTP_ATTR_PERMISSIONS) {
- uint32_t permissions;
- if(_libssh2_get_u32(&buf, &permissions) != 0) {
- return LIBSSH2_ERROR_BUFFER_TOO_SMALL;
- }
- attrs->permissions = permissions;
- }
-
- if(attrs->flags & LIBSSH2_SFTP_ATTR_ACMODTIME) {
- uint32_t atime;
- uint32_t mtime;
- if(_libssh2_get_u32(&buf, &atime) != 0 ||
- _libssh2_get_u32(&buf, &mtime) != 0) {
- return LIBSSH2_ERROR_BUFFER_TOO_SMALL;
- }
- attrs->atime = atime;
- attrs->mtime = mtime;
- }
-
- return (buf.dataptr - buf.data);
- }
-
- /* ************
- * SFTP API *
- ************ */
-
- LIBSSH2_CHANNEL_CLOSE_FUNC(libssh2_sftp_dtor);
-
- /* libssh2_sftp_dtor
- * Shutdown an SFTP stream when the channel closes
- */
- LIBSSH2_CHANNEL_CLOSE_FUNC(libssh2_sftp_dtor)
- {
- LIBSSH2_SFTP *sftp = (LIBSSH2_SFTP *) (*channel_abstract);
-
- (void) session_abstract;
- (void) channel;
-
- /* Free the partial packet storage for sftp_packet_read */
- if(sftp->partial_packet) {
- LIBSSH2_FREE(session, sftp->partial_packet);
- }
-
- /* Free the packet storage for _libssh2_sftp_packet_readdir */
- if(sftp->readdir_packet) {
- LIBSSH2_FREE(session, sftp->readdir_packet);
- }
-
- LIBSSH2_FREE(session, sftp);
- }
-
- /*
- * sftp_init
- *
- * Startup an SFTP session
- */
- static LIBSSH2_SFTP *sftp_init(LIBSSH2_SESSION *session)
- {
- unsigned char *data;
- size_t data_len;
- ssize_t rc;
- LIBSSH2_SFTP *sftp_handle;
- struct string_buf buf;
- unsigned char *endp;
-
- if(session->sftpInit_state == libssh2_NB_state_idle) {
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
- "Initializing SFTP subsystem");
-
- /*
- * The 'sftpInit_sftp' and 'sftpInit_channel' struct fields within the
- * session struct are only to be used during the setup phase. As soon
- * 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.
- *
- * Note that you MUST NOT try to call libssh2_sftp_init() again to get
- * another handle until the previous call has finished and either
- * successfully made a handle or failed and returned error (not
- * including *EAGAIN).
- */
-
- assert(session->sftpInit_sftp == NULL);
- session->sftpInit_sftp = NULL;
- session->sftpInit_state = libssh2_NB_state_created;
- }
-
- sftp_handle = session->sftpInit_sftp;
-
- if(session->sftpInit_state == libssh2_NB_state_created) {
- session->sftpInit_channel =
- _libssh2_channel_open(session, "session", sizeof("session") - 1,
- LIBSSH2_CHANNEL_WINDOW_DEFAULT,
- LIBSSH2_CHANNEL_PACKET_DEFAULT, NULL, 0);
- if(!session->sftpInit_channel) {
- if(libssh2_session_last_errno(session) == LIBSSH2_ERROR_EAGAIN) {
- _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
- "Would block starting up channel");
- }
- else {
- _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
- "Unable to startup channel");
- session->sftpInit_state = libssh2_NB_state_idle;
- }
- return NULL;
- }
-
- session->sftpInit_state = libssh2_NB_state_sent;
- }
-
- if(session->sftpInit_state == libssh2_NB_state_sent) {
- int ret = _libssh2_channel_process_startup(session->sftpInit_channel,
- "subsystem",
- sizeof("subsystem") - 1,
- "sftp",
- strlen("sftp"));
- if(ret == LIBSSH2_ERROR_EAGAIN) {
- _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
- "Would block to request SFTP subsystem");
- return NULL;
- }
- else if(ret) {
- _libssh2_error(session, LIBSSH2_ERROR_CHANNEL_FAILURE,
- "Unable to request SFTP subsystem");
- goto sftp_init_error;
- }
-
- session->sftpInit_state = libssh2_NB_state_sent1;
- }
-
- if(session->sftpInit_state == libssh2_NB_state_sent1) {
- rc = _libssh2_channel_extended_data(session->sftpInit_channel,
- LIBSSH2_CHANNEL_EXTENDED_DATA_IGNORE);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
- "Would block requesting handle extended data");
- return NULL;
- }
-
- sftp_handle =
- session->sftpInit_sftp =
- LIBSSH2_CALLOC(session, sizeof(LIBSSH2_SFTP));
- if(!sftp_handle) {
- _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
- "Unable to allocate a new SFTP structure");
- goto sftp_init_error;
- }
- sftp_handle->channel = session->sftpInit_channel;
- sftp_handle->request_id = 0;
-
- _libssh2_htonu32(session->sftpInit_buffer, 5);
- session->sftpInit_buffer[4] = SSH_FXP_INIT;
- _libssh2_htonu32(session->sftpInit_buffer + 5, LIBSSH2_SFTP_VERSION);
- session->sftpInit_sent = 0; /* nothing's sent yet */
-
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
- "Sending FXP_INIT packet advertising "
- "version %d support",
- (int) LIBSSH2_SFTP_VERSION);
-
- session->sftpInit_state = libssh2_NB_state_sent2;
- }
-
- if(session->sftpInit_state == libssh2_NB_state_sent2) {
- /* sent off what's left of the init buffer to send */
- rc = _libssh2_channel_write(session->sftpInit_channel, 0,
- session->sftpInit_buffer +
- session->sftpInit_sent,
- 9 - session->sftpInit_sent);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
- "Would block sending SSH_FXP_INIT");
- return NULL;
- }
- else if(rc < 0) {
- _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
- "Unable to send SSH_FXP_INIT");
- goto sftp_init_error;
- }
- 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;
-
- /* if less than 9, we remain in this state to send more later on */
- }
- }
-
- rc = sftp_packet_require(sftp_handle, SSH_FXP_VERSION,
- 0, &data, &data_len, 5);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
- "Would block receiving SSH_FXP_VERSION");
- return NULL;
- }
- else if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
- if(data_len > 0) {
- LIBSSH2_FREE(session, data);
- }
- _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "Invalid SSH_FXP_VERSION response");
- goto sftp_init_error;
- }
- else if(rc) {
- _libssh2_error(session, rc,
- "Timeout waiting for response from SFTP subsystem");
- goto sftp_init_error;
- }
-
- buf.data = data;
- buf.dataptr = buf.data + 1;
- buf.len = data_len;
- endp = &buf.data[data_len];
-
- if(_libssh2_get_u32(&buf, &(sftp_handle->version)) != 0) {
- LIBSSH2_FREE(session, data);
- rc = LIBSSH2_ERROR_BUFFER_TOO_SMALL;
- goto sftp_init_error;
- }
-
- if(sftp_handle->version > LIBSSH2_SFTP_VERSION) {
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
- "Truncating remote SFTP version from %lu",
- sftp_handle->version);
- sftp_handle->version = LIBSSH2_SFTP_VERSION;
- }
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
- "Enabling SFTP version %lu compatibility",
- sftp_handle->version);
- while(buf.dataptr < endp) {
- unsigned char *extname, *extdata;
-
- if(_libssh2_get_string(&buf, &extname, NULL)) {
- LIBSSH2_FREE(session, data);
- _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
- "Data too short when extracting extname");
- goto sftp_init_error;
- }
-
- if(_libssh2_get_string(&buf, &extdata, NULL)) {
- LIBSSH2_FREE(session, data);
- _libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
- "Data too short when extracting extdata");
- goto sftp_init_error;
- }
- }
- LIBSSH2_FREE(session, data);
-
- /* Make sure that when the channel gets closed, the SFTP service is shut
- down too */
- sftp_handle->channel->abstract = sftp_handle;
- sftp_handle->channel->close_cb = libssh2_sftp_dtor;
-
- session->sftpInit_state = libssh2_NB_state_idle;
-
- /* clear the sftp and channel pointers in this session struct now */
- session->sftpInit_sftp = NULL;
- session->sftpInit_channel = NULL;
-
- _libssh2_list_init(&sftp_handle->sftp_handles);
-
- return sftp_handle;
-
- sftp_init_error:
- while(_libssh2_channel_free(session->sftpInit_channel) ==
- LIBSSH2_ERROR_EAGAIN);
- 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;
- }
-
- /*
- * libssh2_sftp_init
- *
- * Startup an SFTP session
- */
- LIBSSH2_API LIBSSH2_SFTP *libssh2_sftp_init(LIBSSH2_SESSION *session)
- {
- LIBSSH2_SFTP *ptr;
-
- if(!session)
- return NULL;
-
- if(!(session->state & LIBSSH2_STATE_AUTHENTICATED)) {
- _libssh2_error(session, LIBSSH2_ERROR_INVAL,
- "session not authenticated yet");
- return NULL;
- }
-
- BLOCK_ADJUST_ERRNO(ptr, session, sftp_init(session));
- return ptr;
- }
-
- /*
- * sftp_shutdown
- *
- * Shuts down the SFTP subsystem
- */
- static int
- sftp_shutdown(LIBSSH2_SFTP *sftp)
- {
- int rc;
- LIBSSH2_SESSION *session = sftp->channel->session;
- /*
- * Make sure all memory used in the state variables are free
- */
- if(sftp->partial_packet) {
- LIBSSH2_FREE(session, sftp->partial_packet);
- sftp->partial_packet = NULL;
- }
- if(sftp->open_packet) {
- LIBSSH2_FREE(session, sftp->open_packet);
- sftp->open_packet = NULL;
- }
- if(sftp->readdir_packet) {
- LIBSSH2_FREE(session, sftp->readdir_packet);
- sftp->readdir_packet = NULL;
- }
- if(sftp->fstat_packet) {
- LIBSSH2_FREE(session, sftp->fstat_packet);
- sftp->fstat_packet = NULL;
- }
- if(sftp->unlink_packet) {
- LIBSSH2_FREE(session, sftp->unlink_packet);
- sftp->unlink_packet = NULL;
- }
- if(sftp->rename_packet) {
- LIBSSH2_FREE(session, sftp->rename_packet);
- sftp->rename_packet = NULL;
- }
- 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;
- }
- if(sftp->mkdir_packet) {
- LIBSSH2_FREE(session, sftp->mkdir_packet);
- sftp->mkdir_packet = NULL;
- }
- if(sftp->rmdir_packet) {
- LIBSSH2_FREE(session, sftp->rmdir_packet);
- sftp->rmdir_packet = NULL;
- }
- if(sftp->stat_packet) {
- LIBSSH2_FREE(session, sftp->stat_packet);
- sftp->stat_packet = NULL;
- }
- if(sftp->symlink_packet) {
- LIBSSH2_FREE(session, sftp->symlink_packet);
- sftp->symlink_packet = NULL;
- }
- if(sftp->fsync_packet) {
- LIBSSH2_FREE(session, sftp->fsync_packet);
- sftp->fsync_packet = NULL;
- }
-
- sftp_packet_flush(sftp);
-
- /* TODO: We should consider walking over the sftp_handles list and kill
- * any remaining sftp handles ... */
-
- 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;
- if(!sftp)
- return LIBSSH2_ERROR_BAD_USE;
- BLOCK_ADJUST(rc, sftp->channel->session, sftp_shutdown(sftp));
- return rc;
- }
-
- /* *******************************
- * SFTP File and Directory Ops *
- ******************************* */
-
- /* sftp_open
- */
- static LIBSSH2_SFTP_HANDLE *
- sftp_open(LIBSSH2_SFTP *sftp, const char *filename,
- size_t filename_len, uint32_t flags, long mode,
- int open_type)
- {
- 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
- };
- unsigned char *s;
- ssize_t rc;
- int open_file = (open_type == LIBSSH2_SFTP_OPENFILE)?1:0;
-
- if(sftp->open_state == libssh2_NB_state_idle) {
- /* packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) +
- flags(4) */
- sftp->open_packet_len = filename_len + 13 +
- (open_file? (4 +
- sftp_attrsize(LIBSSH2_SFTP_ATTR_PERMISSIONS)) : 0);
-
- /* surprise! this starts out with nothing sent */
- sftp->open_packet_sent = 0;
- s = sftp->open_packet = LIBSSH2_ALLOC(session, sftp->open_packet_len);
- if(!sftp->open_packet) {
- _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
- "Unable to allocate memory for FXP_OPEN or "
- "FXP_OPENDIR packet");
- return NULL;
- }
- /* Filetype in SFTP 3 and earlier */
- attrs.permissions = mode |
- (open_file ? LIBSSH2_SFTP_ATTR_PFILETYPE_FILE :
- LIBSSH2_SFTP_ATTR_PFILETYPE_DIR);
-
- _libssh2_store_u32(&s, sftp->open_packet_len - 4);
- *(s++) = open_file? SSH_FXP_OPEN : SSH_FXP_OPENDIR;
- sftp->open_request_id = sftp->request_id++;
- _libssh2_store_u32(&s, sftp->open_request_id);
- _libssh2_store_str(&s, filename, filename_len);
-
- if(open_file) {
- _libssh2_store_u32(&s, flags);
- s += sftp_attr2bin(s, &attrs);
- }
-
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Sending %s open request",
- open_file? "file" : "directory");
-
- sftp->open_state = libssh2_NB_state_created;
- }
-
- if(sftp->open_state == libssh2_NB_state_created) {
- rc = _libssh2_channel_write(channel, 0, sftp->open_packet+
- sftp->open_packet_sent,
- sftp->open_packet_len -
- sftp->open_packet_sent);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
- "Would block sending FXP_OPEN or "
- "FXP_OPENDIR command");
- return NULL;
- }
- else if(rc < 0) {
- _libssh2_error(session, rc, "Unable to send FXP_OPEN*");
- LIBSSH2_FREE(session, sftp->open_packet);
- sftp->open_packet = NULL;
- sftp->open_state = libssh2_NB_state_idle;
- return NULL;
- }
-
- /* 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;
- }
- }
-
- if(sftp->open_state == libssh2_NB_state_sent) {
- size_t data_len;
- unsigned char *data;
- static const unsigned char fopen_responses[2] =
- { SSH_FXP_HANDLE, SSH_FXP_STATUS };
- rc = sftp_packet_requirev(sftp, 2, fopen_responses,
- sftp->open_request_id, &data,
- &data_len, 1);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- _libssh2_error(session, LIBSSH2_ERROR_EAGAIN,
- "Would block waiting for status message");
- return NULL;
- }
- else if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
- if(data_len > 0) {
- LIBSSH2_FREE(session, data);
- }
- _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "Response too small");
- return NULL;
- }
- sftp->open_state = libssh2_NB_state_idle;
- if(rc) {
- _libssh2_error(session, rc, "Timeout waiting for status message");
- return NULL;
- }
-
- /* 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;
- }
-
- 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, 10);
- 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 == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
- if(data_len > 0) {
- LIBSSH2_FREE(session, data);
- }
- _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "Too small FXP_HANDLE");
- return NULL;
- }
- else if(!rc)
- /* we got the handle so this is not a bad situation */
- badness = 0;
- }
-
- 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);
- return NULL;
- }
- }
-
- if(data_len < 10) {
- _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "Too small FXP_HANDLE");
- LIBSSH2_FREE(session, data);
- return NULL;
- }
-
- fp = LIBSSH2_CALLOC(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;
- }
- fp->handle_type = open_file ? LIBSSH2_SFTP_HANDLE_FILE :
- LIBSSH2_SFTP_HANDLE_DIR;
-
- 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;
-
- if(fp->handle_len > (data_len - 9))
- /* do not reach beyond the end of the data we got */
- fp->handle_len = data_len - 9;
-
- memcpy(fp->handle, data + 9, fp->handle_len);
-
- 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 */
-
- fp->u.file.offset = 0;
- fp->u.file.offset_sent = 0;
-
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Open command successful");
- return fp;
- }
- return NULL;
- }
-
- /* 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;
-
- if(!sftp)
- return NULL;
-
- BLOCK_ADJUST_ERRNO(hnd, sftp->channel->session,
- sftp_open(sftp, filename, filename_len, flags, mode,
- open_type));
- return hnd;
- }
-
- /*
- * sftp_read
- *
- * Read from an SFTP file handle
- *
- */
- static ssize_t sftp_read(LIBSSH2_SFTP_HANDLE * handle, char *buffer,
- size_t buffer_size)
- {
- LIBSSH2_SFTP *sftp = handle->sftp;
- LIBSSH2_CHANNEL *channel = sftp->channel;
- LIBSSH2_SESSION *session = channel->session;
- size_t count = 0;
- struct sftp_pipeline_chunk *chunk;
- struct sftp_pipeline_chunk *next;
- ssize_t rc;
- struct _libssh2_sftp_handle_file_data *filep =
- &handle->u.file;
- size_t bytes_in_buffer = 0;
- char *sliding_bufferp = buffer;
-
- /* 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:
-
- /* Some data may already have been read from the server in the
- 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. */
- if(filep->data_left) {
- size_t copy = MIN(buffer_size, filep->data_left);
-
- memcpy(buffer, &filep->data[ filep->data_len - filep->data_left],
- copy);
-
- filep->data_left -= copy;
- filep->offset += copy;
-
- if(!filep->data_left) {
- LIBSSH2_FREE(session, filep->data);
- filep->data = NULL;
- }
-
- return copy;
- }
-
- if(filep->eof) {
- return 0;
- }
- else {
- /* We allow a number of bytes being requested at any given time
- without having been acked - until we reach EOF. */
-
- /* Number of bytes asked for that haven't been acked yet */
- size_t already = (size_t)(filep->offset_sent - filep->offset);
-
- size_t max_read_ahead = buffer_size;
- unsigned long recv_window;
-
- /* 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.
- Specifically, '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,
- 1, 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;
- }
- }
-
- while(count > 0) {
- unsigned char *s;
-
- /* 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;
-
- uint32_t size = count;
- //if(size < buffer_size)
- // size = buffer_size;
- if(size > MAX_SFTP_READ_SIZE)
- size = MAX_SFTP_READ_SIZE;
-
- 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->offset = filep->offset_sent;
- 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 -= MIN(size, count); /* deduct the size we used, as we might
- * have to create more packets */
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
- "read request id %d sent (offset: %d, size: %d)",
- request_id, (int)chunk->offset, (int)chunk->len);
- }
- /* FALL-THROUGH */
- case libssh2_NB_state_sent:
-
- sftp->read_state = libssh2_NB_state_idle;
-
- /* 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);
-
- while(chunk) {
- if(chunk->lefttosend) {
-
- rc = _libssh2_channel_write(channel, 0,
- &chunk->packet[chunk->sent],
- chunk->lefttosend);
- if(rc < 0) {
- sftp->read_state = libssh2_NB_state_sent;
- return rc;
- }
-
- /* remember where to continue sending the next time */
- chunk->lefttosend -= rc;
- chunk->sent += rc;
-
- if(chunk->lefttosend) {
- /* We still have data left to send for this chunk.
- * If there is at least one completely sent chunk,
- * we can get out of this loop and start reading. */
- if(chunk != _libssh2_list_first(&handle->packet_list)) {
- break;
- }
- else {
- continue;
- }
- }
- }
-
- /* move on to the next chunk with data to send */
- chunk = _libssh2_list_next(&chunk->node);
- }
- /* FALL-THROUGH */
-
- case libssh2_NB_state_sent2:
-
- sftp->read_state = libssh2_NB_state_idle;
-
- /*
- * Count all ACKed packets and act on the contents of them.
- */
- chunk = _libssh2_list_first(&handle->packet_list);
-
- 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
- };
-
- if(chunk->lefttosend) {
- /* if the chunk still has data left to send, we shouldn't wait
- for an ACK for it just yet */
- if(bytes_in_buffer > 0) {
- return bytes_in_buffer;
- }
- else {
- /* we should never reach this point */
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "sftp_read() internal error a");
- }
- }
-
- rc = sftp_packet_requirev(sftp, 2, read_responses,
- chunk->request_id, &data, &data_len, 9);
- if(rc == LIBSSH2_ERROR_EAGAIN && bytes_in_buffer != 0) {
- /* do not return EAGAIN if we have already
- * written data into the buffer */
- return bytes_in_buffer;
- }
-
- if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
- if(data_len > 0) {
- LIBSSH2_FREE(session, data);
- }
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "Response too small");
- }
- else if(rc < 0) {
- sftp->read_state = libssh2_NB_state_sent2;
- return rc;
- }
-
- /*
- * We get DATA or STATUS back. STATUS can be error, or it is
- * FX_EOF when we reach the end of the file.
- */
-
- switch(data[0]) {
- case SSH_FXP_STATUS:
- /* remove the chunk we just processed */
-
- _libssh2_list_remove(&chunk->node);
- LIBSSH2_FREE(session, chunk);
-
- /* we must remove all outstanding READ requests, as either we
- got an error or we're at end of file */
- sftp_packetlist_flush(handle);
-
- rc32 = _libssh2_ntohu32(data + 5);
- LIBSSH2_FREE(session, data);
-
- if(rc32 == LIBSSH2_FX_EOF) {
- filep->eof = TRUE;
- return bytes_in_buffer;
- }
- else {
- sftp->last_errno = rc32;
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "SFTP READ error");
- }
- break;
-
- case SSH_FXP_DATA:
- if(chunk->offset != filep->offset) {
- /* This could happen if the server returns less bytes than
- requested, which shouldn't happen for normal files. See:
- https://tools.ietf.org/html/draft-ietf-secsh-filexfer-02
- #section-6.4
- */
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "Read Packet At Unexpected Offset");
- }
-
- 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 chunk larger than we requested was returned to us.
- This is a protocol violation and we don't know how to
- deal with it. Bail out! */
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "FXP_READ response too big");
- }
-
- 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);
- }
-
- if((bytes_in_buffer + rc32) > buffer_size) {
- /* figure out the overlap amount */
- filep->data_left = (bytes_in_buffer + rc32) - buffer_size;
-
- /* getting the full packet would overflow the buffer, so
- only get the correct amount and keep the remainder */
- rc32 = (uint32_t)buffer_size - bytes_in_buffer;
-
- /* 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 */
- memcpy(sliding_bufferp, data + 9, rc32);
- filep->offset += rc32;
- bytes_in_buffer += rc32;
- sliding_bufferp += rc32;
-
- if(filep->data_len == 0)
- /* free the allocated data if not stored to keep */
- LIBSSH2_FREE(session, data);
-
- /* 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);
-
- /* check if we have space left in the buffer
- * and either continue to the next chunk or stop
- */
- if(bytes_in_buffer < buffer_size) {
- chunk = next;
- }
- else {
- chunk = NULL;
- }
-
- break;
- default:
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "SFTP Protocol badness: unrecognised "
- "read request response");
- }
- }
-
- if(bytes_in_buffer > 0)
- return bytes_in_buffer;
-
- break;
-
- default:
- assert(!"State machine error; unrecognised read state");
- }
-
- /* we should never reach this point */
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "sftp_read() internal error b");
- }
-
- /* 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;
- if(!hnd)
- return LIBSSH2_ERROR_BAD_USE;
- BLOCK_ADJUST(rc, hnd->sftp->channel->session,
- sftp_read(hnd, buffer, buffer_maxlen));
- return rc;
- }
-
- /* sftp_readdir
- * Read from an SFTP directory handle
- */
- 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)
- {
- LIBSSH2_SFTP *sftp = handle->sftp;
- LIBSSH2_CHANNEL *channel = sftp->channel;
- LIBSSH2_SESSION *session = channel->session;
- size_t data_len;
- uint32_t num_names;
- /* 13 = packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */
- uint32_t packet_len = handle->handle_len + 13;
- unsigned char *s, *data;
- static const unsigned char read_responses[2] = {
- SSH_FXP_NAME, SSH_FXP_STATUS };
- ssize_t retcode;
-
- if(sftp->readdir_state == libssh2_NB_state_idle) {
- if(handle->u.dir.names_left) {
- /*
- * A prior request returned more than one directory entry,
- * feed it back from the buffer
- */
- LIBSSH2_SFTP_ATTRIBUTES attrs_dummy;
- size_t real_longentry_len;
- size_t real_filename_len;
- size_t filename_len;
- size_t longentry_len;
- size_t names_packet_len = handle->u.dir.names_packet_len;
- int attr_len = 0;
-
- if(names_packet_len >= 4) {
- s = (unsigned char *) handle->u.dir.next_name;
- real_filename_len = _libssh2_ntohu32(s);
- s += 4;
- names_packet_len -= 4;
- }
- else {
- filename_len = (size_t)LIBSSH2_ERROR_BUFFER_TOO_SMALL;
- goto end;
- }
-
- filename_len = real_filename_len;
- if(filename_len >= buffer_maxlen) {
- filename_len = (size_t)LIBSSH2_ERROR_BUFFER_TOO_SMALL;
- goto end;
- }
-
- if(buffer_maxlen >= filename_len && names_packet_len >=
- filename_len) {
- memcpy(buffer, s, filename_len);
- buffer[filename_len] = '\0'; /* zero terminate */
- s += real_filename_len;
- names_packet_len -= real_filename_len;
- }
- else {
- filename_len = (size_t)LIBSSH2_ERROR_BUFFER_TOO_SMALL;
- goto end;
- }
-
- if(names_packet_len >= 4) {
- real_longentry_len = _libssh2_ntohu32(s);
- s += 4;
- names_packet_len -= 4;
- }
- else {
- filename_len = (size_t)LIBSSH2_ERROR_BUFFER_TOO_SMALL;
- goto end;
- }
-
- if(longentry && (longentry_maxlen>1)) {
- longentry_len = real_longentry_len;
-
- if(longentry_len >= longentry_maxlen ||
- longentry_len > names_packet_len) {
- filename_len = (size_t)LIBSSH2_ERROR_BUFFER_TOO_SMALL;
- goto end;
- }
-
- memcpy(longentry, s, longentry_len);
- longentry[longentry_len] = '\0'; /* zero terminate */
- }
-
- if(real_longentry_len <= names_packet_len) {
- s += real_longentry_len;
- names_packet_len -= real_longentry_len;
- }
- else {
- filename_len = (size_t)LIBSSH2_ERROR_BUFFER_TOO_SMALL;
- goto end;
- }
-
- if(attrs)
- memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
-
- attr_len = sftp_bin2attr(attrs ? attrs : &attrs_dummy, s,
- names_packet_len);
-
- if(attr_len >= 0) {
- s += attr_len;
- names_packet_len -= attr_len;
- }
- else {
- filename_len = (size_t)LIBSSH2_ERROR_BUFFER_TOO_SMALL;
- goto end;
- }
-
- handle->u.dir.next_name = (char *) s;
- handle->u.dir.names_packet_len = names_packet_len;
- end:
-
- if((--handle->u.dir.names_left) == 0)
- LIBSSH2_FREE(session, handle->u.dir.names_packet);
-
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
- "libssh2_sftp_readdir_ex() return %d",
- filename_len);
- return (ssize_t)filename_len;
- }
-
- /* Request another entry(entries?) */
-
- s = sftp->readdir_packet = LIBSSH2_ALLOC(session, packet_len);
- if(!sftp->readdir_packet)
- return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
- "Unable to allocate memory for "
- "FXP_READDIR packet");
-
- _libssh2_store_u32(&s, packet_len - 4);
- *(s++) = SSH_FXP_READDIR;
- sftp->readdir_request_id = sftp->request_id++;
- _libssh2_store_u32(&s, sftp->readdir_request_id);
- _libssh2_store_str(&s, handle->handle, handle->handle_len);
-
- sftp->readdir_state = libssh2_NB_state_created;
- }
-
- if(sftp->readdir_state == libssh2_NB_state_created) {
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
- "Reading entries from directory handle");
- retcode = _libssh2_channel_write(channel, 0, sftp->readdir_packet,
- packet_len);
- if(retcode == LIBSSH2_ERROR_EAGAIN) {
- return retcode;
- }
- else if((ssize_t)packet_len != retcode) {
- LIBSSH2_FREE(session, sftp->readdir_packet);
- sftp->readdir_packet = NULL;
- sftp->readdir_state = libssh2_NB_state_idle;
- return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
- "_libssh2_channel_write() failed");
- }
-
- LIBSSH2_FREE(session, sftp->readdir_packet);
- sftp->readdir_packet = NULL;
-
- sftp->readdir_state = libssh2_NB_state_sent;
- }
-
- retcode = sftp_packet_requirev(sftp, 2, read_responses,
- sftp->readdir_request_id, &data,
- &data_len, 9);
- if(retcode == LIBSSH2_ERROR_EAGAIN)
- return retcode;
- else if(retcode == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
- if(data_len > 0) {
- LIBSSH2_FREE(session, data);
- }
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "Status message too short");
- }
- else if(retcode) {
- sftp->readdir_state = libssh2_NB_state_idle;
- return _libssh2_error(session, retcode,
- "Timeout waiting for status message");
- }
-
- if(data[0] == SSH_FXP_STATUS) {
- retcode = _libssh2_ntohu32(data + 5);
- LIBSSH2_FREE(session, data);
- if(retcode == LIBSSH2_FX_EOF) {
- sftp->readdir_state = libssh2_NB_state_idle;
- return 0;
- }
- else {
- sftp->last_errno = retcode;
- sftp->readdir_state = libssh2_NB_state_idle;
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "SFTP Protocol Error");
- }
- }
-
- sftp->readdir_state = libssh2_NB_state_idle;
-
- num_names = _libssh2_ntohu32(data + 5);
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "%lu entries returned",
- num_names);
- if(!num_names) {
- LIBSSH2_FREE(session, data);
- return 0;
- }
-
- handle->u.dir.names_left = num_names;
- handle->u.dir.names_packet = data;
- handle->u.dir.next_name = (char *) data + 9;
- handle->u.dir.names_packet_len = data_len - 9;
-
- /* use the name popping mechanism from the start of the function */
- return sftp_readdir(handle, buffer, buffer_maxlen, longentry,
- longentry_maxlen, attrs);
- }
-
- /* 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;
- if(!hnd)
- return LIBSSH2_ERROR_BAD_USE;
- BLOCK_ADJUST(rc, hnd->sftp->channel->session,
- sftp_readdir(hnd, buffer, buffer_maxlen, longentry,
- longentry_maxlen, attrs));
- return rc;
- }
-
- /*
- * sftp_write
- *
- * Write data to an SFTP handle. Returns the number of bytes written, or
- * a negative error code.
- *
- * 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
- * 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.
- *
- * - Return TOTAL bytes acked so far.
- *
- * Caveats:
- * - be careful: we must not return a higher number than what was given!
- *
- * 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.
- */
-
- static ssize_t sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer,
- size_t count)
- {
- LIBSSH2_SFTP *sftp = handle->sftp;
- LIBSSH2_CHANNEL *channel = sftp->channel;
- LIBSSH2_SESSION *session = channel->session;
- size_t data_len;
- uint32_t retcode;
- uint32_t packet_len;
- unsigned char *s, *data;
- ssize_t rc;
- struct sftp_pipeline_chunk *chunk;
- struct sftp_pipeline_chunk *next;
- size_t acked = 0;
- size_t org_count = count;
- size_t already;
-
- switch(sftp->write_state) {
- default:
- case libssh2_NB_state_idle:
-
- /* Number of bytes sent off that haven't been acked and therefore we
- will get passed in here again.
-
- 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 = (size_t) (handle->u.file.offset_sent -
- handle->u.file.offset)+
- handle->u.file.acked;
-
- 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;
-
- /* 25 = packet_len(4) + packet_type(1) + request_id(4) +
- handle_len(4) + offset(8) + count(4) */
- packet_len = handle->handle_len + size + 25;
-
- 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->sent = 0;
- chunk->lefttosend = packet_len;
-
- s = chunk->packet;
- _libssh2_store_u32(&s, packet_len - 4);
-
- *(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);
-
- /* 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 */
- return rc;
-
- /* 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;
- }
-
- /* 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);
-
- while(chunk) {
- if(chunk->lefttosend)
- /* if the chunk still has data left to send, we shouldn't wait
- for an ACK for it just yet */
- break;
-
- 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;
-
- /* we check the packets in order */
- rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
- chunk->request_id, &data, &data_len, 9);
- if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
- if(data_len > 0) {
- LIBSSH2_FREE(session, data);
- }
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "FXP write packet too short");
- }
- else if(rc < 0) {
- if(rc == LIBSSH2_ERROR_EAGAIN)
- sftp->write_state = libssh2_NB_state_sent;
- return rc;
- }
-
- retcode = _libssh2_ntohu32(data + 5);
- LIBSSH2_FREE(session, data);
-
- sftp->last_errno = retcode;
- if(retcode == LIBSSH2_FX_OK) {
- acked += chunk->len; /* number of payload data that was acked
- here */
-
- /* we increase the offset value for all acks */
- handle->u.file.offset += chunk->len;
-
- next = _libssh2_list_next(&chunk->node);
-
- _libssh2_list_remove(&chunk->node); /* remove from list */
- LIBSSH2_FREE(session, chunk); /* free memory */
-
- chunk = next;
- }
- else {
- /* flush all pending packets from the outgoing list */
- sftp_packetlist_flush(handle);
-
- /* since we return error now, the application 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;
-
- /* then reset the offset_sent to be the same as the offset */
- handle->u.file.offset_sent = handle->u.file.offset;
-
- /* clear the acked counter since we can have no pending data to
- ack after an error */
- handle->u.file.acked = 0;
-
- /* 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");
- }
- }
- break;
- }
-
- /* 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);
- /* we got data acked so return that amount, but no more than what
- was asked to get sent! */
-
- /* store the remainder. 'ret' is always equal to or less than 'acked'
- here */
- handle->u.file.acked = acked - ret;
-
- return ret;
- }
-
- else
- return 0; /* nothing was acked, and no EAGAIN was received! */
- }
-
- /* 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;
- if(!hnd)
- return LIBSSH2_ERROR_BAD_USE;
- BLOCK_ADJUST(rc, hnd->sftp->channel->session,
- sftp_write(hnd, buffer, count));
- return rc;
-
- }
-
- static int sftp_fsync(LIBSSH2_SFTP_HANDLE *handle)
- {
- LIBSSH2_SFTP *sftp = handle->sftp;
- LIBSSH2_CHANNEL *channel = sftp->channel;
- LIBSSH2_SESSION *session = channel->session;
- /* 34 = packet_len(4) + packet_type(1) + request_id(4) +
- string_len(4) + strlen("fsync@openssh.com")(17) + handle_len(4) */
- uint32_t packet_len = handle->handle_len + 34;
- size_t data_len;
- unsigned char *packet, *s, *data;
- ssize_t rc;
- uint32_t retcode;
-
- if(sftp->fsync_state == libssh2_NB_state_idle) {
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
- "Issuing fsync command");
- 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->fsync_request_id = sftp->request_id++;
- _libssh2_store_u32(&s, sftp->fsync_request_id);
- _libssh2_store_str(&s, "fsync@openssh.com", 17);
- _libssh2_store_str(&s, handle->handle, handle->handle_len);
-
- sftp->fsync_state = libssh2_NB_state_created;
- }
- else {
- packet = sftp->fsync_packet;
- }
-
- if(sftp->fsync_state == libssh2_NB_state_created) {
- rc = _libssh2_channel_write(channel, 0, packet, packet_len);
- if(rc == LIBSSH2_ERROR_EAGAIN ||
- (0 <= rc && rc < (ssize_t)packet_len)) {
- sftp->fsync_packet = packet;
- return LIBSSH2_ERROR_EAGAIN;
- }
-
- LIBSSH2_FREE(session, packet);
- sftp->fsync_packet = NULL;
-
- if(rc < 0) {
- sftp->fsync_state = libssh2_NB_state_idle;
- return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
- "_libssh2_channel_write() failed");
- }
- sftp->fsync_state = libssh2_NB_state_sent;
- }
-
- rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
- sftp->fsync_request_id, &data, &data_len, 9);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- return rc;
- }
- else if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
- if(data_len > 0) {
- LIBSSH2_FREE(session, data);
- }
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "SFTP fsync packet too short");
- }
- else if(rc) {
- sftp->fsync_state = libssh2_NB_state_idle;
- return _libssh2_error(session, rc,
- "Error waiting for FXP EXTENDED REPLY");
- }
-
- sftp->fsync_state = libssh2_NB_state_idle;
-
- retcode = _libssh2_ntohu32(data + 5);
- LIBSSH2_FREE(session, data);
-
- if(retcode != LIBSSH2_FX_OK) {
- sftp->last_errno = retcode;
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "fsync failed");
- }
-
- return 0;
- }
-
- /* libssh2_sftp_fsync
- * Commit data on the handle to disk.
- */
- LIBSSH2_API int
- libssh2_sftp_fsync(LIBSSH2_SFTP_HANDLE *hnd)
- {
- int rc;
- if(!hnd)
- return LIBSSH2_ERROR_BAD_USE;
- BLOCK_ADJUST(rc, hnd->sftp->channel->session,
- sftp_fsync(hnd));
- return rc;
- }
-
-
- /*
- * sftp_fstat
- *
- * Get or Set stat on a file
- */
- static int sftp_fstat(LIBSSH2_SFTP_HANDLE *handle,
- LIBSSH2_SFTP_ATTRIBUTES *attrs, int setstat)
- {
- LIBSSH2_SFTP *sftp = handle->sftp;
- LIBSSH2_CHANNEL *channel = sftp->channel;
- LIBSSH2_SESSION *session = channel->session;
- size_t data_len;
- /* 13 = packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */
- uint32_t packet_len =
- handle->handle_len + 13 + (setstat ? sftp_attrsize(attrs->flags) : 0);
- unsigned char *s, *data;
- static const unsigned char fstat_responses[2] =
- { SSH_FXP_ATTRS, SSH_FXP_STATUS };
- ssize_t rc;
-
- if(sftp->fstat_state == libssh2_NB_state_idle) {
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Issuing %s command",
- setstat ? "set-stat" : "stat");
- s = sftp->fstat_packet = LIBSSH2_ALLOC(session, packet_len);
- if(!sftp->fstat_packet) {
- return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
- "Unable to allocate memory for "
- "FSTAT/FSETSTAT packet");
- }
-
- _libssh2_store_u32(&s, packet_len - 4);
- *(s++) = setstat ? SSH_FXP_FSETSTAT : SSH_FXP_FSTAT;
- sftp->fstat_request_id = sftp->request_id++;
- _libssh2_store_u32(&s, sftp->fstat_request_id);
- _libssh2_store_str(&s, handle->handle, handle->handle_len);
-
- if(setstat) {
- s += sftp_attr2bin(s, attrs);
- }
-
- sftp->fstat_state = libssh2_NB_state_created;
- }
-
- if(sftp->fstat_state == libssh2_NB_state_created) {
- rc = _libssh2_channel_write(channel, 0, sftp->fstat_packet,
- packet_len);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- return rc;
- }
- else if((ssize_t)packet_len != rc) {
- LIBSSH2_FREE(session, sftp->fstat_packet);
- sftp->fstat_packet = NULL;
- sftp->fstat_state = libssh2_NB_state_idle;
- return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
- (setstat ? "Unable to send FXP_FSETSTAT"
- : "Unable to send FXP_FSTAT command"));
- }
- LIBSSH2_FREE(session, sftp->fstat_packet);
- sftp->fstat_packet = NULL;
-
- sftp->fstat_state = libssh2_NB_state_sent;
- }
-
- rc = sftp_packet_requirev(sftp, 2, fstat_responses,
- sftp->fstat_request_id, &data,
- &data_len, 9);
- if(rc == LIBSSH2_ERROR_EAGAIN)
- return rc;
- else if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
- if(data_len > 0) {
- LIBSSH2_FREE(session, data);
- }
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "SFTP fstat packet too short");
- }
- else if(rc) {
- sftp->fstat_state = libssh2_NB_state_idle;
- return _libssh2_error(session, rc,
- "Timeout waiting for status message");
- }
-
- sftp->fstat_state = libssh2_NB_state_idle;
-
- if(data[0] == SSH_FXP_STATUS) {
- uint32_t retcode;
-
- retcode = _libssh2_ntohu32(data + 5);
- LIBSSH2_FREE(session, data);
- if(retcode == LIBSSH2_FX_OK) {
- return 0;
- }
- else {
- sftp->last_errno = retcode;
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "SFTP Protocol Error");
- }
- }
-
- if(sftp_bin2attr(attrs, data + 5, data_len - 5) < 0) {
- LIBSSH2_FREE(session, data);
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "Attributes too short in SFTP fstat");
- }
-
- LIBSSH2_FREE(session, data);
-
- return 0;
- }
-
- /* libssh2_sftp_fstat_ex
- * Get or Set stat on a file
- */
- LIBSSH2_API int
- libssh2_sftp_fstat_ex(LIBSSH2_SFTP_HANDLE *hnd,
- LIBSSH2_SFTP_ATTRIBUTES *attrs, int setstat)
- {
- int rc;
- if(!hnd || !attrs)
- return LIBSSH2_ERROR_BAD_USE;
- BLOCK_ADJUST(rc, hnd->sftp->channel->session,
- sftp_fstat(hnd, attrs, setstat));
- return rc;
- }
-
-
- /* libssh2_sftp_seek64
- * Set the read/write pointer to an arbitrary position within the file
- */
- LIBSSH2_API void
- libssh2_sftp_seek64(LIBSSH2_SFTP_HANDLE *handle, libssh2_uint64_t offset)
- {
- if(!handle)
- return;
- if(handle->u.file.offset == offset && handle->u.file.offset_sent == offset)
- return;
-
- handle->u.file.offset = handle->u.file.offset_sent = offset;
- /* 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;
- }
-
- /* reset EOF to False */
- handle->u.file.eof = FALSE;
- }
-
- /* libssh2_sftp_seek
- * Set the read/write pointer to an arbitrary position within the file
- */
- LIBSSH2_API void
- libssh2_sftp_seek(LIBSSH2_SFTP_HANDLE *handle, size_t offset)
- {
- libssh2_sftp_seek64(handle, (libssh2_uint64_t)offset);
- }
-
- /* libssh2_sftp_tell
- * Return the current read/write pointer's offset
- */
- LIBSSH2_API size_t
- libssh2_sftp_tell(LIBSSH2_SFTP_HANDLE *handle)
- {
- if(!handle)
- return 0; /* no handle, no size */
-
- /* 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);
- }
-
- /* libssh2_sftp_tell64
- * Return the current read/write pointer's offset
- */
- LIBSSH2_API libssh2_uint64_t
- libssh2_sftp_tell64(LIBSSH2_SFTP_HANDLE *handle)
- {
- if(!handle)
- return 0; /* no handle, no size */
-
- return handle->u.file.offset;
- }
-
- /*
- * Flush all remaining incoming SFTP packets and zombies.
- */
- static void sftp_packet_flush(LIBSSH2_SFTP *sftp)
- {
- LIBSSH2_CHANNEL *channel = sftp->channel;
- LIBSSH2_SESSION *session = channel->session;
- LIBSSH2_SFTP_PACKET *packet = _libssh2_list_first(&sftp->packets);
- struct sftp_zombie_requests *zombie =
- _libssh2_list_first(&sftp->zombie_requests);
-
- while(packet) {
- LIBSSH2_SFTP_PACKET *next;
-
- /* 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;
- }
-
- while(zombie) {
- /* figure out the next node */
- struct sftp_zombie_requests *next = _libssh2_list_next(&zombie->node);
- /* unlink the current one */
- _libssh2_list_remove(&zombie->node);
- /* free the memory */
- LIBSSH2_FREE(session, zombie);
- zombie = next;
- }
-
- }
-
- /* sftp_close_handle
- *
- * Close a file or directory handle.
- * Also frees handle resource and unlinks it from the SFTP structure.
- * The handle is no longer usable after return of this function, unless
- * the return value is LIBSSH2_ERROR_EAGAIN in which case this function
- * should be called again.
- */
- static int
- sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle)
- {
- LIBSSH2_SFTP *sftp = handle->sftp;
- LIBSSH2_CHANNEL *channel = sftp->channel;
- LIBSSH2_SESSION *session = channel->session;
- size_t data_len;
- /* 13 = packet_len(4) + packet_type(1) + request_id(4) + handle_len(4) */
- uint32_t packet_len = handle->handle_len + 13;
- unsigned char *s, *data = NULL;
- int rc = 0;
-
- if(handle->close_state == libssh2_NB_state_idle) {
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Closing handle");
- s = handle->close_packet = LIBSSH2_ALLOC(session, packet_len);
- if(!handle->close_packet) {
- handle->close_state = libssh2_NB_state_idle;
- rc = _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
- "Unable to allocate memory for FXP_CLOSE "
- "packet");
- }
- else {
-
- _libssh2_store_u32(&s, packet_len - 4);
- *(s++) = SSH_FXP_CLOSE;
- handle->close_request_id = sftp->request_id++;
- _libssh2_store_u32(&s, handle->close_request_id);
- _libssh2_store_str(&s, handle->handle, handle->handle_len);
- handle->close_state = libssh2_NB_state_created;
- }
- }
-
- if(handle->close_state == libssh2_NB_state_created) {
- rc = _libssh2_channel_write(channel, 0, handle->close_packet,
- packet_len);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- return rc;
- }
- else if((ssize_t)packet_len != rc) {
- handle->close_state = libssh2_NB_state_idle;
- rc = _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
- "Unable to send FXP_CLOSE command");
- }
- else
- handle->close_state = libssh2_NB_state_sent;
-
- LIBSSH2_FREE(session, handle->close_packet);
- handle->close_packet = NULL;
- }
-
- if(handle->close_state == libssh2_NB_state_sent) {
- rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
- handle->close_request_id, &data,
- &data_len, 9);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- return rc;
- }
- else if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
- if(data_len > 0) {
- LIBSSH2_FREE(session, data);
- }
- data = NULL;
- _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "Packet too short in FXP_CLOSE command");
- }
- else if(rc) {
- _libssh2_error(session, rc,
- "Error waiting for status message");
- }
-
- handle->close_state = libssh2_NB_state_sent1;
- }
-
- if(!data) {
- /* if it reaches this point with data unset, something unwanted
- happened for which we should have set an error code */
- assert(rc);
-
- }
- else {
- int retcode = _libssh2_ntohu32(data + 5);
- LIBSSH2_FREE(session, data);
-
- if(retcode != LIBSSH2_FX_OK) {
- sftp->last_errno = retcode;
- handle->close_state = libssh2_NB_state_idle;
- rc = _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "SFTP Protocol Error");
- }
- }
-
- /* remove this handle from the parent's list */
- _libssh2_list_remove(&handle->node);
-
- if(handle->handle_type == LIBSSH2_SFTP_HANDLE_DIR) {
- if(handle->u.dir.names_left)
- LIBSSH2_FREE(session, handle->u.dir.names_packet);
- }
- else if(handle->handle_type == LIBSSH2_SFTP_HANDLE_FILE) {
- if(handle->u.file.data)
- LIBSSH2_FREE(session, handle->u.file.data);
- }
-
- sftp_packetlist_flush(handle);
- sftp->read_state = libssh2_NB_state_idle;
-
- handle->close_state = libssh2_NB_state_idle;
-
- LIBSSH2_FREE(session, handle);
-
- return rc;
- }
-
- /* 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;
- if(!hnd)
- return LIBSSH2_ERROR_BAD_USE;
- BLOCK_ADJUST(rc, hnd->sftp->channel->session, sftp_close_handle(hnd));
- return rc;
- }
-
- /* sftp_unlink
- * Delete a file from the remote server
- */
- static int sftp_unlink(LIBSSH2_SFTP *sftp, const char *filename,
- size_t filename_len)
- {
- LIBSSH2_CHANNEL *channel = sftp->channel;
- LIBSSH2_SESSION *session = channel->session;
- size_t data_len;
- int retcode;
- /* 13 = packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) */
- uint32_t packet_len = filename_len + 13;
- unsigned char *s, *data;
- int rc;
-
- if(sftp->unlink_state == libssh2_NB_state_idle) {
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Unlinking %s", filename);
- s = sftp->unlink_packet = LIBSSH2_ALLOC(session, packet_len);
- if(!sftp->unlink_packet) {
- return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
- "Unable to allocate memory for FXP_REMOVE "
- "packet");
- }
-
- _libssh2_store_u32(&s, packet_len - 4);
- *(s++) = SSH_FXP_REMOVE;
- sftp->unlink_request_id = sftp->request_id++;
- _libssh2_store_u32(&s, sftp->unlink_request_id);
- _libssh2_store_str(&s, filename, filename_len);
- sftp->unlink_state = libssh2_NB_state_created;
- }
-
- if(sftp->unlink_state == libssh2_NB_state_created) {
- rc = _libssh2_channel_write(channel, 0, sftp->unlink_packet,
- packet_len);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- return rc;
- }
- else if((ssize_t)packet_len != rc) {
- LIBSSH2_FREE(session, sftp->unlink_packet);
- sftp->unlink_packet = NULL;
- sftp->unlink_state = libssh2_NB_state_idle;
- return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
- "Unable to send FXP_REMOVE command");
- }
- LIBSSH2_FREE(session, sftp->unlink_packet);
- sftp->unlink_packet = NULL;
-
- sftp->unlink_state = libssh2_NB_state_sent;
- }
-
- rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
- sftp->unlink_request_id, &data,
- &data_len, 9);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- return rc;
- }
- else if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
- if(data_len > 0) {
- LIBSSH2_FREE(session, data);
- }
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "SFTP unlink packet too short");
- }
- else if(rc) {
- sftp->unlink_state = libssh2_NB_state_idle;
- return _libssh2_error(session, rc,
- "Error waiting for FXP STATUS");
- }
-
- sftp->unlink_state = libssh2_NB_state_idle;
-
- retcode = _libssh2_ntohu32(data + 5);
- LIBSSH2_FREE(session, data);
-
- if(retcode == LIBSSH2_FX_OK) {
- return 0;
- }
- else {
- sftp->last_errno = retcode;
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "SFTP Protocol Error");
- }
- }
-
- /* libssh2_sftp_unlink_ex
- * Delete a file from the remote server
- */
- LIBSSH2_API int
- libssh2_sftp_unlink_ex(LIBSSH2_SFTP *sftp, const char *filename,
- unsigned int filename_len)
- {
- int rc;
- if(!sftp)
- return LIBSSH2_ERROR_BAD_USE;
- 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,
- unsigned int source_filename_len,
- const char *dest_filename,
- unsigned int dest_filename_len, long flags)
- {
- LIBSSH2_CHANNEL *channel = sftp->channel;
- LIBSSH2_SESSION *session = channel->session;
- size_t data_len;
- int retcode;
- uint32_t packet_len =
- source_filename_len + dest_filename_len + 17 + (sftp->version >=
- 5 ? 4 : 0);
- /* packet_len(4) + packet_type(1) + request_id(4) +
- source_filename_len(4) + dest_filename_len(4) + flags(4){SFTP5+) */
- unsigned char *data;
- ssize_t rc;
-
- if(sftp->version < 2) {
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "Server does not support RENAME");
- }
-
- if(sftp->rename_state == libssh2_NB_state_idle) {
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Renaming %s to %s",
- source_filename, dest_filename);
- sftp->rename_s = sftp->rename_packet =
- LIBSSH2_ALLOC(session, packet_len);
- if(!sftp->rename_packet) {
- return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
- "Unable to allocate memory for FXP_RENAME "
- "packet");
- }
-
- _libssh2_store_u32(&sftp->rename_s, packet_len - 4);
- *(sftp->rename_s++) = SSH_FXP_RENAME;
- sftp->rename_request_id = sftp->request_id++;
- _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);
-
- sftp->rename_state = libssh2_NB_state_created;
- }
-
- if(sftp->rename_state == libssh2_NB_state_created) {
- rc = _libssh2_channel_write(channel, 0, sftp->rename_packet,
- sftp->rename_s - sftp->rename_packet);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- return rc;
- }
- else if((ssize_t)packet_len != rc) {
- LIBSSH2_FREE(session, sftp->rename_packet);
- sftp->rename_packet = NULL;
- sftp->rename_state = libssh2_NB_state_idle;
- return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
- "Unable to send FXP_RENAME command");
- }
- LIBSSH2_FREE(session, sftp->rename_packet);
- sftp->rename_packet = NULL;
-
- sftp->rename_state = libssh2_NB_state_sent;
- }
-
- rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
- sftp->rename_request_id, &data,
- &data_len, 9);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- return rc;
- }
- else if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
- if(data_len > 0) {
- LIBSSH2_FREE(session, data);
- }
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "SFTP rename packet too short");
- }
- else if(rc) {
- sftp->rename_state = libssh2_NB_state_idle;
- return _libssh2_error(session, rc,
- "Error waiting for FXP STATUS");
- }
-
- sftp->rename_state = libssh2_NB_state_idle;
-
- retcode = _libssh2_ntohu32(data + 5);
- LIBSSH2_FREE(session, data);
-
- sftp->last_errno = retcode;
-
- /* now convert the SFTP error code to libssh2 return code or error
- message */
- switch(retcode) {
- case LIBSSH2_FX_OK:
- retcode = LIBSSH2_ERROR_NONE;
- break;
-
- case LIBSSH2_FX_FILE_ALREADY_EXISTS:
- retcode = _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "File already exists and "
- "SSH_FXP_RENAME_OVERWRITE not specified");
- break;
-
- case LIBSSH2_FX_OP_UNSUPPORTED:
- retcode = _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "Operation Not Supported");
- break;
-
- default:
- retcode = _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "SFTP Protocol Error");
- break;
- }
-
- return retcode;
- }
-
- /* libssh2_sftp_rename_ex
- * Rename a file on the remote server
- */
- LIBSSH2_API int
- 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;
- if(!sftp)
- return LIBSSH2_ERROR_BAD_USE;
- BLOCK_ADJUST(rc, sftp->channel->session,
- sftp_rename(sftp, source_filename, source_filename_len,
- dest_filename, dest_filename_len, flags));
- return rc;
- }
-
- /*
- * 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") */
- uint32_t packet_len = handle->handle_len + 20 + 17;
- unsigned char *packet, *s, *data;
- ssize_t rc;
- unsigned int flag;
- static const unsigned char responses[2] =
- { SSH_FXP_EXTENDED_REPLY, SSH_FXP_STATUS };
-
- 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) {
- rc = _libssh2_channel_write(channel, 0, packet, packet_len);
- if(rc == LIBSSH2_ERROR_EAGAIN ||
- (0 <= rc && rc < (ssize_t)packet_len)) {
- 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_requirev(sftp, 2, responses, sftp->fstatvfs_request_id,
- &data, &data_len, 9);
-
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- return rc;
- }
- else if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
- if(data_len > 0) {
- LIBSSH2_FREE(session, data);
- }
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "SFTP rename packet too short");
- }
- else if(rc) {
- sftp->fstatvfs_state = libssh2_NB_state_idle;
- return _libssh2_error(session, rc,
- "Error waiting for FXP EXTENDED REPLY");
- }
-
- if(data[0] == SSH_FXP_STATUS) {
- int retcode = _libssh2_ntohu32(data + 5);
- sftp->fstatvfs_state = libssh2_NB_state_idle;
- LIBSSH2_FREE(session, data);
- sftp->last_errno = retcode;
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "SFTP Protocol Error");
- }
-
- 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 = (unsigned int)_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;
- if(!handle || !st)
- return LIBSSH2_ERROR_BAD_USE;
- 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") */
- uint32_t packet_len = path_len + 19 + 17;
- unsigned char *packet, *s, *data;
- ssize_t rc;
- unsigned int flag;
- static const unsigned char responses[2] =
- { SSH_FXP_EXTENDED_REPLY, SSH_FXP_STATUS };
-
- 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) {
- rc = _libssh2_channel_write(channel, 0, packet, packet_len);
- if(rc == LIBSSH2_ERROR_EAGAIN ||
- (0 <= rc && rc < (ssize_t)packet_len)) {
- 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_requirev(sftp, 2, responses, sftp->statvfs_request_id,
- &data, &data_len, 9);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- return rc;
- }
- else if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
- if(data_len > 0) {
- LIBSSH2_FREE(session, data);
- }
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "SFTP fstat packet too short");
- }
- else if(rc) {
- sftp->statvfs_state = libssh2_NB_state_idle;
- return _libssh2_error(session, rc,
- "Error waiting for FXP EXTENDED REPLY");
- }
-
- if(data[0] == SSH_FXP_STATUS) {
- int retcode = _libssh2_ntohu32(data + 5);
- sftp->statvfs_state = libssh2_NB_state_idle;
- LIBSSH2_FREE(session, data);
- sftp->last_errno = retcode;
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "SFTP Protocol Error");
- }
-
- if(data_len < 93) {
- LIBSSH2_FREE(session, data);
- sftp->statvfs_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 = (unsigned int)_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
- libssh2_sftp_statvfs(LIBSSH2_SFTP *sftp, const char *path,
- size_t path_len, LIBSSH2_SFTP_STATVFS *st)
- {
- int rc;
- if(!sftp || !st)
- return LIBSSH2_ERROR_BAD_USE;
- BLOCK_ADJUST(rc, sftp->channel->session, sftp_statvfs(sftp, path, path_len,
- st));
- return rc;
- }
-
-
- /*
- * sftp_mkdir
- *
- * Create an SFTP directory
- */
- static int sftp_mkdir(LIBSSH2_SFTP *sftp, const char *path,
- unsigned int path_len, long mode)
- {
- LIBSSH2_CHANNEL *channel = sftp->channel;
- LIBSSH2_SESSION *session = channel->session;
- LIBSSH2_SFTP_ATTRIBUTES attrs = {
- 0, 0, 0, 0, 0, 0, 0
- };
- size_t data_len;
- int retcode;
- ssize_t packet_len;
- unsigned char *packet, *s, *data;
- int rc;
-
- if(mode != LIBSSH2_SFTP_DEFAULT_MODE) {
- /* Filetype in SFTP 3 and earlier */
- attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS;
- attrs.permissions = mode | LIBSSH2_SFTP_ATTR_PFILETYPE_DIR;
- }
-
- /* 13 = packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */
- packet_len = path_len + 13 + sftp_attrsize(attrs.flags);
-
- if(sftp->mkdir_state == libssh2_NB_state_idle) {
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
- "Creating directory %s with mode 0%lo", path, mode);
- s = packet = LIBSSH2_ALLOC(session, packet_len);
- if(!packet) {
- return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
- "Unable to allocate memory for FXP_MKDIR "
- "packet");
- }
-
- _libssh2_store_u32(&s, packet_len - 4);
- *(s++) = SSH_FXP_MKDIR;
- sftp->mkdir_request_id = sftp->request_id++;
- _libssh2_store_u32(&s, sftp->mkdir_request_id);
- _libssh2_store_str(&s, path, path_len);
-
- s += sftp_attr2bin(s, &attrs);
-
- sftp->mkdir_state = libssh2_NB_state_created;
- }
- else {
- packet = sftp->mkdir_packet;
- }
-
- if(sftp->mkdir_state == libssh2_NB_state_created) {
- rc = _libssh2_channel_write(channel, 0, packet, packet_len);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- sftp->mkdir_packet = packet;
- return rc;
- }
- if(packet_len != rc) {
- LIBSSH2_FREE(session, packet);
- sftp->mkdir_state = libssh2_NB_state_idle;
- return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
- "_libssh2_channel_write() failed");
- }
- LIBSSH2_FREE(session, packet);
- sftp->mkdir_state = libssh2_NB_state_sent;
- sftp->mkdir_packet = NULL;
- }
-
- rc = sftp_packet_require(sftp, SSH_FXP_STATUS, sftp->mkdir_request_id,
- &data, &data_len, 9);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- return rc;
- }
- else if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
- if(data_len > 0) {
- LIBSSH2_FREE(session, data);
- }
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "SFTP mkdir packet too short");
- }
- else if(rc) {
- sftp->mkdir_state = libssh2_NB_state_idle;
- return _libssh2_error(session, rc,
- "Error waiting for FXP STATUS");
- }
-
- sftp->mkdir_state = libssh2_NB_state_idle;
-
- retcode = _libssh2_ntohu32(data + 5);
- LIBSSH2_FREE(session, data);
-
- if(retcode == LIBSSH2_FX_OK) {
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "OK!");
- return 0;
- }
- else {
- sftp->last_errno = retcode;
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "SFTP Protocol Error");
- }
- }
-
- /*
- * libssh2_sftp_mkdir_ex
- *
- * Create an SFTP directory
- */
- LIBSSH2_API int
- libssh2_sftp_mkdir_ex(LIBSSH2_SFTP *sftp, const char *path,
- unsigned int path_len, long mode)
- {
- int rc;
- if(!sftp)
- return LIBSSH2_ERROR_BAD_USE;
- 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,
- unsigned int path_len)
- {
- LIBSSH2_CHANNEL *channel = sftp->channel;
- LIBSSH2_SESSION *session = channel->session;
- size_t data_len;
- int retcode;
- /* 13 = packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */
- ssize_t packet_len = path_len + 13;
- unsigned char *s, *data;
- int rc;
-
- if(sftp->rmdir_state == libssh2_NB_state_idle) {
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "Removing directory: %s",
- path);
- s = sftp->rmdir_packet = LIBSSH2_ALLOC(session, packet_len);
- if(!sftp->rmdir_packet) {
- return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
- "Unable to allocate memory for FXP_RMDIR "
- "packet");
- }
-
- _libssh2_store_u32(&s, packet_len - 4);
- *(s++) = SSH_FXP_RMDIR;
- sftp->rmdir_request_id = sftp->request_id++;
- _libssh2_store_u32(&s, sftp->rmdir_request_id);
- _libssh2_store_str(&s, path, path_len);
-
- sftp->rmdir_state = libssh2_NB_state_created;
- }
-
- if(sftp->rmdir_state == libssh2_NB_state_created) {
- rc = _libssh2_channel_write(channel, 0, sftp->rmdir_packet,
- packet_len);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- return rc;
- }
- else if(packet_len != rc) {
- LIBSSH2_FREE(session, sftp->rmdir_packet);
- sftp->rmdir_packet = NULL;
- sftp->rmdir_state = libssh2_NB_state_idle;
- return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
- "Unable to send FXP_RMDIR command");
- }
- LIBSSH2_FREE(session, sftp->rmdir_packet);
- sftp->rmdir_packet = NULL;
-
- sftp->rmdir_state = libssh2_NB_state_sent;
- }
-
- rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
- sftp->rmdir_request_id, &data, &data_len, 9);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- return rc;
- }
- else if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
- if(data_len > 0) {
- LIBSSH2_FREE(session, data);
- }
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "SFTP rmdir packet too short");
- }
- else if(rc) {
- sftp->rmdir_state = libssh2_NB_state_idle;
- return _libssh2_error(session, rc,
- "Error waiting for FXP STATUS");
- }
-
- sftp->rmdir_state = libssh2_NB_state_idle;
-
- retcode = _libssh2_ntohu32(data + 5);
- LIBSSH2_FREE(session, data);
-
- if(retcode == LIBSSH2_FX_OK) {
- return 0;
- }
- else {
- sftp->last_errno = retcode;
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "SFTP Protocol Error");
- }
- }
-
- /* libssh2_sftp_rmdir_ex
- * Remove a directory
- */
- LIBSSH2_API int
- libssh2_sftp_rmdir_ex(LIBSSH2_SFTP *sftp, const char *path,
- unsigned int path_len)
- {
- int rc;
- if(!sftp)
- return LIBSSH2_ERROR_BAD_USE;
- 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,
- unsigned int path_len, int stat_type,
- LIBSSH2_SFTP_ATTRIBUTES * attrs)
- {
- LIBSSH2_CHANNEL *channel = sftp->channel;
- LIBSSH2_SESSION *session = channel->session;
- size_t data_len;
- /* 13 = packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */
- ssize_t packet_len =
- path_len + 13 +
- ((stat_type ==
- LIBSSH2_SFTP_SETSTAT) ? sftp_attrsize(attrs->flags) : 0);
- unsigned char *s, *data;
- static const unsigned char stat_responses[2] =
- { SSH_FXP_ATTRS, SSH_FXP_STATUS };
- int rc;
-
- if(sftp->stat_state == libssh2_NB_state_idle) {
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "%s %s",
- (stat_type == LIBSSH2_SFTP_SETSTAT) ? "Set-statting" :
- (stat_type ==
- LIBSSH2_SFTP_LSTAT ? "LStatting" : "Statting"), path);
- s = sftp->stat_packet = LIBSSH2_ALLOC(session, packet_len);
- if(!sftp->stat_packet) {
- return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
- "Unable to allocate memory for FXP_*STAT "
- "packet");
- }
-
- _libssh2_store_u32(&s, packet_len - 4);
-
- switch(stat_type) {
- 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;
- }
- sftp->stat_request_id = sftp->request_id++;
- _libssh2_store_u32(&s, sftp->stat_request_id);
- _libssh2_store_str(&s, path, path_len);
-
- if(stat_type == LIBSSH2_SFTP_SETSTAT)
- s += sftp_attr2bin(s, attrs);
-
- sftp->stat_state = libssh2_NB_state_created;
- }
-
- if(sftp->stat_state == libssh2_NB_state_created) {
- rc = _libssh2_channel_write(channel, 0, sftp->stat_packet, packet_len);
- if(rc == LIBSSH2_ERROR_EAGAIN) {
- return rc;
- }
- else if(packet_len != rc) {
- LIBSSH2_FREE(session, sftp->stat_packet);
- sftp->stat_packet = NULL;
- sftp->stat_state = libssh2_NB_state_idle;
- return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
- "Unable to send STAT/LSTAT/SETSTAT command");
- }
- LIBSSH2_FREE(session, sftp->stat_packet);
- sftp->stat_packet = NULL;
-
- sftp->stat_state = libssh2_NB_state_sent;
- }
-
- rc = sftp_packet_requirev(sftp, 2, stat_responses,
- sftp->stat_request_id, &data, &data_len, 9);
- if(rc == LIBSSH2_ERROR_EAGAIN)
- return rc;
- else if(rc == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
- if(data_len > 0) {
- LIBSSH2_FREE(session, data);
- }
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "SFTP stat packet too short");
- }
- else if(rc) {
- sftp->stat_state = libssh2_NB_state_idle;
- return _libssh2_error(session, rc,
- "Timeout waiting for status message");
- }
-
- sftp->stat_state = libssh2_NB_state_idle;
-
- if(data[0] == SSH_FXP_STATUS) {
- int retcode;
-
- retcode = _libssh2_ntohu32(data + 5);
- LIBSSH2_FREE(session, data);
- if(retcode == LIBSSH2_FX_OK) {
- memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
- return 0;
- }
- else {
- sftp->last_errno = retcode;
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "SFTP Protocol Error");
- }
- }
-
- memset(attrs, 0, sizeof(LIBSSH2_SFTP_ATTRIBUTES));
- if(sftp_bin2attr(attrs, data + 5, data_len - 5) < 0) {
- LIBSSH2_FREE(session, data);
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "Attributes too short in SFTP fstat");
- }
-
- LIBSSH2_FREE(session, data);
-
- return 0;
- }
-
- /* libssh2_sftp_stat_ex
- * Stat a file or symbolic link
- */
- LIBSSH2_API int
- libssh2_sftp_stat_ex(LIBSSH2_SFTP *sftp, const char *path,
- unsigned int path_len, int stat_type,
- LIBSSH2_SFTP_ATTRIBUTES *attrs)
- {
- int rc;
- if(!sftp)
- return LIBSSH2_ERROR_BAD_USE;
- 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,
- unsigned int path_len, char *target,
- unsigned int target_len, int link_type)
- {
- LIBSSH2_CHANNEL *channel = sftp->channel;
- LIBSSH2_SESSION *session = channel->session;
- size_t data_len, link_len;
- /* 13 = packet_len(4) + packet_type(1) + request_id(4) + path_len(4) */
- ssize_t packet_len =
- path_len + 13 +
- ((link_type == LIBSSH2_SFTP_SYMLINK) ? (4 + target_len) : 0);
- unsigned char *s, *data;
- static const unsigned char link_responses[2] =
- { SSH_FXP_NAME, SSH_FXP_STATUS };
- int retcode;
-
- if((sftp->version < 3) && (link_type != LIBSSH2_SFTP_REALPATH)) {
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "Server does not support SYMLINK or READLINK");
- }
-
- if(sftp->symlink_state == libssh2_NB_state_idle) {
- s = sftp->symlink_packet = LIBSSH2_ALLOC(session, packet_len);
- if(!sftp->symlink_packet) {
- return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
- "Unable to allocate memory for "
- "SYMLINK/READLINK/REALPATH packet");
- }
-
- _libssh2_debug(session, LIBSSH2_TRACE_SFTP, "%s %s on %s",
- (link_type ==
- LIBSSH2_SFTP_SYMLINK) ? "Creating" : "Reading",
- (link_type ==
- LIBSSH2_SFTP_REALPATH) ? "realpath" : "symlink", path);
-
- _libssh2_store_u32(&s, packet_len - 4);
-
- switch(link_type) {
- 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;
- }
- sftp->symlink_request_id = sftp->request_id++;
- _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);
-
- sftp->symlink_state = libssh2_NB_state_created;
- }
-
- if(sftp->symlink_state == libssh2_NB_state_created) {
- ssize_t rc = _libssh2_channel_write(channel, 0, sftp->symlink_packet,
- packet_len);
- if(rc == LIBSSH2_ERROR_EAGAIN)
- return rc;
- else if(packet_len != rc) {
- LIBSSH2_FREE(session, sftp->symlink_packet);
- sftp->symlink_packet = NULL;
- sftp->symlink_state = libssh2_NB_state_idle;
- return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
- "Unable to send SYMLINK/READLINK command");
- }
- LIBSSH2_FREE(session, sftp->symlink_packet);
- sftp->symlink_packet = NULL;
-
- sftp->symlink_state = libssh2_NB_state_sent;
- }
-
- retcode = sftp_packet_requirev(sftp, 2, link_responses,
- sftp->symlink_request_id, &data,
- &data_len, 9);
- if(retcode == LIBSSH2_ERROR_EAGAIN)
- return retcode;
- else if(retcode == LIBSSH2_ERROR_BUFFER_TOO_SMALL) {
- if(data_len > 0) {
- LIBSSH2_FREE(session, data);
- }
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "SFTP symlink packet too short");
- }
- else if(retcode) {
- sftp->symlink_state = libssh2_NB_state_idle;
- return _libssh2_error(session, retcode,
- "Error waiting for status message");
- }
-
- sftp->symlink_state = libssh2_NB_state_idle;
-
- if(data[0] == SSH_FXP_STATUS) {
- retcode = _libssh2_ntohu32(data + 5);
- LIBSSH2_FREE(session, data);
- if(retcode == LIBSSH2_FX_OK)
- return LIBSSH2_ERROR_NONE;
- else {
- sftp->last_errno = retcode;
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "SFTP Protocol Error");
- }
- }
-
- if(_libssh2_ntohu32(data + 5) < 1) {
- LIBSSH2_FREE(session, data);
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "Invalid READLINK/REALPATH response, "
- "no name entries");
- }
-
- if(data_len < 13) {
- if(data_len > 0) {
- LIBSSH2_FREE(session, data);
- }
- return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
- "SFTP stat packet too short");
- }
-
- /* this reads a u32 and stores it into a signed 32bit value */
- link_len = _libssh2_ntohu32(data + 9);
- if(link_len < target_len) {
- memcpy(target, data + 13, link_len);
- target[link_len] = 0;
- retcode = (int)link_len;
- }
- else
- retcode = LIBSSH2_ERROR_BUFFER_TOO_SMALL;
- LIBSSH2_FREE(session, data);
-
- return retcode;
- }
-
- /* 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;
- if(!sftp)
- return LIBSSH2_ERROR_BAD_USE;
- BLOCK_ADJUST(rc, sftp->channel->session,
- sftp_symlink(sftp, path, path_len, target, target_len,
- link_type));
- return rc;
- }
-
- /* libssh2_sftp_last_error
- * Returns the last error code reported by SFTP
- */
- LIBSSH2_API unsigned long
- libssh2_sftp_last_error(LIBSSH2_SFTP *sftp)
- {
- if(!sftp)
- return 0;
-
- return sftp->last_errno;
- }
-
- /* 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;
- }
|