fe8b55f
From 6e0d757f24a45252c4cae9ea09732eda2562c767 Mon Sep 17 00:00:00 2001
fe8b55f
From: "Richard W.M. Jones" <rjones@redhat.com>
fe8b55f
Date: Tue, 9 Apr 2013 11:42:09 +0200
fe8b55f
Subject: [PATCH 3/3] sftp: Add support for fsync (OpenSSH extension).
fe8b55f
fe8b55f
The new libssh2_sftp_fsync API causes data and metadata in the
fe8b55f
currently open file to be committed to disk at the server.
fe8b55f
fe8b55f
This is an OpenSSH extension to the SFTP protocol.  See:
fe8b55f
fe8b55f
https://bugzilla.mindrot.org/show_bug.cgi?id=1798
fe8b55f
---
fe8b55f
 docs/Makefile.am          |  1 +
fe8b55f
 docs/libssh2_sftp_fsync.3 | 39 +++++++++++++++++++
fe8b55f
 include/libssh2_sftp.h    |  1 +
fe8b55f
 src/sftp.c                | 97 +++++++++++++++++++++++++++++++++++++++++++++++
fe8b55f
 src/sftp.h                |  5 +++
fe8b55f
 5 files changed, 143 insertions(+)
fe8b55f
 create mode 100644 docs/libssh2_sftp_fsync.3
fe8b55f
fe8b55f
diff --git a/docs/Makefile.am b/docs/Makefile.am
fe8b55f
index e4cf487..e6ab394 100644
fe8b55f
--- a/docs/Makefile.am
fe8b55f
+++ b/docs/Makefile.am
fe8b55f
@@ -120,6 +120,7 @@ dist_man_MANS = \
fe8b55f
 	libssh2_sftp_fstat.3 \
fe8b55f
 	libssh2_sftp_fstat_ex.3 \
fe8b55f
 	libssh2_sftp_fstatvfs.3 \
fe8b55f
+	libssh2_sftp_fsync.3 \
fe8b55f
 	libssh2_sftp_get_channel.3 \
fe8b55f
 	libssh2_sftp_init.3 \
fe8b55f
 	libssh2_sftp_last_error.3 \
fe8b55f
diff --git a/docs/libssh2_sftp_fsync.3 b/docs/libssh2_sftp_fsync.3
fe8b55f
new file mode 100644
fe8b55f
index 0000000..646760a
fe8b55f
--- /dev/null
fe8b55f
+++ b/docs/libssh2_sftp_fsync.3
fe8b55f
@@ -0,0 +1,39 @@
fe8b55f
+.TH libssh2_sftp_fsync 3 "8 Apr 2013" "libssh2 1.4.4" "libssh2 manual"
fe8b55f
+.SH NAME
fe8b55f
+libssh2_sftp_fsync - synchronize file to disk
fe8b55f
+.SH SYNOPSIS
fe8b55f
+.nf
fe8b55f
+#include <libssh2.h>
fe8b55f
+#include <libssh2_sftp.h>
fe8b55f
+
fe8b55f
+int
fe8b55f
+libssh2_sftp_fsync(LIBSSH2_SFTP_HANDLE *handle)
fe8b55f
+.fi
fe8b55f
+.SH DESCRIPTION
fe8b55f
+This function causes the remote server to synchronize the file
fe8b55f
+data and metadata to disk (like fsync(2)).
fe8b55f
+
fe8b55f
+For this to work requires fsync@openssh.com support on the server.
fe8b55f
+
fe8b55f
+\fIhandle\fP - SFTP File Handle as returned by
fe8b55f
+.BR libssh2_sftp_open_ex(3)
fe8b55f
+
fe8b55f
+.SH RETURN VALUE
fe8b55f
+Returns 0 on success or negative on failure. If used in non-blocking mode, it
fe8b55f
+returns LIBSSH2_ERROR_EAGAIN when it would otherwise block. While
fe8b55f
+LIBSSH2_ERROR_EAGAIN is a negative number, it isn't really a failure per se.
fe8b55f
+.SH ERRORS
fe8b55f
+\fILIBSSH2_ERROR_ALLOC\fP -  An internal memory allocation call failed.
fe8b55f
+
fe8b55f
+\fILIBSSH2_ERROR_SOCKET_SEND\fP - Unable to send data on socket.
fe8b55f
+
fe8b55f
+\fILIBSSH2_ERROR_SFTP_PROTOCOL\fP - An invalid SFTP protocol response
fe8b55f
+was received on the socket, or an SFTP operation caused an errorcode
fe8b55f
+to be returned by the server.  In particular, this can be returned if
fe8b55f
+the SSH server does not support the fsync operation: the SFTP subcode
fe8b55f
+\fILIBSSH2_FX_OP_UNSUPPORTED\fP will be returned in this case.
fe8b55f
+
fe8b55f
+.SH AVAILABILITY
fe8b55f
+Added in libssh2 1.4.4 and OpenSSH 6.3.
fe8b55f
+.SH SEE ALSO
fe8b55f
+.BR fsync(2)
fe8b55f
diff --git a/include/libssh2_sftp.h b/include/libssh2_sftp.h
fe8b55f
index 74884fb..677faf2 100644
fe8b55f
--- a/include/libssh2_sftp.h
fe8b55f
+++ b/include/libssh2_sftp.h
fe8b55f
@@ -247,6 +247,7 @@ LIBSSH2_API int libssh2_sftp_readdir_ex(LIBSSH2_SFTP_HANDLE *handle, \
fe8b55f
 
fe8b55f
 LIBSSH2_API ssize_t libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *handle,
fe8b55f
                                        const char *buffer, size_t count);
