summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoss Lagerwall <rosslagerwall@gmail.com>2013-10-17 07:26:05 +0200
committerRoss Lagerwall <rosslagerwall@gmail.com>2013-12-05 23:51:30 +0000
commitc3b6615e95bd213beab32d90a8bf38f1b221a8b4 (patch)
treeb18ec470be6377b527e603d58ca274bb5c48074d
parent444a63d09fdaf4db8124801ec6f4ff26ca3c7c0e (diff)
downloadgvfs-c3b6615e95bd213beab32d90a8bf38f1b221a8b4.tar.gz
Implement truncate support for output streams
Backends receive a TRUNCATE message which contains a size parameter. Truncation is signaled with a TRUNCATED message (which contains no other useful information). In more detail: Add a new dbus method, OpenForWriteFlags, which has a flags parameter to implement can_seek and can_truncate. These flags are used in GDaemonFileOutputStream. Compatability with old clients is maintained. Implement the can_truncate and truncate_fn GDaemonFileOutputStream methods. Add two new message types to the daemon socket protocol: G_VFS_DAEMON_SOCKET_PROTOCOL_REQUEST_TRUNCATE G_VFS_DAEMON_SOCKET_PROTOCOL_REPLY_TRUNCATED Add a new job type, GVfsJobTruncate. Add two new methods to GVfsBackend which backend classes can implement: truncate and try_truncate https://bugzilla.gnome.org/show_bug.cgi?id=573837
-rw-r--r--client/gdaemonfile.c66
-rw-r--r--client/gdaemonfileoutputstream.c201
-rw-r--r--client/gdaemonfileoutputstream.h2
-rw-r--r--common/gvfsdaemonprotocol.h6
-rw-r--r--common/org.gtk.vfs.xml12
-rw-r--r--daemon/Makefile.am1
-rw-r--r--daemon/gvfsbackend.c1
-rw-r--r--daemon/gvfsbackend.h9
-rw-r--r--daemon/gvfsjobopenforwrite.c107
-rw-r--r--daemon/gvfsjobopenforwrite.h22
-rw-r--r--daemon/gvfsjobtruncate.c128
-rw-r--r--daemon/gvfsjobtruncate.h63
-rw-r--r--daemon/gvfswritechannel.c26
-rw-r--r--daemon/gvfswritechannel.h1
14 files changed, 591 insertions, 54 deletions
diff --git a/client/gdaemonfile.c b/client/gdaemonfile.c
index 267ceaa3..445c9195 100644
--- a/client/gdaemonfile.c
+++ b/client/gdaemonfile.c
@@ -1238,7 +1238,7 @@ file_open_write (GFile *file,
GVfsDBusMount *proxy;
char *path;
gboolean res;
- gboolean can_seek;
+ guint32 ret_flags;
GUnixFDList *fd_list;
int fd;
GVariant *fd_id_val = NULL;
@@ -1255,20 +1255,20 @@ file_open_write (GFile *file,
if (proxy == NULL)
return NULL;
- res = gvfs_dbus_mount_call_open_for_write_sync (proxy,
- path,
- mode,
- etag,
- make_backup,
- flags,
- pid,
- NULL,
- &fd_id_val,
- &can_seek,
- &initial_offset,
- &fd_list,
- cancellable,
- &local_error);
+ res = gvfs_dbus_mount_call_open_for_write_flags_sync (proxy,
+ path,
+ mode,
+ etag,
+ make_backup,
+ flags,
+ pid,
+ NULL,
+ &fd_id_val,
+ &ret_flags,
+ &initial_offset,
+ &fd_list,
+ cancellable,
+ &local_error);
if (! res)
{
@@ -1295,7 +1295,7 @@ file_open_write (GFile *file,
g_variant_unref (fd_id_val);
g_object_unref (fd_list);
- return g_daemon_file_output_stream_new (fd, can_seek, initial_offset);
+ return g_daemon_file_output_stream_new (fd, ret_flags, initial_offset);
}
static GFileOutputStream *
@@ -3144,7 +3144,7 @@ file_open_write_async_cb (GVfsDBusMount *proxy,
AsyncCallFileReadWrite *data = user_data;
GError *error = NULL;
GSimpleAsyncResult *orig_result;
- gboolean can_seek;
+ guint32 flags;
GUnixFDList *fd_list;
int fd;
GVariant *fd_id_val;
@@ -3154,7 +3154,13 @@ file_open_write_async_cb (GVfsDBusMount *proxy,
orig_result = data->result;
- if (! gvfs_dbus_mount_call_open_for_write_finish (proxy, &fd_id_val, &can_seek, &initial_offset, &fd_list, res, &error))
+ if (! gvfs_dbus_mount_call_open_for_write_flags_finish (proxy,
+ &fd_id_val,
+ &flags,
+ &initial_offset,
+ &fd_list,
+ res,
+ &error))
{
_g_simple_async_result_take_error_stripped (orig_result, error);
goto out;
@@ -3172,7 +3178,7 @@ file_open_write_async_cb (GVfsDBusMount *proxy,
}
else
{
- output_stream = g_daemon_file_output_stream_new (fd, can_seek, initial_offset);
+ output_stream = g_daemon_file_output_stream_new (fd, flags, initial_offset);
g_simple_async_result_set_op_res_gpointer (orig_result, output_stream, g_object_unref);
g_object_unref (fd_list);
}
@@ -3201,17 +3207,17 @@ file_open_write_async_get_proxy_cb (GVfsDBusMount *proxy,
data->result = g_object_ref (result);
- gvfs_dbus_mount_call_open_for_write (proxy,
- path,
- data->mode,
- data->etag,
- data->make_backup,
- data->flags,
- pid,
- NULL,
- cancellable,
- (GAsyncReadyCallback) file_open_write_async_cb,
- data);
+ gvfs_dbus_mount_call_open_for_write_flags (proxy,
+ path,
+ data->mode,
+ data->etag,
+ data->make_backup,
+ data->flags,
+ pid,
+ NULL,
+ cancellable,
+ (GAsyncReadyCallback) file_open_write_async_cb,
+ data);
data->cancelled_tag = _g_dbus_async_subscribe_cancellable (connection, cancellable);
}
diff --git a/client/gdaemonfileoutputstream.c b/client/gdaemonfileoutputstream.c
index e1804867..4a5f0b72 100644
--- a/client/gdaemonfileoutputstream.c
+++ b/client/gdaemonfileoutputstream.c
@@ -98,6 +98,26 @@ typedef struct {
} SeekOperation;
typedef enum {
+ TRUNCATE_STATE_INIT = 0,
+ TRUNCATE_STATE_WROTE_REQUEST,
+ TRUNCATE_STATE_HANDLE_INPUT
+} TruncateState;
+
+typedef struct {
+ TruncateState state;
+
+ /* Output */
+ goffset size;
+ /* Input */
+ gboolean ret_val;
+ GError *ret_error;
+
+ gboolean sent_cancel;
+
+ guint32 seq_nr;
+} TruncateOperation;
+
+typedef enum {
CLOSE_STATE_INIT = 0,
CLOSE_STATE_WROTE_REQUEST,
CLOSE_STATE_HANDLE_INPUT
@@ -157,7 +177,8 @@ struct _GDaemonFileOutputStream {
GOutputStream *command_stream;
GInputStream *data_stream;
- guint can_seek : 1;
+ gboolean can_seek;
+ gboolean can_truncate;
guint32 seq_nr;
goffset current_offset;
@@ -191,6 +212,11 @@ static gboolean g_daemon_file_output_stream_seek (GFileOutputStre
GSeekType type,
GCancellable *cancellable,
GError **error);
+static gboolean g_daemon_file_output_stream_can_truncate (GFileOutputStream *stream);
+static gboolean g_daemon_file_output_stream_truncate (GFileOutputStream *stream,
+ goffset size,
+ GCancellable *cancellable,
+ GError **error);
static void g_daemon_file_output_stream_write_async (GOutputStream *stream,
const void *buffer,
gsize count,
@@ -276,6 +302,8 @@ g_daemon_file_output_stream_class_init (GDaemonFileOutputStreamClass *klass)
file_stream_class->tell = g_daemon_file_output_stream_tell;
file_stream_class->can_seek = g_daemon_file_output_stream_can_seek;
file_stream_class->seek = g_daemon_file_output_stream_seek;
+ file_stream_class->can_truncate = g_daemon_file_output_stream_can_truncate;
+ file_stream_class->truncate_fn = g_daemon_file_output_stream_truncate;
file_stream_class->query_info = g_daemon_file_output_stream_query_info;
file_stream_class->get_etag = g_daemon_file_output_stream_get_etag;
file_stream_class->query_info_async = g_daemon_file_output_stream_query_info_async;
@@ -292,7 +320,7 @@ g_daemon_file_output_stream_init (GDaemonFileOutputStream *info)
GFileOutputStream *
g_daemon_file_output_stream_new (int fd,
- gboolean can_seek,
+ guint32 flags,
goffset initial_offset)
{
GDaemonFileOutputStream *stream;
@@ -301,7 +329,8 @@ g_daemon_file_output_stream_new (int fd,
stream->command_stream = g_unix_output_stream_new (fd, FALSE);
stream->data_stream = g_unix_input_stream_new (fd, TRUE);
- stream->can_seek = can_seek;
+ stream->can_seek = flags & OPEN_FOR_WRITE_FLAG_CAN_SEEK;
+ stream->can_truncate = flags & OPEN_FOR_WRITE_FLAG_CAN_TRUNCATE;
stream->current_offset = initial_offset;
return G_FILE_OUTPUT_STREAM (stream);
@@ -1020,6 +1049,172 @@ g_daemon_file_output_stream_seek (GFileOutputStream *stream,
return op.ret_val;
}
+static StateOp
+iterate_truncate_state_machine (GDaemonFileOutputStream *file,
+ IOOperationData *io_op,
+ TruncateOperation *op)
+{
+ gsize len;
+
+ while (TRUE)
+ {
+ switch (op->state)
+ {
+ case TRUNCATE_STATE_INIT:
+ append_request (file,
+ G_VFS_DAEMON_SOCKET_PROTOCOL_REQUEST_TRUNCATE,
+ op->size & 0xffffffff,
+ op->size >> 32,
+ 0,
+ &op->seq_nr);
+ op->state = TRUNCATE_STATE_WROTE_REQUEST;
+ io_op->io_buffer = file->output_buffer->str;
+ io_op->io_size = file->output_buffer->len;
+ io_op->io_allow_cancel = TRUE;
+ return STATE_OP_WRITE;
+
+ case TRUNCATE_STATE_WROTE_REQUEST:
+ if (io_op->io_cancelled)
+ {
+ if (!op->sent_cancel)
+ unappend_request (file);
+ op->ret_val = FALSE;
+ g_set_error_literal (&op->ret_error,
+ G_IO_ERROR,
+ G_IO_ERROR_CANCELLED,
+ _("Operation was cancelled"));
+ return STATE_OP_DONE;
+ }
+
+ if (io_op->io_res < file->output_buffer->len)
+ {
+ g_string_remove_in_front (file->output_buffer, io_op->io_res);
+ io_op->io_buffer = file->output_buffer->str;
+ io_op->io_size = file->output_buffer->len;
+ io_op->io_allow_cancel = FALSE;
+ return STATE_OP_WRITE;
+ }
+ g_string_truncate (file->output_buffer, 0);
+
+ op->state = TRUNCATE_STATE_HANDLE_INPUT;
+ break;
+
+ case TRUNCATE_STATE_HANDLE_INPUT:
+ if (io_op->cancelled && !op->sent_cancel)
+ {
+ op->sent_cancel = TRUE;
+ append_request (file, G_VFS_DAEMON_SOCKET_PROTOCOL_REQUEST_CANCEL,
+ op->seq_nr, 0, 0, NULL);
+ op->state = TRUNCATE_STATE_WROTE_REQUEST;
+ io_op->io_buffer = file->output_buffer->str;
+ io_op->io_size = file->output_buffer->len;
+ io_op->io_allow_cancel = FALSE;
+ return STATE_OP_WRITE;
+ }
+
+ if (io_op->io_res > 0)
+ {
+ gsize unread_size = io_op->io_size - io_op->io_res;
+ g_string_set_size (file->input_buffer,
+ file->input_buffer->len - unread_size);
+ }
+
+ len = get_reply_header_missing_bytes (file->input_buffer);
+ if (len > 0)
+ {
+ gsize current_len = file->input_buffer->len;
+ g_string_set_size (file->input_buffer, current_len + len);
+ io_op->io_buffer = file->input_buffer->str + current_len;
+ io_op->io_size = len;
+ io_op->io_allow_cancel = !op->sent_cancel;
+ return STATE_OP_READ;
+ }
+
+ {
+ GVfsDaemonSocketProtocolReply reply;
+ char *data;
+ data = decode_reply (file->input_buffer, &reply);
+
+ if (reply.type == G_VFS_DAEMON_SOCKET_PROTOCOL_REPLY_ERROR &&
+ reply.seq_nr == op->seq_nr)
+ {
+ op->ret_val = FALSE;
+ decode_error (&reply, data, &op->ret_error);
+ g_string_truncate (file->input_buffer, 0);
+ return STATE_OP_DONE;
+ }
+ else if (reply.type == G_VFS_DAEMON_SOCKET_PROTOCOL_REPLY_TRUNCATED &&
+ reply.seq_nr == op->seq_nr)
+ {
+ op->ret_val = TRUE;
+ g_string_truncate (file->input_buffer, 0);
+ return STATE_OP_DONE;
+ }
+ /* Ignore other reply types */
+ }
+
+ g_string_truncate (file->input_buffer, 0);
+
+ /* This wasn't interesting, read next reply */
+ op->state = TRUNCATE_STATE_HANDLE_INPUT;
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+
+ /* Clear io_op between non-op state switches */
+ io_op->io_size = 0;
+ io_op->io_res = 0;
+ io_op->io_cancelled = FALSE;
+ }
+}
+
+static gboolean
+g_daemon_file_output_stream_can_truncate (GFileOutputStream *stream)
+{
+ GDaemonFileOutputStream *file;
+
+ file = G_DAEMON_FILE_OUTPUT_STREAM (stream);
+
+ return file->can_truncate;
+}
+
+static gboolean
+g_daemon_file_output_stream_truncate (GFileOutputStream *stream,
+ goffset size,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GDaemonFileOutputStream *file;
+ TruncateOperation op;
+
+ file = G_DAEMON_FILE_OUTPUT_STREAM (stream);
+
+ if (!file->can_truncate)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Truncate not supported on stream"));
+ return FALSE;
+ }
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return FALSE;
+
+ memset (&op, 0, sizeof (op));
+ op.state = TRUNCATE_STATE_INIT;
+ op.size = size;
+
+ if (!run_sync_state_machine (file, (state_machine_iterator)iterate_truncate_state_machine,
+ &op, cancellable, error))
+ return FALSE; /* IO Error */
+
+ if (!op.ret_val)
+ g_propagate_error (error, op.ret_error);
+
+ return op.ret_val;
+}
+
static char *
g_daemon_file_output_stream_get_etag (GFileOutputStream *stream)
{
diff --git a/client/gdaemonfileoutputstream.h b/client/gdaemonfileoutputstream.h
index 0b48728f..cee761bc 100644
--- a/client/gdaemonfileoutputstream.h
+++ b/client/gdaemonfileoutputstream.h
@@ -45,7 +45,7 @@ struct _GDaemonFileOutputStreamClass
GType g_daemon_file_output_stream_get_type (void) G_GNUC_CONST;
GFileOutputStream *g_daemon_file_output_stream_new (int fd,
- gboolean can_seek,
+ guint32 flags,
goffset initial_offset);
G_END_DECLS
diff --git a/common/gvfsdaemonprotocol.h b/common/gvfsdaemonprotocol.h
index 440620b4..e74a5d24 100644
--- a/common/gvfsdaemonprotocol.h
+++ b/common/gvfsdaemonprotocol.h
@@ -19,6 +19,10 @@ G_BEGIN_DECLS
/* Normal ops are faster, one minute timeout */
#define G_VFS_DBUS_TIMEOUT_MSECS (1000*60)
+/* Flags for the OpenForWriteFlags method */
+#define OPEN_FOR_WRITE_FLAG_CAN_SEEK (1<<0)
+#define OPEN_FOR_WRITE_FLAG_CAN_TRUNCATE (1<<1)
+
typedef struct {
guint32 command;
guint32 seq_nr;
@@ -36,6 +40,7 @@ typedef struct {
#define G_VFS_DAEMON_SOCKET_PROTOCOL_REQUEST_SEEK_SET 4
#define G_VFS_DAEMON_SOCKET_PROTOCOL_REQUEST_SEEK_END 5
#define G_VFS_DAEMON_SOCKET_PROTOCOL_REQUEST_QUERY_INFO 6
+#define G_VFS_DAEMON_SOCKET_PROTOCOL_REQUEST_TRUNCATE 7
/*
read, readahead reply:
@@ -67,6 +72,7 @@ typedef struct {
#define G_VFS_DAEMON_SOCKET_PROTOCOL_REPLY_WRITTEN 3
#define G_VFS_DAEMON_SOCKET_PROTOCOL_REPLY_CLOSED 4
#define G_VFS_DAEMON_SOCKET_PROTOCOL_REPLY_INFO 5
+#define G_VFS_DAEMON_SOCKET_PROTOCOL_REPLY_TRUNCATED 6
typedef union {
diff --git a/common/org.gtk.vfs.xml b/common/org.gtk.vfs.xml
index b668352a..63038b02 100644
--- a/common/org.gtk.vfs.xml
+++ b/common/org.gtk.vfs.xml
@@ -199,6 +199,18 @@
<arg type='t' name='initial_offset' direction='out'/>
<annotation name="org.gtk.GDBus.C.UnixFD" value="true"/>
</method>
+ <method name="OpenForWriteFlags">
+ <arg type='ay' name='path_data' direction='in'/>
+ <arg type='q' name='mode' direction='in'/>
+ <arg type='s' name='etag' direction='in'/>
+ <arg type='b' name='make_backup' direction='in'/>
+ <arg type='u' name='flags' direction='in'/>
+ <arg type='u' name='pid' direction='in'/>
+ <arg type='h' name='fd_id' direction='out'/>
+ <arg type='u' name='flags' direction='out'/>
+ <arg type='t' name='initial_offset' direction='out'/>
+ <annotation name="org.gtk.GDBus.C.UnixFD" value="true"/>
+ </method>
<method name="QueryInfo">
<arg type='ay' name='path_data' direction='in'/>
<arg type='s' name='attributes' direction='in'/>
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 020943ac..6a1b8378 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -178,6 +178,7 @@ libdaemon_la_SOURCES = \
gvfsjobopenforwrite.c gvfsjobopenforwrite.h \
gvfsjobwrite.c gvfsjobwrite.h \
gvfsjobseekwrite.c gvfsjobseekwrite.h \
+ gvfsjobtruncate.c gvfsjobtruncate.h \
gvfsjobclosewrite.c gvfsjobclosewrite.h \
gvfsjobqueryinfo.c gvfsjobqueryinfo.h \
gvfsjobqueryinforead.c gvfsjobqueryinforead.h \
diff --git a/daemon/gvfsbackend.c b/daemon/gvfsbackend.c
index f6eb77bc..26eba289 100644
--- a/daemon/gvfsbackend.c
+++ b/daemon/gvfsbackend.c
@@ -280,6 +280,7 @@ register_path_cb (GDBusConnection *conn,
g_signal_connect (skeleton, "handle-unmount", G_CALLBACK (g_vfs_job_unmount_new_handle), data);
g_signal_connect (skeleton, "handle-open-for-read", G_CALLBACK (g_vfs_job_open_for_read_new_handle), data);
g_signal_connect (skeleton, "handle-open-for-write", G_CALLBACK (g_vfs_job_open_for_write_new_handle), data);
+ g_signal_connect (skeleton, "handle-open-for-write-flags", G_CALLBACK (g_vfs_job_open_for_write_new_handle_with_flags), data);
g_signal_connect (skeleton, "handle-copy", G_CALLBACK (g_vfs_job_copy_new_handle), data);
g_signal_connect (skeleton, "handle-move", G_CALLBACK (g_vfs_job_move_new_handle), data);
g_signal_connect (skeleton, "handle-push", G_CALLBACK (g_vfs_job_push_new_handle), data);
diff --git a/daemon/gvfsbackend.h b/daemon/gvfsbackend.h
index 45ec4fb7..8e7ba551 100644
--- a/daemon/gvfsbackend.h
+++ b/daemon/gvfsbackend.h
@@ -55,6 +55,7 @@ typedef struct _GVfsJobRead GVfsJobRead;
typedef struct _GVfsJobOpenForWrite GVfsJobOpenForWrite;
typedef struct _GVfsJobWrite GVfsJobWrite;
typedef struct _GVfsJobSeekWrite GVfsJobSeekWrite;
+typedef struct _GVfsJobTruncate GVfsJobTruncate;
typedef struct _GVfsJobCloseWrite GVfsJobCloseWrite;
typedef struct _GVfsJobQueryInfo GVfsJobQueryInfo;
typedef struct _GVfsJobQueryInfoRead GVfsJobQueryInfoRead;
@@ -236,6 +237,14 @@ struct _GVfsBackendClass
GVfsBackendHandle handle,
goffset offset,
GSeekType type);
+ void (*truncate) (GVfsBackend *backend,
+ GVfsJobTruncate *job,
+ GVfsBackendHandle handle,
+ goffset size);
+ gboolean (*try_truncate) (GVfsBackend *backend,
+ GVfsJobTruncate *job,
+ GVfsBackendHandle handle,
+ goffset size);
void (*query_info) (GVfsBackend *backend,
GVfsJobQueryInfo *job,
const char *filename,
diff --git a/daemon/gvfsjobopenforwrite.c b/daemon/gvfsjobopenforwrite.c
index 429e2679..1388dabf 100644
--- a/daemon/gvfsjobopenforwrite.c
+++ b/daemon/gvfsjobopenforwrite.c
@@ -81,28 +81,29 @@ g_vfs_job_open_for_write_init (GVfsJobOpenForWrite *job)
{
}
-gboolean
-g_vfs_job_open_for_write_new_handle (GVfsDBusMount *object,
- GDBusMethodInvocation *invocation,
- GUnixFDList *fd_list,
- const gchar *arg_path_data,
- guint16 arg_mode,
- const gchar *arg_etag,
- gboolean arg_make_backup,
- guint arg_flags,
- guint arg_pid,
- GVfsBackend *backend)
+static gboolean
+open_for_write_new_handle_common (GVfsDBusMount *object,
+ GDBusMethodInvocation *invocation,
+ GUnixFDList *fd_list,
+ const gchar *arg_path_data,
+ guint16 arg_mode,
+ const gchar *arg_etag,
+ gboolean arg_make_backup,
+ guint arg_flags,
+ guint arg_pid,
+ GVfsBackend *backend,
+ GVfsJobOpenForWriteVersion version)
{
GVfsJobOpenForWrite *job;
-
+
if (g_vfs_backend_invocation_first_handler (object, invocation, backend))
return TRUE;
-
+
job = g_object_new (G_VFS_TYPE_JOB_OPEN_FOR_WRITE,
"object", object,
"invocation", invocation,
NULL);
-
+
job->filename = g_strdup (arg_path_data);
job->mode = arg_mode;
if (*arg_etag != 0)
@@ -111,6 +112,7 @@ g_vfs_job_open_for_write_new_handle (GVfsDBusMount *object,
job->flags = arg_flags;
job->backend = backend;
job->pid = arg_pid;
+ job->version = version;
g_vfs_job_source_new_job (G_VFS_JOB_SOURCE (backend), G_VFS_JOB (job));
g_object_unref (job);
@@ -118,6 +120,56 @@ g_vfs_job_open_for_write_new_handle (GVfsDBusMount *object,
return TRUE;
}
+gboolean
+g_vfs_job_open_for_write_new_handle (GVfsDBusMount *object,
+ GDBusMethodInvocation *invocation,
+ GUnixFDList *fd_list,
+ const gchar *arg_path_data,
+ guint16 arg_mode,
+ const gchar *arg_etag,
+ gboolean arg_make_backup,
+ guint arg_flags,
+ guint arg_pid,
+ GVfsBackend *backend)
+{
+ return open_for_write_new_handle_common(object,
+ invocation,
+ fd_list,
+ arg_path_data,
+ arg_mode,
+ arg_etag,
+ arg_make_backup,
+ arg_flags,
+ arg_pid,
+ backend,
+ OPEN_FOR_WRITE_VERSION_ORIGINAL);
+}
+
+gboolean
+g_vfs_job_open_for_write_new_handle_with_flags (GVfsDBusMount *object,
+ GDBusMethodInvocation *invocation,
+ GUnixFDList *fd_list,
+ const gchar *arg_path_data,
+ guint16 arg_mode,
+ const gchar *arg_etag,
+ gboolean arg_make_backup,
+ guint arg_flags,
+ guint arg_pid,
+ GVfsBackend *backend)
+{
+ return open_for_write_new_handle_common(object,
+ invocation,
+ fd_list,
+ arg_path_data,
+ arg_mode,
+ arg_etag,
+ arg_make_backup,
+ arg_flags,
+ arg_pid,
+ backend,
+ OPEN_FOR_WRITE_VERSION_WITH_FLAGS);
+}
+
static void
run (GVfsJob *job)
{
@@ -233,6 +285,13 @@ g_vfs_job_open_for_write_set_can_seek (GVfsJobOpenForWrite *job,
}
void
+g_vfs_job_open_for_write_set_can_truncate (GVfsJobOpenForWrite *job,
+ gboolean can_truncate)
+{
+ job->can_truncate = can_truncate;
+}
+
+void
g_vfs_job_open_for_write_set_initial_offset (GVfsJobOpenForWrite *job,
goffset initial_offset)
{
@@ -284,10 +343,22 @@ create_reply (GVfsJob *job,
g_signal_emit_by_name (job, "new-source", open_job->write_channel);
- gvfs_dbus_mount_complete_open_for_write (object, invocation,
- fd_list, g_variant_new_handle (fd_id),
- open_job->can_seek,
- open_job->initial_offset);
+ switch (open_job->version)
+ {
+ case OPEN_FOR_WRITE_VERSION_ORIGINAL:
+ gvfs_dbus_mount_complete_open_for_write (object, invocation,
+ fd_list, g_variant_new_handle (fd_id),
+ open_job->can_seek ? OPEN_FOR_WRITE_FLAG_CAN_SEEK : 0,
+ open_job->initial_offset);
+ break;
+ case OPEN_FOR_WRITE_VERSION_WITH_FLAGS:
+ gvfs_dbus_mount_complete_open_for_write_flags (object, invocation,
+ fd_list, g_variant_new_handle (fd_id),
+ (open_job->can_seek ? OPEN_FOR_WRITE_FLAG_CAN_SEEK : 0) |
+ (open_job->can_truncate ? OPEN_FOR_WRITE_FLAG_CAN_TRUNCATE : 0),
+ open_job->initial_offset);
+ break;
+ }
close (remote_fd);
g_object_unref (fd_list);
diff --git a/daemon/gvfsjobopenforwrite.h b/daemon/gvfsjobopenforwrite.h
index 79854d23..141189f3 100644
--- a/daemon/gvfsjobopenforwrite.h
+++ b/daemon/gvfsjobopenforwrite.h
@@ -44,6 +44,11 @@ typedef enum {
OPEN_FOR_WRITE_REPLACE = 2
} GVfsJobOpenForWriteMode;
+typedef enum {
+ OPEN_FOR_WRITE_VERSION_ORIGINAL,
+ OPEN_FOR_WRITE_VERSION_WITH_FLAGS,
+} GVfsJobOpenForWriteVersion;
+
struct _GVfsJobOpenForWrite
{
GVfsJobDBus parent_instance;
@@ -57,11 +62,14 @@ struct _GVfsJobOpenForWrite
GVfsBackend *backend;
GVfsBackendHandle backend_handle;
- gboolean can_seek;
+ guint can_seek : 1;
+ guint can_truncate : 1;
goffset initial_offset;
GVfsWriteChannel *write_channel;
GPid pid;
+
+ GVfsJobOpenForWriteVersion version;
};
struct _GVfsJobOpenForWriteClass
@@ -81,10 +89,22 @@ gboolean g_vfs_job_open_for_write_new_handle (GVfsDBusMount *obj
guint arg_flags,
guint arg_pid,
GVfsBackend *backend);
+gboolean g_vfs_job_open_for_write_new_handle_with_flags (GVfsDBusMount *object,
+ GDBusMethodInvocation *invocation,
+ GUnixFDList *fd_list,
+ const gchar *arg_path_data,
+ guint16 arg_mode,
+ const gchar *arg_etag,
+ gboolean arg_make_backup,
+ guint arg_flags,
+ guint arg_pid,
+ GVfsBackend *backend);
void g_vfs_job_open_for_write_set_handle (GVfsJobOpenForWrite *job,
GVfsBackendHandle handle);
void g_vfs_job_open_for_write_set_can_seek (GVfsJobOpenForWrite *job,
gboolean can_seek);
+void g_vfs_job_open_for_write_set_can_truncate (GVfsJobOpenForWrite *job,
+ gboolean can_truncate);
void g_vfs_job_open_for_write_set_initial_offset (GVfsJobOpenForWrite *job,
goffset initial_offset);
GPid g_vfs_job_open_for_write_get_pid (GVfsJobOpenForWrite *job);
diff --git a/daemon/gvfsjobtruncate.c b/daemon/gvfsjobtruncate.c
new file mode 100644
index 00000000..bac32483
--- /dev/null
+++ b/daemon/gvfsjobtruncate.c
@@ -0,0 +1,128 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2013 Ross Lagerwall
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include "gvfswritechannel.h"
+#include "gvfsjobtruncate.h"
+#include "gvfsdaemonutils.h"
+
+G_DEFINE_TYPE (GVfsJobTruncate, g_vfs_job_truncate, G_VFS_TYPE_JOB)
+
+static void run (GVfsJob *job);
+static gboolean try (GVfsJob *job);
+static void send_reply (GVfsJob *job);
+
+static void
+g_vfs_job_truncate_finalize (GObject *object)
+{
+ GVfsJobTruncate *job;
+
+ job = G_VFS_JOB_TRUNCATE (object);
+ g_object_unref (job->channel);
+
+ if (G_OBJECT_CLASS (g_vfs_job_truncate_parent_class)->finalize)
+ (*G_OBJECT_CLASS (g_vfs_job_truncate_parent_class)->finalize) (object);
+}
+
+static void
+g_vfs_job_truncate_class_init (GVfsJobTruncateClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GVfsJobClass *job_class = G_VFS_JOB_CLASS (klass);
+
+ gobject_class->finalize = g_vfs_job_truncate_finalize;
+
+ job_class->run = run;
+ job_class->try = try;
+ job_class->send_reply = send_reply;
+}
+
+static void
+g_vfs_job_truncate_init (GVfsJobTruncate *job)
+{
+}
+
+GVfsJob *
+g_vfs_job_truncate_new (GVfsWriteChannel *channel,
+ GVfsBackendHandle handle,
+ goffset size,
+ GVfsBackend *backend)
+{
+ GVfsJobTruncate *job;
+
+ job = g_object_new (G_VFS_TYPE_JOB_TRUNCATE, NULL);
+
+ job->backend = backend;
+ job->channel = g_object_ref (channel);
+ job->handle = handle;
+ job->size = size;
+
+ return G_VFS_JOB (job);
+}
+
+/* Might be called on an i/o thread */
+static void
+send_reply (GVfsJob *job)
+{
+ GVfsJobTruncate *op_job = G_VFS_JOB_TRUNCATE (job);
+
+ g_debug ("job_truncate send reply\n");
+
+ if (job->failed)
+ g_vfs_channel_send_error (G_VFS_CHANNEL (op_job->channel), job->error);
+ else
+ g_vfs_write_channel_send_truncated (op_job->channel);
+}
+
+static void
+run (GVfsJob *job)
+{
+ GVfsJobTruncate *op_job = G_VFS_JOB_TRUNCATE (job);
+ GVfsBackendClass *class = G_VFS_BACKEND_GET_CLASS (op_job->backend);
+
+ if (class->truncate)
+ class->truncate (op_job->backend, op_job, op_job->handle, op_job->size);
+ else
+ g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Operation not supported by backend"));
+}
+
+static gboolean
+try (GVfsJob *job)
+{
+ GVfsJobTruncate *op_job = G_VFS_JOB_TRUNCATE (job);
+ GVfsBackendClass *class = G_VFS_BACKEND_GET_CLASS (op_job->backend);
+
+ if (class->try_truncate)
+ return class->try_truncate (op_job->backend,
+ op_job,
+ op_job->handle,
+ op_job->size);
+ else
+ return FALSE;
+}
diff --git a/daemon/gvfsjobtruncate.h b/daemon/gvfsjobtruncate.h
new file mode 100644
index 00000000..9a3f0494
--- /dev/null
+++ b/daemon/gvfsjobtruncate.h
@@ -0,0 +1,63 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2013 Ross Lagerwall
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __G_VFS_JOB_TRUNCATE_H__
+#define __G_VFS_JOB_TRUNCATE_H__
+
+#include <gvfsjob.h>
+#include <gvfsbackend.h>
+#include <gvfswritechannel.h>
+
+G_BEGIN_DECLS
+
+#define G_VFS_TYPE_JOB_TRUNCATE (g_vfs_job_truncate_get_type ())
+#define G_VFS_JOB_TRUNCATE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_VFS_TYPE_JOB_TRUNCATE, GVfsJobTruncate))
+#define G_VFS_JOB_TRUNCATE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_VFS_TYPE_JOB_TRUNCATE, GVfsJobTruncateClass))
+#define G_VFS_IS_JOB_TRUNCATE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_VFS_TYPE_JOB_TRUNCATE))
+#define G_VFS_IS_JOB_TRUNCATE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_VFS_TYPE_JOB_TRUNCATE))
+#define G_VFS_JOB_TRUNCATE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_VFS_TYPE_JOB_TRUNCATE, GVfsJobTruncateClass))
+
+typedef struct _GVfsJobTruncateClass GVfsJobTruncateClass;
+
+struct _GVfsJobTruncate
+{
+ GVfsJob parent_instance;
+
+ GVfsWriteChannel *channel;
+ GVfsBackend *backend;
+ GVfsBackendHandle handle;
+ goffset size;
+};
+
+struct _GVfsJobTruncateClass
+{
+ GVfsJobClass parent_class;
+};
+
+GType g_vfs_job_truncate_get_type (void) G_GNUC_CONST;
+
+GVfsJob *g_vfs_job_truncate_new (GVfsWriteChannel *channel,
+ GVfsBackendHandle handle,
+ goffset size,
+ GVfsBackend *backend);
+
+G_END_DECLS
+
+#endif /* __G_VFS_JOB_TRUNCATE_H__ */
diff --git a/daemon/gvfswritechannel.c b/daemon/gvfswritechannel.c
index bc71f76f..c691deb6 100644
--- a/daemon/gvfswritechannel.c
+++ b/daemon/gvfswritechannel.c
@@ -38,6 +38,7 @@
#include <gvfsdaemonutils.h>
#include <gvfsjobwrite.h>
#include <gvfsjobseekwrite.h>
+#include <gvfsjobtruncate.h>
#include <gvfsjobclosewrite.h>
#include <gvfsjobqueryinfowrite.h>
@@ -137,7 +138,12 @@ write_channel_handle_request (GVfsChannel *channel,
((goffset)arg1) | (((goffset)arg2) << 32),
backend);
break;
-
+ case G_VFS_DAEMON_SOCKET_PROTOCOL_REQUEST_TRUNCATE:
+ job = g_vfs_job_truncate_new (write_channel,
+ backend_handle,
+ ((goffset)arg1) | (((goffset)arg2) << 32),
+ backend);
+ break;
case G_VFS_DAEMON_SOCKET_PROTOCOL_REQUEST_QUERY_INFO:
attrs = g_strndup (data, data_len);
job = g_vfs_job_query_info_write_new (write_channel,
@@ -181,6 +187,24 @@ g_vfs_write_channel_send_seek_offset (GVfsWriteChannel *write_channel,
/* Might be called on an i/o thread
*/
void
+g_vfs_write_channel_send_truncated (GVfsWriteChannel *write_channel)
+{
+ GVfsDaemonSocketProtocolReply reply;
+ GVfsChannel *channel;
+
+ channel = G_VFS_CHANNEL (write_channel);
+
+ reply.type = g_htonl (G_VFS_DAEMON_SOCKET_PROTOCOL_REPLY_TRUNCATED);
+ reply.seq_nr = g_htonl (g_vfs_channel_get_current_seq_nr (channel));
+ reply.arg1 = g_htonl (0);
+ reply.arg2 = g_htonl (0);
+
+ g_vfs_channel_send_reply (channel, &reply, NULL, 0);
+}
+
+/* Might be called on an i/o thread
+ */
+void
g_vfs_write_channel_send_closed (GVfsWriteChannel *write_channel,
const char *etag)
{
diff --git a/daemon/gvfswritechannel.h b/daemon/gvfswritechannel.h
index 4fba2589..d721f238 100644
--- a/daemon/gvfswritechannel.h
+++ b/daemon/gvfswritechannel.h
@@ -55,6 +55,7 @@ void g_vfs_write_channel_send_closed (GVfsWriteChannel *write_
const char *etag);
void g_vfs_write_channel_send_seek_offset (GVfsWriteChannel *write_channel,
goffset offset);
+void g_vfs_write_channel_send_truncated (GVfsWriteChannel *write_channel);
G_END_DECLS