summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMax Maisel <max.maisel@posteo.de>2017-09-04 17:31:03 +0200
committerOndrej Holy <oholy@redhat.com>2017-09-29 13:05:12 +0200
commit28e5ef8366874764fd25f45d76834c93547dbd78 (patch)
tree8d45d98358f21e10618fcf111e95edbdd3d91324
parente417487975ac99ca935fabf2907150b6d618f197 (diff)
downloadgvfs-28e5ef8366874764fd25f45d76834c93547dbd78.tar.gz
sftp: Add support for setting timestamps
Implement set time::modified and time::access in function try_set_attribute. The SFTP protocol limitation that atime and mtime must be set simultaneously is overcome by stating the file and perform a read-modify-write operation. Therefore the new callback function set_attribute_stat_reply is added. Furthermore time::modifed and time::access are added to try_query_settable_attributes. This patch makes 'rsync -t' and 'touch' utilities work over gvfsd-sftp. https://bugzilla.gnome.org/show_bug.cgi?id=527339
-rw-r--r--daemon/gvfsbackendsftp.c139
1 files changed, 119 insertions, 20 deletions
diff --git a/daemon/gvfsbackendsftp.c b/daemon/gvfsbackendsftp.c
index 22ad4db7..7818c9e3 100644
--- a/daemon/gvfsbackendsftp.c
+++ b/daemon/gvfsbackendsftp.c
@@ -65,6 +65,7 @@
#include "gvfsjobprogress.h"
#include "gvfsjobpush.h"
#include "gvfsjobpull.h"
+#include "gvfsjobsetattribute.h"
#include "gvfsdaemonprotocol.h"
#include "gvfsutils.h"
#include "gvfskeyring.h"
@@ -5172,6 +5173,17 @@ try_query_settable_attributes (GVfsBackend *backend,
G_FILE_ATTRIBUTE_INFO_COPY_WITH_FILE |
G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
+ g_file_attribute_info_list_add (list,
+ G_FILE_ATTRIBUTE_TIME_MODIFIED,
+ G_FILE_ATTRIBUTE_TYPE_UINT64,
+ G_FILE_ATTRIBUTE_INFO_COPY_WITH_FILE |
+ G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
+
+ g_file_attribute_info_list_add (list,
+ G_FILE_ATTRIBUTE_TIME_ACCESS,
+ G_FILE_ATTRIBUTE_TYPE_UINT64,
+ G_FILE_ATTRIBUTE_INFO_COPY_WHEN_MOVED);
+
g_vfs_job_query_attributes_set_list (job, list);
g_vfs_job_succeeded (G_VFS_JOB (job));
g_file_attribute_info_list_unref (list);
@@ -5194,6 +5206,74 @@ set_attribute_reply (GVfsBackendSftp *backend,
_("Invalid reply received"));
}
+static void
+set_attribute_stat_reply (GVfsBackendSftp *backend,
+ int reply_type,
+ GDataInputStream *reply,
+ guint32 len,
+ GVfsJob *job,
+ gpointer user_data)
+{
+ GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
+ GDataOutputStream *command;
+ GVfsJobSetAttribute *op_job = G_VFS_JOB_SET_ATTRIBUTE (job);
+
+ if (reply_type == SSH_FXP_ATTRS)
+ {
+ guint32 mtime;
+ guint32 atime;
+ GFileInfo *info = g_file_info_new ();
+
+ parse_attributes (backend, info, NULL, reply, NULL);
+
+ /* parse_attributes sets either both timestamps or none
+ * so checking one of them is enough. */
+ if (!g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_MODIFIED))
+ {
+ g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Operation unsupported"));
+ g_object_unref (info);
+ return;
+ }
+ /* Timestamps must be read as uint64 but sftp only supports uint32 */
+ if (op_job->value.uint64 > G_MAXUINT32)
+ {
+ g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
+ _("Value out of range, sftp only supports 32bit timestamps"));
+ g_object_unref (info);
+ return;
+ }
+
+ if (g_strcmp0 (op_job->attribute, G_FILE_ATTRIBUTE_TIME_ACCESS) == 0)
+ {
+ mtime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
+ atime = op_job->value.uint64;
+ }
+ else
+ {
+ atime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_ACCESS);
+ mtime = op_job->value.uint64;
+ }
+
+ g_object_unref (info);
+
+ command = new_command_stream (op_backend, SSH_FXP_SETSTAT);
+ put_string (command, op_job->filename);
+
+ g_data_output_stream_put_uint32 (command, SSH_FILEXFER_ATTR_ACMODTIME, NULL, NULL);
+ g_data_output_stream_put_uint32 (command, atime, NULL, NULL);
+ g_data_output_stream_put_uint32 (command, mtime, NULL, NULL);
+ queue_command_stream_and_free (&op_backend->command_connection, command,
+ set_attribute_reply,
+ G_VFS_JOB (job), NULL);
+ }
+ else if (reply_type == SSH_FXP_STATUS)
+ result_from_status (job, reply, -1, -1);
+ else
+ g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_FAILED,
+ _("Invalid reply received"));
+}
+
static gboolean
try_set_attribute (GVfsBackend *backend,
GVfsJobSetAttribute *job,
@@ -5206,33 +5286,52 @@ try_set_attribute (GVfsBackend *backend,
GVfsBackendSftp *op_backend = G_VFS_BACKEND_SFTP (backend);
GDataOutputStream *command;
- if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_MODE) != 0)
+ if (g_strcmp0 (attribute, G_FILE_ATTRIBUTE_UNIX_MODE) == 0)
{
- g_vfs_job_failed (G_VFS_JOB (job),
- G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
- _("Operation unsupported"));
- return TRUE;
+ if (type != G_FILE_ATTRIBUTE_TYPE_UINT32)
+ {
+ g_vfs_job_failed (G_VFS_JOB (job),
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ _("Invalid attribute type (uint32 expected)"));
+ return TRUE;
+ }
+
+ command = new_command_stream (op_backend,
+ SSH_FXP_SETSTAT);
+ put_string (command, filename);
+ g_data_output_stream_put_uint32 (command, SSH_FILEXFER_ATTR_PERMISSIONS, NULL, NULL);
+ g_data_output_stream_put_uint32 (command, (*(guint32 *)value_p) & 0777, NULL, NULL);
+ queue_command_stream_and_free (&op_backend->command_connection, command,
+ set_attribute_reply,
+ G_VFS_JOB (job), NULL);
}
+ else if (g_strcmp0 (attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED) == 0 ||
+ g_strcmp0 (attribute, G_FILE_ATTRIBUTE_TIME_ACCESS) == 0)
+ {
+ if (type != G_FILE_ATTRIBUTE_TYPE_UINT64)
+ {
+ g_vfs_job_failed (G_VFS_JOB (job),
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_ARGUMENT,
+ _("Invalid attribute type (uint64 expected)"));
+ return TRUE;
+ }
- if (type != G_FILE_ATTRIBUTE_TYPE_UINT32)
+ command = new_command_stream (op_backend, SSH_FXP_LSTAT);
+ put_string (command, filename);
+
+ queue_command_stream_and_free (&op_backend->command_connection, command,
+ set_attribute_stat_reply,
+ G_VFS_JOB (job), NULL);
+ }
+ else
{
g_vfs_job_failed (G_VFS_JOB (job),
- G_IO_ERROR,
- G_IO_ERROR_INVALID_ARGUMENT,
- "%s",
- _("Invalid attribute type (uint32 expected)"));
- return TRUE;
+ G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Operation unsupported"));
}
- command = new_command_stream (op_backend,
- SSH_FXP_SETSTAT);
- put_string (command, filename);
- g_data_output_stream_put_uint32 (command, SSH_FILEXFER_ATTR_PERMISSIONS, NULL, NULL);
- g_data_output_stream_put_uint32 (command, (*(guint32 *)value_p) & 0777, NULL, NULL);
- queue_command_stream_and_free (&op_backend->command_connection, command,
- set_attribute_reply,
- G_VFS_JOB (job), NULL);
-
return TRUE;
}