diff options
author | Maxim Mikityanskiy <maxtram95@gmail.com> | 2020-04-18 15:31:49 +0300 |
---|---|---|
committer | Ondrej Holy <oholy@redhat.com> | 2020-05-12 14:13:06 +0000 |
commit | 8e75d14569f90b1832a06b2f9d386e2030bd67ae (patch) | |
tree | eefe7660d1bf9bca5c44b4c2dfe9db23687a2bcd /daemon | |
parent | 8f45ea4d97bb9239e6c91c7baceec815d3d9c7cb (diff) | |
download | gvfs-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')
-rw-r--r-- | daemon/gvfsbackendsftp.c | 112 |
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 |