diff options
author | Ross Lagerwall <rosslagerwall@gmail.com> | 2014-07-24 07:26:09 +0100 |
---|---|---|
committer | Ross Lagerwall <rosslagerwall@gmail.com> | 2014-10-26 15:58:56 +0000 |
commit | f6f99e22a8200adfc6011156231eec57af44027c (patch) | |
tree | 9af6594ad4776dace35b041169a50f732546e080 /client | |
parent | 8dc5941cf17d2d82c8faf69b0ea88b7c66e0ccff (diff) | |
download | gvfs-f6f99e22a8200adfc6011156231eec57af44027c.tar.gz |
fuse: Truncate stream rather than path if possible
When truncating, if a writable stream is open, truncate that rather than
using g_file_replace on the path since that fails to have the desired
effect if replace is implemented by writing to a temporary and then
renaming.
https://bugzilla.gnome.org/show_bug.cgi?id=632296
Diffstat (limited to 'client')
-rw-r--r-- | client/gvfsfusedaemon.c | 149 |
1 files changed, 82 insertions, 67 deletions
diff --git a/client/gvfsfusedaemon.c b/client/gvfsfusedaemon.c index 79657347..40cf52e2 100644 --- a/client/gvfsfusedaemon.c +++ b/client/gvfsfusedaemon.c @@ -1923,13 +1923,70 @@ pad_file (FileHandle *fh, gsize num, goffset current_size) return (res < 0 ? res : 0); } +static int +truncate_stream (GFile *file, FileHandle *fh, off_t size) +{ + goffset current_size; + GError *error = NULL; + int result = 0; + + if (g_seekable_can_truncate (G_SEEKABLE (fh->stream))) + { + g_seekable_truncate (fh->stream, size, NULL, &error); + } + else if (size == 0) + { + g_output_stream_close (fh->stream, NULL, NULL); + g_object_unref (fh->stream); + fh->stream = NULL; + + fh->stream = g_file_replace (file, 0, FALSE, 0, NULL, &error); + + if (fh->stream != NULL) + { + /* The stream created by g_file_replace() won't always replace + * the file until it's been closed. So close it now to make + * future operations consistent. */ + g_output_stream_close (fh->stream, NULL, NULL); + g_object_unref (fh->stream); + fh->stream = NULL; + } + } + else if (file_handle_get_size (fh, ¤t_size)) + { + if (current_size == size) + { + /* Don't have to do anything to succeed */ + } + else if ((current_size < size) && g_seekable_can_seek (G_SEEKABLE (fh->stream))) + { + /* If the truncated size is larger than the current size + * then we need to pad out the difference with 0's */ + goffset orig_pos = g_seekable_tell (G_SEEKABLE (fh->stream)); + result = pad_file (fh, size - current_size, current_size); + if (result == 0) + g_seekable_seek (G_SEEKABLE (fh->stream), orig_pos, G_SEEK_SET, NULL, &error); + } + } + else + { + result = -ENOTSUP; + } + + if (error) + { + result = -errno_from_error (error); + g_error_free (error); + } + + return result; +} + static gint vfs_ftruncate (const gchar *path, off_t size, struct fuse_file_info *fi) { GFile *file; - GError *error = NULL; gint result = 0; - goffset current_size; debug_print ("vfs_ftruncate: %s\n", path); @@ -1946,56 +2003,7 @@ vfs_ftruncate (const gchar *path, off_t size, struct fuse_file_info *fi) result = setup_output_stream (file, fh, 0); if (result == 0) - { - if (g_seekable_can_truncate (G_SEEKABLE (fh->stream))) - { - g_seekable_truncate (fh->stream, size, NULL, &error); - } - else if (size == 0) - { - g_output_stream_close (fh->stream, NULL, NULL); - g_object_unref (fh->stream); - fh->stream = NULL; - - fh->stream = g_file_replace (file, 0, FALSE, 0, NULL, &error); - - if (fh->stream != NULL) - { - /* The stream created by g_file_replace() won't always replace - * the file until it's been closed. So close it now to make - * future operations consistent. */ - g_output_stream_close (fh->stream, NULL, NULL); - g_object_unref (fh->stream); - fh->stream = NULL; - } - } - else if (file_handle_get_size (fh, ¤t_size)) - { - if (current_size == size) - { - /* Don't have to do anything to succeed */ - } - else if ((current_size < size) && g_seekable_can_seek (G_SEEKABLE (fh->stream))) - { - /* If the truncated size is larger than the current size - * then we need to pad out the difference with 0's */ - goffset orig_pos = g_seekable_tell (G_SEEKABLE (fh->stream)); - result = pad_file (fh, size - current_size, current_size); - if (result == 0) - g_seekable_seek (G_SEEKABLE (fh->stream), orig_pos, G_SEEK_SET, NULL, &error); - } - } - else - { - result = -ENOTSUP; - } - - if (error) - { - result = -errno_from_error (error); - g_error_free (error); - } - } + result = truncate_stream (file, fh, size); g_mutex_unlock (&fh->mutex); file_handle_unref (fh); @@ -2038,27 +2046,34 @@ vfs_truncate (const gchar *path, off_t size) if (fh) g_mutex_lock (&fh->mutex); - if (size == 0) + if (fh->stream && fh->op == FILE_OP_WRITE) { - file_output_stream = g_file_replace (file, 0, FALSE, 0, NULL, &error); + result = truncate_stream (file, fh, size); } else { - file_output_stream = g_file_append_to (file, 0, NULL, &error); - if (file_output_stream) - g_seekable_truncate (G_SEEKABLE (file_output_stream), size, NULL, &error); - } + if (size == 0) + { + file_output_stream = g_file_replace (file, 0, FALSE, 0, NULL, &error); + } + else + { + file_output_stream = g_file_append_to (file, 0, NULL, &error); + if (file_output_stream) + g_seekable_truncate (G_SEEKABLE (file_output_stream), size, NULL, &error); + } - if (error) - { - result = -errno_from_error (error); - g_error_free (error); - } + if (error) + { + result = -errno_from_error (error); + g_error_free (error); + } - if (file_output_stream) - { - g_output_stream_close (G_OUTPUT_STREAM (file_output_stream), NULL, NULL); - g_object_unref (file_output_stream); + if (file_output_stream) + { + g_output_stream_close (G_OUTPUT_STREAM (file_output_stream), NULL, NULL); + g_object_unref (file_output_stream); + } } if (fh) |