diff options
-rw-r--r-- | client/gdaemonfile.c | 66 | ||||
-rw-r--r-- | client/gdaemonfileoutputstream.c | 201 | ||||
-rw-r--r-- | client/gdaemonfileoutputstream.h | 2 | ||||
-rw-r--r-- | common/gvfsdaemonprotocol.h | 6 | ||||
-rw-r--r-- | common/org.gtk.vfs.xml | 12 | ||||
-rw-r--r-- | daemon/Makefile.am | 1 | ||||
-rw-r--r-- | daemon/gvfsbackend.c | 1 | ||||
-rw-r--r-- | daemon/gvfsbackend.h | 9 | ||||
-rw-r--r-- | daemon/gvfsjobopenforwrite.c | 107 | ||||
-rw-r--r-- | daemon/gvfsjobopenforwrite.h | 22 | ||||
-rw-r--r-- | daemon/gvfsjobtruncate.c | 128 | ||||
-rw-r--r-- | daemon/gvfsjobtruncate.h | 63 | ||||
-rw-r--r-- | daemon/gvfswritechannel.c | 26 | ||||
-rw-r--r-- | daemon/gvfswritechannel.h | 1 |
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 |