From 19e62a78a677c2617d663cf5df0b2db924842d58 Mon Sep 17 00:00:00 2001 From: Abdelrahman Youssef Date: Wed, 13 Mar 2024 16:02:18 +0200 Subject: [PATCH] sftp: Added lsetstat extension Signed-off-by: Abdelrahman Youssef Reviewed-by: Jakub Jelen --- include/libssh/sftp.h | 23 +++++++++++ src/sftp.c | 93 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/include/libssh/sftp.h b/include/libssh/sftp.h index 4bda8c8f..cf4458c3 100644 --- a/include/libssh/sftp.h +++ b/include/libssh/sftp.h @@ -948,6 +948,29 @@ LIBSSH_API int sftp_rename(sftp_session sftp, const char *original, const char */ LIBSSH_API int sftp_setstat(sftp_session sftp, const char *file, sftp_attributes attr); +/** + * @brief This request is like setstat (excluding mode and size) but sets file + * attributes on symlinks themselves. + * + * Note, that this function can only set time values using 32 bit values due to + * the restrictions in the SFTP protocol version 3 implemented by libssh. + * The support for 64 bit time values was introduced in SFTP version 5, which is + * not implemented by libssh nor any major SFTP servers. + * + * @param sftp The sftp session handle. + * + * @param file The symbolic link which attributes should be changed. + * + * @param attr The file attributes structure with the attributes set + * which should be changed. + * + * @return 0 on success, < 0 on error with ssh and sftp error set. + * + * @see sftp_get_error() + */ +LIBSSH_API int +sftp_lsetstat(sftp_session sftp, const char *file, sftp_attributes attr); + /** * @brief Change the file owner and group * diff --git a/src/sftp.c b/src/sftp.c index a7ed5a75..154de480 100644 --- a/src/sftp.c +++ b/src/sftp.c @@ -1893,6 +1893,10 @@ int sftp_setstat(sftp_session sftp, const char *file, sftp_attributes attr) sftp_status_message status = NULL; int rc; + if (sftp == NULL || file == NULL || attr == NULL) { + return -1; + } + buffer = ssh_buffer_new(); if (buffer == NULL) { ssh_set_error_oom(sftp->session); @@ -1967,6 +1971,95 @@ int sftp_setstat(sftp_session sftp, const char *file, sftp_attributes attr) return -1; } +int +sftp_lsetstat(sftp_session sftp, const char *file, sftp_attributes attr) +{ + uint32_t id; + ssh_buffer buffer = NULL; + sftp_message msg = NULL; + sftp_status_message status = NULL; + const char *extension_name = "lsetstat@openssh.com"; + int rc; + + if (sftp == NULL || file == NULL || attr == NULL) { + return -1; + } + + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(sftp->session); + sftp_set_error(sftp, SSH_FX_FAILURE); + return -1; + } + + id = sftp_get_new_id(sftp); + + rc = ssh_buffer_pack(buffer, "dss", id, extension_name, file); + if (rc != SSH_OK) { + ssh_set_error_oom(sftp->session); + SSH_BUFFER_FREE(buffer); + sftp_set_error(sftp, SSH_FX_FAILURE); + return -1; + } + + rc = buffer_add_attributes(buffer, attr); + if (rc != 0) { + ssh_set_error_oom(sftp->session); + SSH_BUFFER_FREE(buffer); + sftp_set_error(sftp, SSH_FX_FAILURE); + return -1; + } + + rc = sftp_packet_write(sftp, SSH_FXP_EXTENDED, buffer); + SSH_BUFFER_FREE(buffer); + if (rc < 0) { + return -1; + } + + while (msg == NULL) { + if (sftp_read_and_dispatch(sftp) < 0) { + return -1; + } + msg = sftp_dequeue(sftp, id); + } + + /* By specification, this command only returns SSH_FXP_STATUS */ + if (msg->packet_type == SSH_FXP_STATUS) { + status = parse_status_msg(msg); + sftp_message_free(msg); + if (status == NULL) { + return -1; + } + sftp_set_error(sftp, status->status); + switch (status->status) { + case SSH_FX_OK: + status_msg_free(status); + return 0; + default: + break; + } + /* + * The status should be SSH_FX_OK if the command was successful, if it + * didn't, then there was an error + */ + ssh_set_error(sftp->session, + SSH_REQUEST_DENIED, + "SFTP server: %s", + status->errormsg); + status_msg_free(status); + return -1; + } else { + ssh_set_error(sftp->session, + SSH_FATAL, + "Received message %d when attempting to lsetstat", + msg->packet_type); + sftp_message_free(msg); + sftp_set_error(sftp, SSH_FX_BAD_MESSAGE); + } + + return -1; +} + /* Change the file owner and group */ int sftp_chown(sftp_session sftp, const char *file, uid_t owner, gid_t group) { struct sftp_attributes_struct attr;