fe8b55f
+LIBSSH2_API int libssh2_sftp_fsync(LIBSSH2_SFTP_HANDLE *handle);
fe8b55f
 
fe8b55f
 LIBSSH2_API int libssh2_sftp_close_handle(LIBSSH2_SFTP_HANDLE *handle);
fe8b55f
 #define libssh2_sftp_close(handle) libssh2_sftp_close_handle(handle)
fe8b55f
diff --git a/src/sftp.c b/src/sftp.c
fe8b55f
index 65fa77a..01017fd 100644
fe8b55f
--- a/src/sftp.c
fe8b55f
+++ b/src/sftp.c
fe8b55f
@@ -986,6 +986,10 @@ sftp_shutdown(LIBSSH2_SFTP *sftp)
fe8b55f
         LIBSSH2_FREE(session, sftp->symlink_packet);
fe8b55f
         sftp->symlink_packet = NULL;
fe8b55f
     }
fe8b55f
+    if (sftp->fsync_packet) {
fe8b55f
+        LIBSSH2_FREE(session, sftp->fsync_packet);
fe8b55f
+        sftp->fsync_packet = NULL;
fe8b55f
+    }
fe8b55f
 
fe8b55f
     sftp_packet_flush(sftp);
fe8b55f
 
fe8b55f
@@ -2014,6 +2018,99 @@ libssh2_sftp_write(LIBSSH2_SFTP_HANDLE *hnd, const char *buffer,
fe8b55f
 
fe8b55f
 }
fe8b55f
 
