summaryrefslogtreecommitdiff
path: root/libs/libssh2/src/sftp.c
diff options
context:
space:
mode:
Diffstat (limited to 'libs/libssh2/src/sftp.c')
-rw-r--r--libs/libssh2/src/sftp.c187
1 files changed, 160 insertions, 27 deletions
diff --git a/libs/libssh2/src/sftp.c b/libs/libssh2/src/sftp.c
index f8f1323e02..6ede311109 100644
--- a/libs/libssh2/src/sftp.c
+++ b/libs/libssh2/src/sftp.c
@@ -47,6 +47,7 @@
#include "sftp.h"
#include <assert.h>
+#include <stdlib.h> /* strtol() */
/* This release of libssh2 implements Version 5 with automatic downgrade
* based on server's declaration
@@ -102,26 +103,6 @@ static int sftp_packet_ask(LIBSSH2_SFTP *sftp, unsigned char packet_type,
size_t *data_len);
static void sftp_packet_flush(LIBSSH2_SFTP *sftp);
-/* _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.
*
@@ -381,8 +362,8 @@ window_adjust:
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:
+ sftp->packet_state = (rc == LIBSSH2_ERROR_EAGAIN) ?
+ libssh2_NB_state_sent :
libssh2_NB_state_idle;
if(rc == LIBSSH2_ERROR_EAGAIN)
@@ -988,20 +969,42 @@ static LIBSSH2_SFTP *sftp_init(LIBSSH2_SESSION *session)
sftp_handle->version));
while(buf.dataptr < endp) {
unsigned char *extname, *extdata;
+ size_t extname_len, extdata_len;
+ uint32_t extversion = 0;
- if(_libssh2_get_string(&buf, &extname, NULL)) {
+ if(_libssh2_get_string(&buf, &extname, &extname_len)) {
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)) {
+ if(_libssh2_get_string(&buf, &extdata, &extdata_len)) {
LIBSSH2_FREE(session, data);
_libssh2_error(session, LIBSSH2_ERROR_BUFFER_TOO_SMALL,
"Data too short when extracting extdata");
goto sftp_init_error;
}
+
+ if(extdata_len > 0) {
+ char *extversion_str;
+ extversion_str = (char *)LIBSSH2_ALLOC(session, extdata_len + 1);
+ if(!extversion_str) {
+ _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
+ "Unable to allocate memory for SSH_FXP_VERSION "
+ "packet");
+ goto sftp_init_error;
+ }
+ memcpy(extversion_str, extdata, extdata_len);
+ extversion_str[extdata_len] = '\0';
+ extversion = (uint32_t)strtol(extversion_str, NULL, 10);
+ LIBSSH2_FREE(session, extversion_str);
+ }
+ if(extname_len == 24
+ && strncmp("posix-rename@openssh.com", (char *)extname, 24) == 0) {
+ sftp_handle->posix_rename_version = extversion;
+ }
+
}
LIBSSH2_FREE(session, data);
@@ -1163,7 +1166,7 @@ sftp_open(LIBSSH2_SFTP *sftp, const char *filename,
/* packet_len(4) + packet_type(1) + request_id(4) + filename_len(4) +
flags(4) */
sftp->open_packet_len = (uint32_t)(filename_len + 13 +
- (open_file? (4 + sftp_attrsize(attrs.flags)) : 0));
+ (open_file ? (4 + sftp_attrsize(attrs.flags)) : 0));
/* surprise! this starts out with nothing sent */
sftp->open_packet_sent = 0;
@@ -1180,7 +1183,7 @@ sftp_open(LIBSSH2_SFTP *sftp, const char *filename,
LIBSSH2_SFTP_ATTR_PFILETYPE_DIR);
_libssh2_store_u32(&s, sftp->open_packet_len - 4);
- *(s++) = open_file? SSH_FXP_OPEN : SSH_FXP_OPENDIR;
+ *(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);
@@ -1191,7 +1194,7 @@ sftp_open(LIBSSH2_SFTP *sftp, const char *filename,
}
_libssh2_debug((session, LIBSSH2_TRACE_SFTP, "Sending %s open request",
- open_file? "file" : "directory"));
+ open_file ? "file" : "directory"));
sftp->open_state = libssh2_NB_state_created;
}
@@ -3015,6 +3018,136 @@ libssh2_sftp_rename_ex(LIBSSH2_SFTP *sftp, const char *source_filename,
return rc;
}
+static int
+sftp_posix_rename(LIBSSH2_SFTP *sftp, const char *source_filename,
+ size_t source_filename_len,
+ const char *dest_filename,
+ size_t dest_filename_len)
+{
+ LIBSSH2_CHANNEL *channel = sftp->channel;
+ LIBSSH2_SESSION *session = channel->session;
+ uint32_t packet_len;
+ size_t data_len = 0;
+ unsigned char *packet, *s, *data = NULL;
+ ssize_t rc;
+ uint32_t retcode;
+
+ if(sftp->posix_rename_version != 1) {
+ return _libssh2_error(session, LIBSSH2_FX_OP_UNSUPPORTED,
+ "Server does not support"
+ "posix-rename@openssh.com");
+ }
+
+ if(source_filename_len > UINT32_MAX ||
+ dest_filename_len > UINT32_MAX ||
+ 45 + source_filename_len + dest_filename_len > UINT32_MAX) {
+ return _libssh2_error(session, LIBSSH2_ERROR_OUT_OF_BOUNDARY,
+ "Input too large"
+ "posix-rename@openssh.com");
+ }
+
+ packet_len = (uint32_t)(45 + source_filename_len + dest_filename_len);
+
+ /* 45 = packet_len(4) + packet_type(1) + request_id(4) +
+ string_len(4) + strlen("posix-rename@openssh.com")(24) +
+ oldpath_len(4) + source_filename_len +
+ newpath_len(4) + dest_filename_len */
+
+ if(sftp->posix_rename_state == libssh2_NB_state_idle) {
+ _libssh2_debug((session, LIBSSH2_TRACE_SFTP,
+ "Issuing posix_rename 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->posix_rename_request_id = sftp->request_id++;
+ _libssh2_store_u32(&s, sftp->posix_rename_request_id);
+ _libssh2_store_str(&s, "posix-rename@openssh.com", 24);
+ _libssh2_store_str(&s, source_filename, source_filename_len);
+ _libssh2_store_str(&s, dest_filename, dest_filename_len);
+
+ sftp->posix_rename_state = libssh2_NB_state_created;
+ }
+ else {
+ packet = sftp->posix_rename_packet;
+ }
+
+ if(sftp->posix_rename_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->posix_rename_packet = packet;
+ return LIBSSH2_ERROR_EAGAIN;
+ }
+
+ LIBSSH2_FREE(session, packet);
+ sftp->posix_rename_packet = NULL;
+
+ if(rc < 0) {
+ sftp->posix_rename_state = libssh2_NB_state_idle;
+ return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
+ "_libssh2_channel_write() failed");
+ }
+ sftp->posix_rename_state = libssh2_NB_state_sent;
+ }
+
+ rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
+ sftp->posix_rename_request_id,
+ &data, &data_len, 9);
+ if(rc == LIBSSH2_ERROR_EAGAIN) {
+ return (int)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 posix_rename packet too short");
+ }
+ else if(rc) {
+ sftp->posix_rename_state = libssh2_NB_state_idle;
+ return (int)_libssh2_error(session, (int)rc,
+ "Error waiting for FXP EXTENDED REPLY");
+ }
+
+ sftp->posix_rename_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,
+ "posix_rename failed");
+ }
+
+ return 0;
+}
+
+/* libssh2_sftp_posix_rename_ex
+ * Rename a file on the remote server using the posix-rename@openssh.com
+ * extension.
+ */
+LIBSSH2_API int
+libssh2_sftp_posix_rename_ex(LIBSSH2_SFTP *sftp, const char *source_filename,
+ size_t source_filename_len,
+ const char *dest_filename,
+ size_t dest_filename_len)
+{
+ int rc;
+ if(!sftp)
+ return LIBSSH2_ERROR_BAD_USE;
+ BLOCK_ADJUST(rc, sftp->channel->session,
+ sftp_posix_rename(sftp, source_filename, source_filename_len,
+ dest_filename, dest_filename_len));
+ return rc;
+}
+
/* sftp_fstatvfs
* Get file system statistics
*/