diff options
Diffstat (limited to 'libs/libssh2/src/sftp.c')
-rw-r--r-- | libs/libssh2/src/sftp.c | 187 |
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 */ |