summaryrefslogtreecommitdiff
path: root/daemon/gvfsbackendsftp.c
diff options
context:
space:
mode:
authorMaxim Mikityanskiy <maxtram95@gmail.com>2020-04-18 15:31:49 +0300
committerOndrej Holy <oholy@redhat.com>2020-05-12 14:13:06 +0000
commit8e75d14569f90b1832a06b2f9d386e2030bd67ae (patch)
treeeefe7660d1bf9bca5c44b4c2dfe9db23687a2bcd /daemon/gvfsbackendsftp.c
parent8f45ea4d97bb9239e6c91c7baceec815d3d9c7cb (diff)
downloadgvfs-8e75d14569f90b1832a06b2f9d386e2030bd67ae.tar.gz
sftp: Copy file timestamps on push/pull
Copy and move operations preserve file attributes (such as modification time) in most of existing scenarios: local copy/move, remote copy/move, file_copy_fallback in glib. However, one case remains special: copy/move between local and remote (gvfs) locations. It's implemented by push and pull operations in backends, which don't attempt to preserve the usual attributes (e.g., mtime and atime). This commit implements the missing piece of functionality in sftp backend. Modification time is preserved on copy and move, and access time is preserved on move only, complying to the settable attributes list of sftp backend. Signed-off-by: Maxim Mikityanskiy <maxtram95@gmail.com>
Diffstat (limited to 'daemon/gvfsbackendsftp.c')
-rw-r--r--daemon/gvfsbackendsftp.c112
1 files changed, 89 insertions, 23 deletions
diff --git a/daemon/gvfsbackendsftp.c b/daemon/gvfsbackendsftp.c
index cde9cd85..1bb4a673 100644
--- a/daemon/gvfsbackendsftp.c
+++ b/daemon/gvfsbackendsftp.c
@@ -5491,6 +5491,8 @@ typedef struct {
/* fstat information */
goffset size;
guint32 permissions;
+ guint64 mtime;
+ guint64 atime;
/* state */
goffset offset;
@@ -5640,8 +5642,15 @@ push_close_deleted_file (GVfsBackendSftp *backend,
}
static void
-push_close_delete_or_succeed (SftpPushHandle *handle)
+push_close_delete_or_succeed (GVfsBackendSftp *backend,
+ int reply_type,
+ GDataInputStream *reply,
+ guint32 len,
+ GVfsJob *job,
+ gpointer user_data)
{
+ SftpPushHandle *handle = user_data;
+
if (handle->tempname)
{
/* If we wrote to a temp file, do delete then rename. */
@@ -5662,15 +5671,52 @@ push_close_delete_or_succeed (SftpPushHandle *handle)
}
static void
-push_close_restore_permissions (GVfsBackendSftp *backend,
- int reply_type,
- GDataInputStream *reply,
- guint32 len,
- GVfsJob *job,
- gpointer user_data)
+push_close_restore_permissions (SftpPushHandle *handle)
+{
+ gboolean default_perms = (handle->op_job->flags & G_FILE_COPY_TARGET_DEFAULT_PERMS);
+ guint32 flags = SSH_FILEXFER_ATTR_ACMODTIME;
+
+ if (!default_perms)
+ flags |= SSH_FILEXFER_ATTR_PERMISSIONS;
+
+ /* Restore the source file's permissions and timestamps. */
+ GDataOutputStream *command = new_command_stream (handle->backend, SSH_FXP_SETSTAT);
+ put_string (command, handle->tempname ? handle->tempname : handle->op_job->destination);
+ g_data_output_stream_put_uint32 (command, flags, NULL, NULL);
+ if (!default_perms)
+ g_data_output_stream_put_uint32 (command, handle->permissions, NULL, NULL);
+ g_data_output_stream_put_uint32 (command, handle->atime, NULL, NULL);
+ g_data_output_stream_put_uint32 (command, handle->mtime, NULL, NULL);
+ queue_command_stream_and_free (&handle->backend->command_connection, command,
+ push_close_delete_or_succeed,
+ handle->job, handle);
+}
+
+static void
+push_close_stat_reply (GVfsBackendSftp *backend,
+ int reply_type,
+ GDataInputStream *reply,
+ guint32 len,
+ GVfsJob *job,
+ gpointer user_data)
{
- /* We don't care if setting the permissions succeeded or not. */
- push_close_delete_or_succeed (user_data);
+ SftpPushHandle *handle = user_data;
+
+ if (reply_type == SSH_FXP_ATTRS)
+ {
+ GFileInfo *info = g_file_info_new ();
+
+ parse_attributes (backend, info, NULL, reply, NULL);
+
+ /* Don't fail on error, but fall back to the local atime
+ * (assigned in push_source_fstat_cb). */
+ if (g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_ACCESS))
+ handle->atime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_ACCESS);
+
+ g_object_unref (info);
+ }
+
+ push_close_restore_permissions (handle);
}
static void
@@ -5688,19 +5734,19 @@ push_close_write_reply (GVfsBackendSftp *backend,
guint32 code = read_status_code (reply);
if (code == SSH_FX_OK)
{
- if (handle->op_job->flags & G_FILE_COPY_TARGET_DEFAULT_PERMS)
- push_close_delete_or_succeed (handle);
- else
+ /* Atime is COPY_WHEN_MOVED, but not COPY_WITH_FILE. */
+ if (!handle->op_job->remove_source &&
+ !(handle->op_job->flags & G_FILE_COPY_ALL_METADATA))
{
- /* Restore the source file's permissions. */
- GDataOutputStream *command = new_command_stream (backend, SSH_FXP_SETSTAT);
- put_string (command, handle->tempname ? handle->tempname : handle->op_job->destination);
- g_data_output_stream_put_uint32 (command, SSH_FILEXFER_ATTR_PERMISSIONS, NULL, NULL);
- g_data_output_stream_put_uint32 (command, handle->permissions, NULL, NULL);
+ GDataOutputStream *command = new_command_stream (backend, SSH_FXP_LSTAT);
+ put_string (command, handle->op_job->destination);
queue_command_stream_and_free (&backend->command_connection, command,
- push_close_restore_permissions,
+ push_close_stat_reply,
job, handle);
+ return;
}
+
+ push_close_restore_permissions (handle);
return;
}
else
@@ -6070,6 +6116,8 @@ push_source_fstat_cb (GObject *source, GAsyncResult *res, gpointer user_data)
{
handle->permissions = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE) & 0777;
handle->size = g_file_info_get_size (info);
+ handle->mtime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
+ handle->atime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_ACCESS);
command = new_command_stream (handle->backend, SSH_FXP_OPEN);
put_string (command, handle->op_job->destination);
@@ -6102,7 +6150,9 @@ push_source_open_cb (GObject *source, GAsyncResult *res, gpointer user_data)
g_file_input_stream_query_info_async (fin,
G_FILE_ATTRIBUTE_STANDARD_SIZE ","
- G_FILE_ATTRIBUTE_UNIX_MODE,
+ G_FILE_ATTRIBUTE_UNIX_MODE ","
+ G_FILE_ATTRIBUTE_TIME_MODIFIED ","
+ G_FILE_ATTRIBUTE_TIME_ACCESS,
0, NULL,
push_source_fstat_cb, handle);
}
@@ -6211,6 +6261,8 @@ typedef struct {
/* fstat information */
goffset size;
guint32 mode;
+ guint64 mtime;
+ guint64 atime;
/* state */
goffset offset;
@@ -6317,12 +6369,22 @@ pull_close_cb (GObject *source, GAsyncResult *res, gpointer user_data)
{
g_vfs_job_progress_callback (handle->n_written, handle->n_written, handle->job);
- if (handle->size >= 0 && !(handle->op_job->flags & G_FILE_COPY_TARGET_DEFAULT_PERMS))
+ if (handle->size >= 0)
{
GFileInfo *info = g_file_info_new ();
- g_file_info_set_attribute_uint32 (info,
- G_FILE_ATTRIBUTE_UNIX_MODE,
- handle->mode);
+ if (!(handle->op_job->flags & G_FILE_COPY_TARGET_DEFAULT_PERMS))
+ g_file_info_set_attribute_uint32 (info,
+ G_FILE_ATTRIBUTE_UNIX_MODE,
+ handle->mode);
+ g_file_info_set_attribute_uint64 (info,
+ G_FILE_ATTRIBUTE_TIME_MODIFIED,
+ handle->mtime);
+ /* Atime is COPY_WHEN_MOVED, but not COPY_WITH_FILE. */
+ if (handle->op_job->remove_source ||
+ (handle->op_job->flags & G_FILE_COPY_ALL_METADATA))
+ g_file_info_set_attribute_uint64 (info,
+ G_FILE_ATTRIBUTE_TIME_ACCESS,
+ handle->atime);
g_file_set_attributes_async (handle->dest,
info,
G_FILE_QUERY_INFO_NONE,
@@ -6573,6 +6635,10 @@ pull_fstat_reply (GVfsBackendSftp *backend,
handle->size = g_file_info_get_size (info);
handle->mode = g_file_info_get_attribute_uint32 (info,
G_FILE_ATTRIBUTE_UNIX_MODE);
+ handle->mtime = g_file_info_get_attribute_uint64 (info,
+ G_FILE_ATTRIBUTE_TIME_MODIFIED);
+ handle->atime = g_file_info_get_attribute_uint64 (info,
+ G_FILE_ATTRIBUTE_TIME_ACCESS);
g_object_unref (info);
}
else