summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoss Lagerwall <rosslagerwall@gmail.com>2014-07-24 07:26:09 +0100
committerRoss Lagerwall <rosslagerwall@gmail.com>2014-10-26 15:58:56 +0000
commitf6f99e22a8200adfc6011156231eec57af44027c (patch)
tree9af6594ad4776dace35b041169a50f732546e080
parent8dc5941cf17d2d82c8faf69b0ea88b7c66e0ccff (diff)
downloadgvfs-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
-rw-r--r--client/gvfsfusedaemon.c149
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, &current_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, &current_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)