diff options
author | Alexander Larsson <alexl@redhat.com> | 2009-02-27 15:50:51 +0000 |
---|---|---|
committer | Alexander Larsson <alexl@src.gnome.org> | 2009-02-27 15:50:51 +0000 |
commit | afc01fa5e7f0d4626f578f9d92e17f9ba05003f0 (patch) | |
tree | 6af7428db5e0d6877c8d9c30946a162f53a76703 | |
parent | 415187aeda32a168c2270f93b9ebb7b07a785ba7 (diff) | |
download | gvfs-afc01fa5e7f0d4626f578f9d92e17f9ba05003f0.tar.gz |
Support query info on output streams
2009-02-27 Alexander Larsson <alexl@redhat.com>
* client/gdaemonfileoutputstream.c:
Support query info on output streams
* daemon/Makefile.am:
* daemon/gvfsbackend.h:
* daemon/gvfsjobqueryinfowrite.[ch]:
* daemon/gvfswritechannel.c:
Add query info write support.
* daemon/gvfsbackendtest.c:
Implement writing to files in test backend.
Implement query info on write
* test/test-query-info-stream.c:
Test g_file_output_stream_query_info().
svn path=/trunk/; revision=2260
-rw-r--r-- | ChangeLog | 18 | ||||
-rw-r--r-- | client/gdaemonfileoutputstream.c | 367 | ||||
-rw-r--r-- | daemon/Makefile.am | 1 | ||||
-rw-r--r-- | daemon/gvfsbackend.h | 67 | ||||
-rw-r--r-- | daemon/gvfsbackendtest.c | 203 | ||||
-rw-r--r-- | daemon/gvfsjobqueryinfowrite.c | 144 | ||||
-rw-r--r-- | daemon/gvfsjobqueryinfowrite.h | 67 | ||||
-rw-r--r-- | daemon/gvfswritechannel.c | 11 | ||||
-rw-r--r-- | test/test-query-info-stream.c | 89 |
9 files changed, 862 insertions, 105 deletions
@@ -1,6 +1,24 @@ 2009-02-27 Alexander Larsson <alexl@redhat.com> * client/gdaemonfileoutputstream.c: + Support query info on output streams + + * daemon/Makefile.am: + * daemon/gvfsbackend.h: + * daemon/gvfsjobqueryinfowrite.[ch]: + * daemon/gvfswritechannel.c: + Add query info write support. + + * daemon/gvfsbackendtest.c: + Implement writing to files in test backend. + Implement query info on write + + * test/test-query-info-stream.c: + Test g_file_output_stream_query_info(). + +2009-02-27 Alexander Larsson <alexl@redhat.com> + + * client/gdaemonfileoutputstream.c: Add and use g_string_remove_in_front helper function. 2009-02-27 Alexander Larsson <alexl@redhat.com> diff --git a/client/gdaemonfileoutputstream.c b/client/gdaemonfileoutputstream.c index 558acd56..fc89d0d3 100644 --- a/client/gdaemonfileoutputstream.c +++ b/client/gdaemonfileoutputstream.c @@ -40,6 +40,7 @@ #include "gdaemonfileoutputstream.h" #include "gvfsdaemondbus.h" #include <gvfsdaemonprotocol.h> +#include <gvfsfileinfo.h> #define MAX_WRITE_SIZE (4*1024*1024) @@ -116,6 +117,26 @@ typedef struct { guint32 seq_nr; } CloseOperation; +typedef enum { + QUERY_STATE_INIT = 0, + QUERY_STATE_WROTE_REQUEST, + QUERY_STATE_HANDLE_INPUT, +} QueryState; + +typedef struct { + QueryState state; + + /* Input */ + char *attributes; + + /* Output */ + GFileInfo *info; + GError *ret_error; + + gboolean sent_cancel; + + guint32 seq_nr; +} QueryOperation; typedef struct { gboolean cancelled; @@ -150,44 +171,55 @@ struct _GDaemonFileOutputStream { }; -static gssize g_daemon_file_output_stream_write (GOutputStream *stream, - const void *buffer, - gsize count, - GCancellable *cancellable, - GError **error); -static gboolean g_daemon_file_output_stream_close (GOutputStream *stream, - GCancellable *cancellable, - GError **error); -static GFileInfo *g_daemon_file_output_stream_query_info (GFileOutputStream *stream, - char *attributes, - GCancellable *cancellable, - GError **error); -static char *g_daemon_file_output_stream_get_etag (GFileOutputStream *stream); -static goffset g_daemon_file_output_stream_tell (GFileOutputStream *stream); -static gboolean g_daemon_file_output_stream_can_seek (GFileOutputStream *stream); -static gboolean g_daemon_file_output_stream_seek (GFileOutputStream *stream, - goffset offset, - GSeekType type, - GCancellable *cancellable, - GError **error); -static void g_daemon_file_output_stream_write_async (GOutputStream *stream, - const void *buffer, - gsize count, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer data); -static gssize g_daemon_file_output_stream_write_finish (GOutputStream *stream, - GAsyncResult *result, - GError **error); -static void g_daemon_file_output_stream_close_async (GOutputStream *stream, - int io_priority, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer data); -static gboolean g_daemon_file_output_stream_close_finish (GOutputStream *stream, - GAsyncResult *result, - GError **error); +static gssize g_daemon_file_output_stream_write (GOutputStream *stream, + const void *buffer, + gsize count, + GCancellable *cancellable, + GError **error); +static gboolean g_daemon_file_output_stream_close (GOutputStream *stream, + GCancellable *cancellable, + GError **error); +static GFileInfo *g_daemon_file_output_stream_query_info (GFileOutputStream *stream, + char *attributes, + GCancellable *cancellable, + GError **error); +static char *g_daemon_file_output_stream_get_etag (GFileOutputStream *stream); +static goffset g_daemon_file_output_stream_tell (GFileOutputStream *stream); +static gboolean g_daemon_file_output_stream_can_seek (GFileOutputStream *stream); +static gboolean g_daemon_file_output_stream_seek (GFileOutputStream *stream, + goffset offset, + GSeekType type, + GCancellable *cancellable, + GError **error); +static void g_daemon_file_output_stream_write_async (GOutputStream *stream, + const void *buffer, + gsize count, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer data); +static gssize g_daemon_file_output_stream_write_finish (GOutputStream *stream, + GAsyncResult *result, + GError **error); +static void g_daemon_file_output_stream_close_async (GOutputStream *stream, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer data); +static gboolean g_daemon_file_output_stream_close_finish (GOutputStream *stream, + GAsyncResult *result, + GError **error); +static void g_daemon_file_output_stream_query_info_async (GFileOutputStream *stream, + char *attributes, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +static GFileInfo *g_daemon_file_output_stream_query_info_finish (GFileOutputStream *stream, + GAsyncResult *result, + GError **error); + + G_DEFINE_TYPE (GDaemonFileOutputStream, g_daemon_file_output_stream, G_TYPE_FILE_OUTPUT_STREAM) @@ -246,6 +278,8 @@ g_daemon_file_output_stream_class_init (GDaemonFileOutputStreamClass *klass) file_stream_class->seek = g_daemon_file_output_stream_seek; 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; + file_stream_class->query_info_finish = g_daemon_file_output_stream_query_info_finish; } static void @@ -317,9 +351,10 @@ get_reply_header_missing_bytes (GString *buffer) type = g_ntohl (reply->type); arg2 = g_ntohl (reply->arg2); - /* ERROR and CLOSED has extra data w/ len in arg2 */ + /* ERROR, CLOSED and INFO has extra data w/ len in arg2 */ if (type == G_VFS_DAEMON_SOCKET_PROTOCOL_REPLY_ERROR || - type == G_VFS_DAEMON_SOCKET_PROTOCOL_REPLY_CLOSED) + type == G_VFS_DAEMON_SOCKET_PROTOCOL_REPLY_CLOSED || + type == G_VFS_DAEMON_SOCKET_PROTOCOL_REPLY_INFO) return G_VFS_DAEMON_SOCKET_PROTOCOL_REPLY_SIZE + arg2 - buffer->len; return 0; } @@ -885,7 +920,7 @@ iterate_seek_state_machine (GDaemonFileOutputStream *file, IOOperationData *io_o } /* Got full header */ - + { GVfsDaemonSocketProtocolReply reply; char *data; @@ -975,24 +1010,165 @@ g_daemon_file_output_stream_get_etag (GFileOutputStream *stream) return g_strdup (file->etag); } +static StateOp +iterate_query_state_machine (GDaemonFileOutputStream *file, + IOOperationData *io_op, + QueryOperation *op) +{ + gsize len; + guint32 request; + + while (TRUE) + { + switch (op->state) + { + /* Initial state for read op */ + case QUERY_STATE_INIT: + request = G_VFS_DAEMON_SOCKET_PROTOCOL_REQUEST_QUERY_INFO; + append_request (file, request, + 0, + 0, + strlen (op->attributes), + &op->seq_nr); + g_string_append (file->output_buffer, + op->attributes); + + op->state = QUERY_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; /* Allow cancel before first byte of request sent */ + return STATE_OP_WRITE; + + /* wrote parts of output_buffer */ + case QUERY_STATE_WROTE_REQUEST: + if (io_op->io_cancelled) + { + op->info = NULL; + 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 = QUERY_STATE_HANDLE_INPUT; + break; + + /* No op */ + case QUERY_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 = QUERY_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; + } + + /* Got full header */ + + { + 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->info = NULL; + 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_INFO) + { + op->info = gvfs_file_info_demarshal (data, reply.arg2); + 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 = SEEK_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 GFileInfo * g_daemon_file_output_stream_query_info (GFileOutputStream *stream, char *attributes, GCancellable *cancellable, GError **error) { -#if 0 - GDaemonFileOutputStream *file; + GDaemonFileOutputStream *file; + QueryOperation op; file = G_DAEMON_FILE_OUTPUT_STREAM (stream); -#endif + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return NULL; - g_set_error (error, - G_IO_ERROR, - G_IO_ERROR_NOT_SUPPORTED, - _("Query info not supported on stream")); + memset (&op, 0, sizeof (op)); + op.state = QUERY_STATE_INIT; + if (attributes) + op.attributes = attributes; + else + op.attributes = ""; + + if (!run_sync_state_machine (file, (state_machine_iterator)iterate_query_state_machine, + &op, cancellable, error)) + return NULL; /* IO Error */ - return NULL; + if (op.info == NULL) + g_propagate_error (error, op.ret_error); + + return op.info; } /************************************************************************ @@ -1374,3 +1550,96 @@ g_daemon_file_output_stream_close_finish (GOutputStream *stream, /* Failures handled in generic close_finish code */ return TRUE; } + +static void +async_query_done (GOutputStream *stream, + gpointer op_data, + GAsyncReadyCallback callback, + gpointer user_data, + GError *io_error) +{ + GDaemonFileOutputStream *file; + GSimpleAsyncResult *simple; + QueryOperation *op; + GFileInfo *info; + GError *error; + + file = G_DAEMON_FILE_OUTPUT_STREAM (stream); + + op = op_data; + + if (io_error) + { + info = NULL; + error = io_error; + } + else + { + info = op->info; + error = op->ret_error; + } + + simple = g_simple_async_result_new (G_OBJECT (stream), + callback, user_data, + g_daemon_file_output_stream_query_info_async); + + if (info == NULL) + g_simple_async_result_set_from_error (simple, error); + else + g_simple_async_result_set_op_res_gpointer (simple, info, + g_object_unref); + + /* Complete immediately, not in idle, since we're already in a mainloop callout */ + g_simple_async_result_complete (simple); + g_object_unref (simple); + + if (op->ret_error) + g_error_free (op->ret_error); + g_free (op->attributes); + g_free (op); +} + +static void +g_daemon_file_output_stream_query_info_async (GFileOutputStream *stream, + char *attributes, + int io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GDaemonFileOutputStream *file; + QueryOperation *op; + + file = G_DAEMON_FILE_OUTPUT_STREAM (stream); + + op = g_new0 (QueryOperation, 1); + op->state = QUERY_STATE_INIT; + if (attributes) + op->attributes = g_strdup (attributes); + else + op->attributes = g_strdup (""); + + run_async_state_machine (file, + (state_machine_iterator)iterate_query_state_machine, + op, io_priority, + callback, user_data, + cancellable, + async_query_done); +} + +static GFileInfo * +g_daemon_file_output_stream_query_info_finish (GFileOutputStream *stream, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + GFileInfo *info; + + simple = G_SIMPLE_ASYNC_RESULT (result); + g_assert (g_simple_async_result_get_source_tag (simple) == g_daemon_file_output_stream_query_info_async); + + info = g_simple_async_result_get_op_res_gpointer (simple); + + return g_object_ref (info); + +} diff --git a/daemon/Makefile.am b/daemon/Makefile.am index dc21955d..58a32a5c 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -135,6 +135,7 @@ libdaemon_la_SOURCES = \ gvfsjobclosewrite.c gvfsjobclosewrite.h \ gvfsjobqueryinfo.c gvfsjobqueryinfo.h \ gvfsjobqueryinforead.c gvfsjobqueryinforead.h \ + gvfsjobqueryinfowrite.c gvfsjobqueryinfowrite.h \ gvfsjobqueryfsinfo.c gvfsjobqueryfsinfo.h \ gvfsjobenumerate.c gvfsjobenumerate.h \ gvfsjobsetdisplayname.c gvfsjobsetdisplayname.h \ diff --git a/daemon/gvfsbackend.h b/daemon/gvfsbackend.h index e1393aaf..243d978c 100644 --- a/daemon/gvfsbackend.h +++ b/daemon/gvfsbackend.h @@ -43,35 +43,36 @@ typedef struct _GVfsBackend GVfsBackend; typedef struct _GVfsBackendPrivate GVfsBackendPrivate; typedef struct _GVfsBackendClass GVfsBackendClass; -typedef struct _GVfsJobMount GVfsJobMount; -typedef struct _GVfsJobUnmount GVfsJobUnmount; -typedef struct _GVfsJobMountMountable GVfsJobMountMountable; +typedef struct _GVfsJobMount GVfsJobMount; +typedef struct _GVfsJobUnmount GVfsJobUnmount; +typedef struct _GVfsJobMountMountable GVfsJobMountMountable; typedef struct _GVfsJobUnmountMountable GVfsJobUnmountMountable; -typedef struct _GVfsJobOpenForRead GVfsJobOpenForRead; -typedef struct _GVfsJobOpenIconForRead GVfsJobOpenIconForRead; -typedef struct _GVfsJobSeekRead GVfsJobSeekRead; -typedef struct _GVfsJobCloseRead GVfsJobCloseRead; -typedef struct _GVfsJobRead GVfsJobRead; -typedef struct _GVfsJobOpenForWrite GVfsJobOpenForWrite; -typedef struct _GVfsJobWrite GVfsJobWrite; -typedef struct _GVfsJobSeekWrite GVfsJobSeekWrite; -typedef struct _GVfsJobCloseWrite GVfsJobCloseWrite; -typedef struct _GVfsJobQueryInfo GVfsJobQueryInfo; -typedef struct _GVfsJobQueryInfoRead GVfsJobQueryInfoRead; -typedef struct _GVfsJobQueryFsInfo GVfsJobQueryFsInfo; -typedef struct _GVfsJobEnumerate GVfsJobEnumerate; -typedef struct _GVfsJobSetDisplayName GVfsJobSetDisplayName; -typedef struct _GVfsJobTrash GVfsJobTrash; -typedef struct _GVfsJobDelete GVfsJobDelete; -typedef struct _GVfsJobMakeDirectory GVfsJobMakeDirectory; -typedef struct _GVfsJobMakeSymlink GVfsJobMakeSymlink; -typedef struct _GVfsJobCopy GVfsJobCopy; -typedef struct _GVfsJobMove GVfsJobMove; -typedef struct _GVfsJobPush GVfsJobPush; -typedef struct _GVfsJobPull GVfsJobPull; -typedef struct _GVfsJobSetAttribute GVfsJobSetAttribute; -typedef struct _GVfsJobQueryAttributes GVfsJobQueryAttributes; -typedef struct _GVfsJobCreateMonitor GVfsJobCreateMonitor; +typedef struct _GVfsJobOpenForRead GVfsJobOpenForRead; +typedef struct _GVfsJobOpenIconForRead GVfsJobOpenIconForRead; +typedef struct _GVfsJobSeekRead GVfsJobSeekRead; +typedef struct _GVfsJobCloseRead GVfsJobCloseRead; +typedef struct _GVfsJobRead GVfsJobRead; +typedef struct _GVfsJobOpenForWrite GVfsJobOpenForWrite; +typedef struct _GVfsJobWrite GVfsJobWrite; +typedef struct _GVfsJobSeekWrite GVfsJobSeekWrite; +typedef struct _GVfsJobCloseWrite GVfsJobCloseWrite; +typedef struct _GVfsJobQueryInfo GVfsJobQueryInfo; +typedef struct _GVfsJobQueryInfoRead GVfsJobQueryInfoRead; +typedef struct _GVfsJobQueryInfoWrite GVfsJobQueryInfoWrite; +typedef struct _GVfsJobQueryFsInfo GVfsJobQueryFsInfo; +typedef struct _GVfsJobEnumerate GVfsJobEnumerate; +typedef struct _GVfsJobSetDisplayName GVfsJobSetDisplayName; +typedef struct _GVfsJobTrash GVfsJobTrash; +typedef struct _GVfsJobDelete GVfsJobDelete; +typedef struct _GVfsJobMakeDirectory GVfsJobMakeDirectory; +typedef struct _GVfsJobMakeSymlink GVfsJobMakeSymlink; +typedef struct _GVfsJobCopy GVfsJobCopy; +typedef struct _GVfsJobMove GVfsJobMove; +typedef struct _GVfsJobPush GVfsJobPush; +typedef struct _GVfsJobPull GVfsJobPull; +typedef struct _GVfsJobSetAttribute GVfsJobSetAttribute; +typedef struct _GVfsJobQueryAttributes GVfsJobQueryAttributes; +typedef struct _GVfsJobCreateMonitor GVfsJobCreateMonitor; typedef gpointer GVfsBackendHandle; @@ -248,6 +249,16 @@ struct _GVfsBackendClass GVfsBackendHandle handle, GFileInfo *info, GFileAttributeMatcher *attribute_matcher); + void (*query_info_on_write)(GVfsBackend *backend, + GVfsJobQueryInfoWrite *job, + GVfsBackendHandle handle, + GFileInfo *info, + GFileAttributeMatcher *attribute_matcher); + gboolean (*try_query_info_on_write)(GVfsBackend *backend, + GVfsJobQueryInfoWrite *job, + GVfsBackendHandle handle, + GFileInfo *info, + GFileAttributeMatcher *attribute_matcher); void (*query_fs_info) (GVfsBackend *backend, GVfsJobQueryFsInfo *job, const char *filename, diff --git a/daemon/gvfsbackendtest.c b/daemon/gvfsbackendtest.c index f6616696..ccc6e3af 100644 --- a/daemon/gvfsbackendtest.c +++ b/daemon/gvfsbackendtest.c @@ -39,6 +39,10 @@ #include "gvfsjobseekread.h" #include "gvfsjobqueryinfo.h" #include "gvfsjobenumerate.h" +#include "gvfsjobwrite.h" +#include "gvfsjobopenforwrite.h" +#include "gvfsjobclosewrite.h" +#include "gvfsjobqueryinfowrite.h" G_DEFINE_TYPE (GVfsBackendTest, g_vfs_backend_test, G_VFS_TYPE_BACKEND) @@ -334,6 +338,197 @@ do_query_info (GVfsBackend *backend, g_object_unref (file); } +static void +do_create (GVfsBackend *backend, + GVfsJobOpenForWrite *job, + const char *filename, + GFileCreateFlags flags) +{ + GFile *file; + GFileOutputStream *out; + GError *error; + + file = g_vfs_get_file_for_path (g_vfs_get_local (), + filename); + + error = NULL; + out = g_file_create (file, flags, G_VFS_JOB (job)->cancellable, &error); + g_object_unref (file); + if (out) + { + g_vfs_job_open_for_write_set_can_seek (job, FALSE); + g_vfs_job_open_for_write_set_handle (job, out); + g_vfs_job_succeeded (G_VFS_JOB (job)); + } + else + { + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); + g_error_free (error); + } +} + +static void +do_append_to (GVfsBackend *backend, + GVfsJobOpenForWrite *job, + const char *filename, + GFileCreateFlags flags) +{ + GFile *file; + GFileOutputStream *out; + GError *error; + + file = g_vfs_get_file_for_path (g_vfs_get_local (), + filename); + + error = NULL; + out = g_file_append_to (file, flags, G_VFS_JOB (job)->cancellable, &error); + g_object_unref (file); + if (out) + { + g_vfs_job_open_for_write_set_can_seek (job, FALSE); + g_vfs_job_open_for_write_set_handle (job, out); + g_vfs_job_succeeded (G_VFS_JOB (job)); + } + else + { + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); + g_error_free (error); + } +} + +static void +do_replace (GVfsBackend *backend, + GVfsJobOpenForWrite *job, + const char *filename, + const char *etag, + gboolean make_backup, + GFileCreateFlags flags) +{ + GFile *file; + GFileOutputStream *out; + GError *error; + + file = g_vfs_get_file_for_path (g_vfs_get_local (), + filename); + + error = NULL; + out = g_file_replace (file, + etag, make_backup, + flags, G_VFS_JOB (job)->cancellable, + &error); + g_object_unref (file); + if (out) + { + g_vfs_job_open_for_write_set_can_seek (job, FALSE); + g_vfs_job_open_for_write_set_handle (job, out); + g_vfs_job_succeeded (G_VFS_JOB (job)); + } + else + { + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); + g_error_free (error); + } +} + +static void +do_close_write (GVfsBackend *backend, + GVfsJobCloseWrite *job, + GVfsBackendHandle handle) +{ + GFileOutputStream *out; + GError *error; + char *etag; + + out = (GFileOutputStream *)handle; + + error = NULL; + if (!g_output_stream_close (G_OUTPUT_STREAM (out), + G_VFS_JOB (job)->cancellable, + &error)) + { + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); + g_error_free (error); + } + else + { + etag = g_file_output_stream_get_etag (out); + + if (etag) + { + g_vfs_job_close_write_set_etag (job, etag); + g_free (etag); + } + + g_vfs_job_succeeded (G_VFS_JOB (job)); + } + + g_object_unref (out); +} + +static void +do_write (GVfsBackend *backend, + GVfsJobWrite *job, + GVfsBackendHandle handle, + char *buffer, + gsize buffer_size) +{ + GFileOutputStream *out; + GError *error; + gssize res; + + g_print ("do_write\n"); + + out = (GFileOutputStream *)handle; + + error = NULL; + res = g_output_stream_write (G_OUTPUT_STREAM (out), + buffer, buffer_size, + G_VFS_JOB (job)->cancellable, + &error); + if (res < 0) + { + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); + g_error_free (error); + } + else + { + g_vfs_job_write_set_written_size (job, res); + g_vfs_job_succeeded (G_VFS_JOB (job)); + } +} + +static void +do_query_info_on_write (GVfsBackend *backend, + GVfsJobQueryInfoWrite *job, + GVfsBackendHandle handle, + GFileInfo *info, + GFileAttributeMatcher *attribute_matcher) +{ + GFileOutputStream *out; + GError *error; + GFileInfo *info2; + + g_print ("do_query_info_on_write\n"); + + out = (GFileOutputStream *)handle; + + error = NULL; + info2 = g_file_output_stream_query_info (out, job->attributes, + G_VFS_JOB (job)->cancellable, + &error); + if (info2 == NULL) + { + g_vfs_job_failed_from_error (G_VFS_JOB (job), error); + g_error_free (error); + } + else + { + g_file_info_copy_into (info2, info); + g_object_unref (info2); + g_vfs_job_succeeded (G_VFS_JOB (job)); + } +} + static gboolean try_enumerate (GVfsBackend *backend, GVfsJobEnumerate *job, @@ -384,6 +579,14 @@ g_vfs_backend_test_class_init (GVfsBackendTestClass *klass) backend_class->seek_on_read = do_seek_on_read; backend_class->query_info_on_read = do_query_info_on_read; backend_class->close_read = do_close_read; + + backend_class->replace = do_replace; + backend_class->create = do_create; + backend_class->append_to = do_append_to; + backend_class->write = do_write; + backend_class->query_info_on_write = do_query_info_on_write; + backend_class->close_write = do_close_write; + backend_class->query_info = do_query_info; backend_class->try_enumerate = try_enumerate; } diff --git a/daemon/gvfsjobqueryinfowrite.c b/daemon/gvfsjobqueryinfowrite.c new file mode 100644 index 00000000..7b454f35 --- /dev/null +++ b/daemon/gvfsjobqueryinfowrite.c @@ -0,0 +1,144 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2006-2007 Red Hat, Inc. + * + * 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., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Alexander Larsson <alexl@redhat.com> + */ + +#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 "gvfsjobqueryinfowrite.h" +#include "gvfsdaemonutils.h" + +G_DEFINE_TYPE (GVfsJobQueryInfoWrite, g_vfs_job_query_info_write, 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_query_info_write_finalize (GObject *object) +{ + GVfsJobQueryInfoWrite *job; + + job = G_VFS_JOB_QUERY_INFO_WRITE (object); + g_object_unref (job->channel); + g_object_unref (job->file_info); + g_free (job->attributes); + g_file_attribute_matcher_unref (job->attribute_matcher); + + if (G_OBJECT_CLASS (g_vfs_job_query_info_write_parent_class)->finalize) + (*G_OBJECT_CLASS (g_vfs_job_query_info_write_parent_class)->finalize) (object); +} + +static void +g_vfs_job_query_info_write_class_init (GVfsJobQueryInfoWriteClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GVfsJobClass *job_class = G_VFS_JOB_CLASS (klass); + + gobject_class->finalize = g_vfs_job_query_info_write_finalize; + + job_class->run = run; + job_class->try = try; + job_class->send_reply = send_reply; +} + +static void +g_vfs_job_query_info_write_init (GVfsJobQueryInfoWrite *job) +{ +} + +GVfsJob * +g_vfs_job_query_info_write_new (GVfsWriteChannel *channel, + GVfsBackendHandle handle, + const char *attrs, + GVfsBackend *backend) +{ + GVfsJobQueryInfoWrite *job; + + job = g_object_new (G_VFS_TYPE_JOB_QUERY_INFO_WRITE, + NULL); + + job->backend = backend; + job->channel = g_object_ref (channel); + job->handle = handle; + job->attributes = g_strdup (attrs); + job->attribute_matcher = g_file_attribute_matcher_new (attrs); + + job->file_info = g_file_info_new (); + g_file_info_set_attribute_mask (job->file_info, job->attribute_matcher); + + return G_VFS_JOB (job); +} + +/* Might be called on an i/o thwrite */ +static void +send_reply (GVfsJob *job) +{ + GVfsJobQueryInfoWrite *op_job = G_VFS_JOB_QUERY_INFO_WRITE (job); + + if (job->failed) + g_vfs_channel_send_error (G_VFS_CHANNEL (op_job->channel), job->error); + else + g_vfs_channel_send_info (G_VFS_CHANNEL (op_job->channel), op_job->file_info); +} + +static void +run (GVfsJob *job) +{ + GVfsJobQueryInfoWrite *op_job = G_VFS_JOB_QUERY_INFO_WRITE (job); + GVfsBackendClass *class = G_VFS_BACKEND_GET_CLASS (op_job->backend); + + if (class->query_info_on_write == NULL) + { + g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + _("Operation not supported by backend")); + return; + } + + class->query_info_on_write (op_job->backend, + op_job, + op_job->handle, + op_job->file_info, + op_job->attribute_matcher); +} + +static gboolean +try (GVfsJob *job) +{ + GVfsJobQueryInfoWrite *op_job = G_VFS_JOB_QUERY_INFO_WRITE (job); + GVfsBackendClass *class = G_VFS_BACKEND_GET_CLASS (op_job->backend); + + if (class->try_query_info_on_write == NULL) + return FALSE; + + return class->try_query_info_on_write (op_job->backend, + op_job, + op_job->handle, + op_job->file_info, + op_job->attribute_matcher); +} diff --git a/daemon/gvfsjobqueryinfowrite.h b/daemon/gvfsjobqueryinfowrite.h new file mode 100644 index 00000000..1a2ac4cf --- /dev/null +++ b/daemon/gvfsjobqueryinfowrite.h @@ -0,0 +1,67 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 2006-2009 Red Hat, Inc. + * + * 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., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Alexander Larsson <alexl@redhat.com> + */ + +#ifndef __G_VFS_JOB_QUERY_INFO_WRITE_H__ +#define __G_VFS_JOB_QUERY_INFO_WRITE_H__ + +#include <gvfsjob.h> +#include <gvfsbackend.h> +#include <gvfswritechannel.h> + +G_BEGIN_DECLS + +#define G_VFS_TYPE_JOB_QUERY_INFO_WRITE (g_vfs_job_query_info_write_get_type ()) +#define G_VFS_JOB_QUERY_INFO_WRITE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_VFS_TYPE_JOB_QUERY_INFO_WRITE, GVfsJobQueryInfoWrite)) +#define G_VFS_JOB_QUERY_INFO_WRITE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_VFS_TYPE_JOB_QUERY_INFO_WRITE, GVfsJobQueryInfoWriteClass)) +#define G_VFS_IS_JOB_QUERY_INFO_WRITE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_VFS_TYPE_JOB_QUERY_INFO_WRITE)) +#define G_VFS_IS_JOB_QUERY_INFO_WRITE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_VFS_TYPE_JOB_QUERY_INFO_WRITE)) +#define G_VFS_JOB_QUERY_INFO_WRITE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_VFS_TYPE_JOB_QUERY_INFO_WRITE, GVfsJobQueryInfoWriteClass)) + +typedef struct _GVfsJobQueryInfoWriteClass GVfsJobQueryInfoWriteClass; + +struct _GVfsJobQueryInfoWrite +{ + GVfsJob parent_instance; + + GVfsWriteChannel *channel; + GVfsBackend *backend; + GVfsBackendHandle handle; + char *attributes; + GFileAttributeMatcher *attribute_matcher; + + GFileInfo *file_info; +}; + +struct _GVfsJobQueryInfoWriteClass +{ + GVfsJobClass parent_class; +}; + +GType g_vfs_job_query_info_write_get_type (void) G_GNUC_CONST; + +GVfsJob *g_vfs_job_query_info_write_new (GVfsWriteChannel *channel, + GVfsBackendHandle handle, + const char *attrs, + GVfsBackend *backend); +G_END_DECLS + +#endif /* __G_VFS_JOB_QUERY_INFO_WRITE_H__ */ diff --git a/daemon/gvfswritechannel.c b/daemon/gvfswritechannel.c index e340914b..1646aceb 100644 --- a/daemon/gvfswritechannel.c +++ b/daemon/gvfswritechannel.c @@ -39,6 +39,7 @@ #include <gvfsjobwrite.h> #include <gvfsjobseekwrite.h> #include <gvfsjobclosewrite.h> +#include <gvfsjobqueryinfowrite.h> struct _GVfsWriteChannel { @@ -103,6 +104,7 @@ write_channel_handle_request (GVfsChannel *channel, GVfsBackendHandle backend_handle; GVfsBackend *backend; GVfsWriteChannel *write_channel; + char *attrs; write_channel = G_VFS_WRITE_CHANNEL (channel); backend_handle = g_vfs_channel_get_backend_handle (channel); @@ -136,6 +138,15 @@ write_channel_handle_request (GVfsChannel *channel, 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, + backend_handle, + attrs, + backend); + g_free (attrs); + break; + default: g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, diff --git a/test/test-query-info-stream.c b/test/test-query-info-stream.c index 401a6ea0..d1288900 100644 --- a/test/test-query-info-stream.c +++ b/test/test-query-info-stream.c @@ -86,29 +86,9 @@ allocate_block (gsize size) } static void -create_file (GFile *file, gsize size) -{ - guchar *data; - GError *error; - - data = allocate_block (size); - - error = NULL; - if (!g_file_replace_contents (file, - (char *)data, - size, - NULL, FALSE, 0, - NULL, NULL, &error)) - { - g_print ("error creating file: %s\n", error->message); - exit (1); - } - g_free (data); -} - -static void check_query_info_res (GFileInfo *info, - GError *error) + GError *error, + gsize expected_size) { goffset file_size; @@ -125,16 +105,69 @@ check_query_info_res (GFileInfo *info, } file_size = g_file_info_get_size (info); - if (file_size != 100*1000) + if (file_size != expected_size) { g_print ("wrong file size\n"); exit (1); } } +static void +check_query_info_out (GFileOutputStream *out, gsize expected_size) +{ + GFileInfo *info; + GError *error; + + error = NULL; + info = g_file_output_stream_query_info (out, "*", NULL, &error); + + check_query_info_res (info, error, expected_size); +} + +static void +create_file (GFile *file, gsize size) +{ + GFileOutputStream *out; + guchar *data; + gsize written; + GError *error; + + data = allocate_block (size); + + error = NULL; + out = g_file_replace (file, NULL, FALSE, 0, NULL, &error); + if (out == NULL) + { + g_print ("error creating file: %s\n", error->message); + exit (1); + } + + check_query_info_out (out, 0); + + if (!g_output_stream_write_all (G_OUTPUT_STREAM (out), + data, size, + &written, + NULL, &error)) + { + g_print ("error writing to file: %s\n", error->message); + exit (1); + } + + check_query_info_out (out, written); + + if (written != size) + { + g_print ("not all data written to file\n"); + exit (1); + } + + g_output_stream_close (G_OUTPUT_STREAM (out), NULL, NULL); + + g_free (data); +} static void -check_query_info (GFileInputStream *in) +check_query_info (GFileInputStream *in, gsize expected_size) { GFileInfo *info; GError *error; @@ -142,7 +175,7 @@ check_query_info (GFileInputStream *in) error = NULL; info = g_file_input_stream_query_info (in, "*", NULL, &error); - check_query_info_res (info, error); + check_query_info_res (info, error, expected_size); } static void @@ -158,7 +191,7 @@ async_cb (GObject *source_object, g_file_input_stream_query_info_finish (G_FILE_INPUT_STREAM (source_object), res, &error); - check_query_info_res (info, error); + check_query_info_res (info, error, 100*1000); g_main_loop_quit (main_loop); } @@ -209,7 +242,7 @@ main (int argc, char *argv[]) exit (1); } - check_query_info (in); + check_query_info (in, 100*1000); buffer = malloc (100*1000); @@ -238,7 +271,7 @@ main (int argc, char *argv[]) read_size += res; - check_query_info (in); + check_query_info (in, 100*1000); } while (1); |