fe8b55f
+static int sftp_fsync(LIBSSH2_SFTP_HANDLE *handle)
fe8b55f
+{
fe8b55f
+    LIBSSH2_SFTP *sftp = handle->sftp;
fe8b55f
+    LIBSSH2_CHANNEL *channel = sftp->channel;
fe8b55f
+    LIBSSH2_SESSION *session = channel->session;
fe8b55f
+    /* 34 = packet_len(4) + packet_type(1) + request_id(4) +
fe8b55f
+       string_len(4) + strlen("fsync@openssh.com")(17) + handle_len(4) */
fe8b55f
+    uint32_t packet_len = handle->handle_len + 34;
fe8b55f
+    size_t data_len;
fe8b55f
+    unsigned char *packet, *s, *data;
fe8b55f
+    ssize_t rc;
fe8b55f
+    uint32_t retcode;
fe8b55f
+
fe8b55f
+    if (sftp->fsync_state == libssh2_NB_state_idle) {
fe8b55f
+        _libssh2_debug(session, LIBSSH2_TRACE_SFTP,
fe8b55f
+                       "Issuing fsync command");
fe8b55f
+        s = packet = LIBSSH2_ALLOC(session, packet_len);
fe8b55f
+        if (!packet) {
fe8b55f
+            return _libssh2_error(session, LIBSSH2_ERROR_ALLOC,
fe8b55f
+                                  "Unable to allocate memory for FXP_EXTENDED "
fe8b55f
+                                  "packet");
fe8b55f
+        }
fe8b55f
+
fe8b55f
+        _libssh2_store_u32(&s, packet_len - 4);
fe8b55f
+        *(s++) = SSH_FXP_EXTENDED;
fe8b55f
+        sftp->fsync_request_id = sftp->request_id++;
fe8b55f
+        _libssh2_store_u32(&s, sftp->fsync_request_id);
fe8b55f
+        _libssh2_store_str(&s, "fsync@openssh.com", 17);
fe8b55f
+        _libssh2_store_str(&s, handle->handle, handle->handle_len);
fe8b55f
+
fe8b55f
+        sftp->fsync_state = libssh2_NB_state_created;
fe8b55f
+    } else {
fe8b55f
+        packet = sftp->fsync_packet;
fe8b55f
+    }
fe8b55f
+
fe8b55f
+    if (sftp->fsync_state == libssh2_NB_state_created) {
fe8b55f
+        rc = _libssh2_channel_write(channel, 0, packet, packet_len);
fe8b55f
+        if (rc == LIBSSH2_ERROR_EAGAIN ||
fe8b55f
+            (0 <= rc && rc < (ssize_t)packet_len)) {
fe8b55f
+            sftp->fsync_packet = packet;
fe8b55f
+            return LIBSSH2_ERROR_EAGAIN;
fe8b55f
+        }
fe8b55f
+
fe8b55f
+        LIBSSH2_FREE(session, packet);
fe8b55f
+        sftp->fsync_packet = NULL;
fe8b55f
+
fe8b55f
+        if (rc < 0) {
fe8b55f
+            sftp->fsync_state = libssh2_NB_state_idle;
fe8b55f
+            return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_SEND,
fe8b55f
+                                  "_libssh2_channel_write() failed");
fe8b55f
+        }
fe8b55f
+        sftp->fsync_state = libssh2_NB_state_sent;
fe8b55f
+    }
fe8b55f
+
fe8b55f
+    rc = sftp_packet_require(sftp, SSH_FXP_STATUS,
fe8b55f
+                             sftp->fsync_request_id, &data, &data_len);
fe8b55f
+    if (rc == LIBSSH2_ERROR_EAGAIN) {
fe8b55f
+        return rc;
fe8b55f
+    } else if (rc) {
fe8b55f
+        sftp->fsync_state = libssh2_NB_state_idle;
fe8b55f
+        return _libssh2_error(session, rc,
fe8b55f
+                              "Error waiting for FXP EXTENDED REPLY");
fe8b55f
+    }
fe8b55f
+
fe8b55f
+    sftp->fsync_state = libssh2_NB_state_idle;
fe8b55f
+
fe8b55f
+    retcode = _libssh2_ntohu32(data + 5);
fe8b55f
+    LIBSSH2_FREE(session, data);
fe8b55f
+
fe8b55f
+    if (retcode != LIBSSH2_FX_OK) {
fe8b55f
+        sftp->last_errno = retcode;
fe8b55f
+        return _libssh2_error(session, LIBSSH2_ERROR_SFTP_PROTOCOL,
fe8b55f
+                              "fsync failed");
fe8b55f
+    }
fe8b55f
+
fe8b55f
+    return 0;
fe8b55f
+}
fe8b55f
+
fe8b55f
+/* libssh2_sftp_fsync
fe8b55f
+ * Commit data on the handle to disk.
fe8b55f
+ */
fe8b55f
+LIBSSH2_API int
fe8b55f
+libssh2_sftp_fsync(LIBSSH2_SFTP_HANDLE *hnd)
fe8b55f
+{
fe8b55f
+    int rc;
fe8b55f
+    if(!hnd)
fe8b55f
+        return LIBSSH2_ERROR_BAD_USE;
fe8b55f
+    BLOCK_ADJUST(rc, hnd->sftp->channel->session,
fe8b55f
+                 sftp_fsync(hnd));
fe8b55f
+    return rc;
fe8b55f
+}
fe8b55f
+
fe8b55f
+
fe8b55f
 /*
fe8b55f
  * sftp_fstat
fe8b55f
  *
fe8b55f
diff --git a/src/sftp.h b/src/sftp.h
fe8b55f
index 55bdb46..63e8139 100644
fe8b55f
--- a/src/sftp.h
fe8b55f
+++ b/src/sftp.h
fe8b55f
@@ -175,6 +175,11 @@ struct _LIBSSH2_SFTP
fe8b55f
     /* State variable used in sftp_write() */
fe8b55f
     libssh2_nonblocking_states write_state;
fe8b55f
 
fe8b55f
+    /* State variables used in sftp_fsync() */
fe8b55f
+    libssh2_nonblocking_states fsync_state;
fe8b55f
+    unsigned char *fsync_packet;
fe8b55f
+    uint32_t fsync_request_id;
fe8b55f
+
fe8b55f
     /* State variables used in libssh2_sftp_readdir() */
fe8b55f
     libssh2_nonblocking_states readdir_state;
fe8b55f
     unsigned char *readdir_packet;
fe8b55f
-- 
fe8b55f
1.8.1.4
fe8b55f