diff --git a/libssh2/include/libssh2_sftp.h b/libssh2/include/libssh2_sftp.h index 476ea87..4adce8e 100644 --- a/libssh2/include/libssh2_sftp.h +++ b/libssh2/include/libssh2_sftp.h @@ -251,6 +251,8 @@ LIBSSH2_API int libssh2_sftp_readdir_ex(LIBSSH2_SFTP_HANDLE *handle, \ LIBSSH2_API ssize_t libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle, const char *buffer, size_t count); +LIBSSH2_API int libssh2_sftp_punchhole(LIBSSH2_SFTP_HANDLE *handle, + off_t off, off_t len); LIBSSH2_API int libssh2_sftp_fsync(LIBSSH2_SFTP_HANDLE *handle); LIBSSH2_API int libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle); diff --git a/libssh2/src/sftp.c b/libssh2/src/sftp.c index 378974c..be6b7ca 100644 --- a/libssh2/src/sftp.c +++ b/libssh2/src/sftp.c @@ -2336,6 +2336,111 @@ libssh2_sftp_fsync(LIBSSH2_SFTP_HANDLE *hnd) } +static int sftp_punchhole(LIBSSH2_SFTP_HANDLE *handle, off_t off, off_t len) +{ + LIBSSH2_SFTP *sftp = handle->sftp; + LIBSSH2_CHANNEL *channel = sftp->channel; + LIBSSH2_SESSION *session = channel->session; + /* 55 = packet_len(4) + packet_type(1) + request_id(4) + + string_len(4) + strlen("punchhole@funkthat.com")(22) + handle_len(4) + + off_len(8) + len_len(8) */ + uint32_t packet_len = handle->handle_len + 55; + size_t data_len; + unsigned char *packet, *s, *data; + ssize_t rc; + uint32_t retcode; + + if(sftp->punchhole_state == libssh2_NB_state_idle) { + _libssh2_debug(session, LIBSSH2_TRACE_SFTP, + "Issuing punchhole 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->punchhole_request_id = sftp->request_id++; + _libssh2_store_u32(&s, sftp->punchhole_request_id); + _libssh2_store_str(&s, "punchhole@funkthat.com", 22); + _libssh2_store_str(&s, handle->handle, handle->handle_len); + _libssh2_store_u64(&s, off); + _libssh2_store_u64(&s, len); + + sftp->punchhole_state = libssh2_NB_state_created; + } + else { + packet = sftp->punchhole_packet; + } + + if(sftp->punchhole_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->punchhole_packet = packet; + return LIBSSH2_ERROR_EAGAIN; + } + + LIBSSH2_FREE(session, packet); + sftp->punchhole_packet = NULL; + + if(rc < 0) { + sftp->punchhole_state = libssh2_NB_state_idle; + return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND, + "_libssh2_channel_write() failed"); + } + sftp->punchhole_state = libssh2_NB_state_sent; + } + + rc = sftp_packet_require(sftp, SSH_FXP_STATUS, + sftp->punchhole_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 punchhole packet too short"); + } + else if(rc) { + sftp->punchhole_state = libssh2_NB_state_idle; + return _libssh2_error(session, rc, + "Error waiting for FXP EXTENDED REPLY"); + } + + sftp->punchhole_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, + "punchhole failed"); + } + + return 0; +} + +/* libssh2_sftp_punchhole + * Punch hole in remote file. + */ +LIBSSH2_API int +libssh2_sftp_punchhole(LIBSSH2_SFTP_HANDLE *hnd, off_t off, off_t len) +{ + int rc; + if(!hnd) + return LIBSSH2_ERROR_BAD_USE; + BLOCK_ADJUST(rc, hnd->sftp->channel->session, + sftp_punchhole(hnd, off, len)); + return rc; +} + + /* * sftp_fstat * diff --git a/libssh2/src/sftp.h b/libssh2/src/sftp.h index 129b8f0..83de48e 100644 --- a/libssh2/src/sftp.h +++ b/libssh2/src/sftp.h @@ -183,6 +183,11 @@ struct _LIBSSH2_SFTP unsigned char *fsync_packet; uint32_t fsync_request_id; + /* State variables used in sftp_punchhole() */ + libssh2_nonblocking_states punchhole_state; + unsigned char *punchhole_packet; + uint32_t punchhole_request_id; + /* State variables used in libssh2_sftp_readdir() */ libssh2_nonblocking_states readdir_state; unsigned char *readdir_packet;