aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/libssh/sftp.h13
-rw-r--r--libssh/sftp.c83
2 files changed, 96 insertions, 0 deletions
diff --git a/include/libssh/sftp.h b/include/libssh/sftp.h
index 83c6f1de..8aede051 100644
--- a/include/libssh/sftp.h
+++ b/include/libssh/sftp.h
@@ -593,6 +593,19 @@ int sftp_chmod(SFTP_SESSION *sftp, const char *file, mode_t mode);
int sftp_utimes(SFTP_SESSION *sftp, const char *file, const struct timeval *times);
/**
+ * @brief Create a symbolic link.
+ *
+ * @param sftp The sftp session handle.
+ *
+ * @param target Specifies the target of the symlink.
+ *
+ * @param dest Specifies the path name of the symlink to be created.
+ *
+ * @return 0 on success, < 0 on error with ssh and sftp error set.
+ */
+int sftp_symlink(SFTP_SESSION *sftp, const char *target, const char *dest);
+
+/**
* @brief Canonicalize a sftp path.
*
* @param sftp The sftp session handle.
diff --git a/libssh/sftp.c b/libssh/sftp.c
index 4aa63c97..f0007d8c 100644
--- a/libssh/sftp.c
+++ b/libssh/sftp.c
@@ -2212,6 +2212,89 @@ int sftp_utimes(SFTP_SESSION *sftp, const char *file,
return sftp_setstat(sftp, file, &attr);
}
+int sftp_symlink(SFTP_SESSION *sftp, const char *target, const char *dest) {
+ STATUS_MESSAGE *status = NULL;
+ SFTP_MESSAGE *msg = NULL;
+ STRING *target_s;
+ STRING *dest_s;
+ BUFFER *buffer;
+ u32 id;
+
+ if (sftp == NULL || target == NULL || dest == NULL) {
+ return -1;
+ }
+
+ buffer = buffer_new();
+ if (buffer == NULL) {
+ return -1;
+ }
+
+ target_s = string_from_char(target);
+ if (target_s == NULL) {
+ buffer_free(buffer);
+ return -1;
+ }
+
+ dest_s = string_from_char(dest);
+ if (dest_s == NULL) {
+ string_free(target_s);
+ buffer_free(buffer);
+ return -1;
+ }
+
+ id = sftp_get_new_id(sftp);
+ if (buffer_add_u32(buffer, id) < 0 ||
+ buffer_add_ssh_string(buffer, target_s) < 0 ||
+ buffer_add_ssh_string(buffer, dest_s) < 0 ||
+ sftp_packet_write(sftp, SSH_FXP_SYMLINK, buffer) < 0) {
+ buffer_free(buffer);
+ string_free(dest_s);
+ string_free(target_s);
+ return -1;
+ }
+ buffer_free(buffer);
+ string_free(dest_s);
+ string_free(target_s);
+
+ 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 set stats", msg->packet_type);
+ sftp_message_free(msg);
+ }
+
+ return -1;
+}
+
/* another code written by Nick */
char *sftp_canonicalize_path(SFTP_SESSION *sftp, const char *path) {
STATUS_MESSAGE *status = NULL;