diff options
author | Alexander Larsson <alexl@redhat.com> | 2009-02-27 12:59:47 +0000 |
---|---|---|
committer | Alexander Larsson <alexl@src.gnome.org> | 2009-02-27 12:59:47 +0000 |
commit | 28713ed6b20cb617bce93e639b764617a118e1dd (patch) | |
tree | 8e840db7df291d1c60a55eb0aba9364ae89bde73 | |
parent | 4c1cbdac8d59b1bf53319bc9e5a165c8b11adacb (diff) | |
download | gvfs-28713ed6b20cb617bce93e639b764617a118e1dd.tar.gz |
Add (de)marshalling functions for GFileInfos.
2009-02-27 Alexander Larsson <alexl@redhat.com>
* common/Makefile.am:
* common/gvfsfileinfo.[ch]:
Add (de)marshalling functions for GFileInfos.
* common/gvfsdaemonprotocol.h:
Add protocol extensions for query info over streams
* client/gdaemonfileinputstream.c:
Support sync query_info.
* daemon/Makefile.am:
* daemon/gvfsjobqueryinforead.[ch]:
* daemon/gvfsbackend.h:
Added query info job and backend call for input streams
* daemon/gvfsbackendtest.c:
Implement query_info_on_read
* daemon/gvfschannel.[ch]:
Add g_vfs_channel_send_info
* daemon/gvfsreadchannel.c:
(read_channel_handle_request):
Handle query info
* test/Makefile.am:
* test/test-query-info-stream.c:
Add test for stream query info.
svn path=/trunk/; revision=2257
-rw-r--r-- | ChangeLog | 31 | ||||
-rw-r--r-- | client/gdaemonfileinputstream.c | 419 | ||||
-rw-r--r-- | common/Makefile.am | 1 | ||||
-rw-r--r-- | common/gvfsdaemonprotocol.h | 6 | ||||
-rw-r--r-- | common/gvfsfileinfo.c | 278 | ||||
-rw-r--r-- | common/gvfsfileinfo.h | 37 | ||||
-rw-r--r-- | daemon/Makefile.am | 1 | ||||
-rw-r--r-- | daemon/gvfsbackend.h | 11 | ||||
-rw-r--r-- | daemon/gvfsbackendtest.c | 39 | ||||
-rw-r--r-- | daemon/gvfschannel.c | 21 | ||||
-rw-r--r-- | daemon/gvfschannel.h | 2 | ||||
-rw-r--r-- | daemon/gvfsjobqueryinforead.c | 144 | ||||
-rw-r--r-- | daemon/gvfsjobqueryinforead.h | 67 | ||||
-rw-r--r-- | daemon/gvfsreadchannel.c | 13 | ||||
-rw-r--r-- | test/Makefile.am | 1 | ||||
-rw-r--r-- | test/test-query-info-stream.c | 226 |
16 files changed, 1270 insertions, 27 deletions
@@ -1,3 +1,34 @@ +2009-02-27 Alexander Larsson <alexl@redhat.com> + + * common/Makefile.am: + * common/gvfsfileinfo.[ch]: + Add (de)marshalling functions for GFileInfos. + + * common/gvfsdaemonprotocol.h: + Add protocol extensions for query info over streams + + * client/gdaemonfileinputstream.c: + Support sync query_info. + + * daemon/Makefile.am: + * daemon/gvfsjobqueryinforead.[ch]: + * daemon/gvfsbackend.h: + Added query info job and backend call for input streams + + * daemon/gvfsbackendtest.c: + Implement query_info_on_read + + * daemon/gvfschannel.[ch]: + Add g_vfs_channel_send_info + + * daemon/gvfsreadchannel.c: + (read_channel_handle_request): + Handle query info + + * test/Makefile.am: + * test/test-query-info-stream.c: + Add test for stream query info. + 2009-02-26 Alexander Larsson <alexl@redhat.com> Bug 570977 – sftp backend sends trailing zeros to communication data diff --git a/client/gdaemonfileinputstream.c b/client/gdaemonfileinputstream.c index 8cdc3622..62bbdc5d 100644 --- a/client/gdaemonfileinputstream.c +++ b/client/gdaemonfileinputstream.c @@ -62,6 +62,7 @@ #include "gdaemonfileinputstream.h" #include "gvfsdaemondbus.h" #include <gvfsdaemonprotocol.h> +#include <gvfsfileinfo.h> #define MAX_READ_SIZE (4*1024*1024) @@ -151,6 +152,30 @@ typedef struct { guint32 seq_nr; } CloseOperation; +typedef enum { + QUERY_STATE_INIT = 0, + QUERY_STATE_WROTE_REQUEST, + QUERY_STATE_HANDLE_INPUT, + QUERY_STATE_HANDLE_INPUT_BLOCK, + QUERY_STATE_HANDLE_HEADER, + QUERY_STATE_READ_BLOCK, + QUERY_STATE_SKIP_BLOCK +} 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; @@ -164,7 +189,15 @@ typedef struct { gboolean io_cancelled; } IOOperationData; -typedef StateOp (*state_machine_iterator) (GDaemonFileInputStream *file, IOOperationData *io_op, gpointer data); +typedef struct { + char *data; + gsize len; + int seek_generation; +} PreRead; + +typedef StateOp (*state_machine_iterator) (GDaemonFileInputStream *file, + IOOperationData *io_op, + gpointer data); struct _GDaemonFileInputStream { GFileInputStream parent; @@ -177,6 +210,8 @@ struct _GDaemonFileInputStream { guint32 seq_nr; goffset current_offset; + GList *pre_reads; + InputState input_state; gsize input_block_size; int input_block_seek_generation; @@ -241,6 +276,24 @@ G_DEFINE_TYPE (GDaemonFileInputStream, g_daemon_file_input_stream, G_TYPE_FILE_INPUT_STREAM) static void +pre_read_free (PreRead *pre) +{ + g_free (pre->data); + g_free (pre); +} + +static void +g_string_remove_in_front (GString *string, + gsize bytes) +{ + memmove (string->str, + string->str + bytes, + string->len - bytes); + g_string_truncate (string, + string->len - bytes); +} + +static void g_daemon_file_input_stream_finalize (GObject *object) { GDaemonFileInputStream *file; @@ -252,6 +305,14 @@ g_daemon_file_input_stream_finalize (GObject *object) if (file->data_stream) g_object_unref (file->data_stream); + while (file->pre_reads) + { + PreRead *pre = file->pre_reads->data; + file->pre_reads = g_list_delete_link (file->pre_reads, + file->pre_reads); + pre_read_free (pre); + } + g_string_free (file->input_buffer, TRUE); g_string_free (file->output_buffer, TRUE); @@ -322,7 +383,8 @@ error_is_cancel (GError *error) static void append_request (GDaemonFileInputStream *stream, guint32 command, - guint32 arg1, guint32 arg2, guint32 *seq_nr) + guint32 arg1, guint32 arg2, guint32 data_len, + guint32 *seq_nr) { GVfsDaemonSocketProtocolRequest cmd; @@ -335,7 +397,7 @@ append_request (GDaemonFileInputStream *stream, guint32 command, cmd.seq_nr = g_htonl (stream->seq_nr++); cmd.arg1 = g_htonl (arg1); cmd.arg2 = g_htonl (arg2); - cmd.data_len = 0; + cmd.data_len = g_htonl (data_len); g_string_append_len (stream->output_buffer, (char *)&cmd, G_VFS_DAEMON_SOCKET_PROTOCOL_REQUEST_SIZE); @@ -356,7 +418,8 @@ get_reply_header_missing_bytes (GString *buffer) type = g_ntohl (reply->type); arg2 = g_ntohl (reply->arg2); - if (type == G_VFS_DAEMON_SOCKET_PROTOCOL_REPLY_ERROR) + if (type == G_VFS_DAEMON_SOCKET_PROTOCOL_REPLY_ERROR || + type == G_VFS_DAEMON_SOCKET_PROTOCOL_REPLY_INFO) return G_VFS_DAEMON_SOCKET_PROTOCOL_REPLY_SIZE + arg2 - buffer->len; return 0; } @@ -480,6 +543,7 @@ static StateOp iterate_read_state_machine (GDaemonFileInputStream *file, IOOperationData *io_op, ReadOperation *op) { gsize len; + PreRead *pre; while (TRUE) { @@ -487,6 +551,40 @@ iterate_read_state_machine (GDaemonFileInputStream *file, IOOperationData *io_op { /* Initial state for read op */ case READ_STATE_INIT: + + while (file->pre_reads) + { + pre = file->pre_reads->data; + if (file->seek_generation != pre->seek_generation) + { + file->pre_reads = g_list_delete_link (file->pre_reads, + file->pre_reads); + pre_read_free (pre); + } + else + { + len = MIN (op->buffer_size, pre->len); + memcpy (op->buffer, pre->data, len); + op->ret_val = len; + op->ret_error = NULL; + + if (len < pre->len) + { + memmove (pre->data, pre->data + len, pre->len - len); + pre->len -= len; + } + else + { + file->pre_reads = g_list_delete_link (file->pre_reads, + file->pre_reads); + pre_read_free (pre); + } + + return STATE_OP_DONE; + } + } + + /* If we're already reading some data, but we didn't read all, just use that and don't even send a request */ if (file->input_state == INPUT_STATE_IN_BLOCK && @@ -500,7 +598,7 @@ iterate_read_state_machine (GDaemonFileInputStream *file, IOOperationData *io_op } append_request (file, G_VFS_DAEMON_SOCKET_PROTOCOL_REQUEST_READ, - op->buffer_size, 0, &op->seq_nr); + op->buffer_size, 0, 0, &op->seq_nr); op->state = READ_STATE_WROTE_COMMAND; io_op->io_buffer = file->output_buffer->str; io_op->io_size = file->output_buffer->len; @@ -521,11 +619,8 @@ iterate_read_state_machine (GDaemonFileInputStream *file, IOOperationData *io_op if (io_op->io_res < file->output_buffer->len) { - memcpy (file->output_buffer->str, - file->output_buffer->str + io_op->io_res, - file->output_buffer->len - io_op->io_res); - g_string_truncate (file->output_buffer, - file->output_buffer->len - io_op->io_res); + 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; @@ -542,7 +637,7 @@ iterate_read_state_machine (GDaemonFileInputStream *file, IOOperationData *io_op { op->sent_cancel = TRUE; append_request (file, G_VFS_DAEMON_SOCKET_PROTOCOL_REQUEST_CANCEL, - op->seq_nr, 0, NULL); + op->seq_nr, 0, 0, NULL); op->state = READ_STATE_WROTE_COMMAND; io_op->io_buffer = file->output_buffer->str; io_op->io_size = file->output_buffer->len; @@ -763,8 +858,18 @@ iterate_close_state_machine (GDaemonFileInputStream *file, IOOperationData *io_o { /* Initial state for read op */ case CLOSE_STATE_INIT: + + /* Clear any pre-read data blocks */ + while (file->pre_reads) + { + PreRead *pre = file->pre_reads->data; + file->pre_reads = g_list_delete_link (file->pre_reads, + file->pre_reads); + pre_read_free (pre); + } + append_request (file, G_VFS_DAEMON_SOCKET_PROTOCOL_REQUEST_CLOSE, - 0, 0, &op->seq_nr); + 0, 0, 0, &op->seq_nr); op->state = CLOSE_STATE_WROTE_REQUEST; io_op->io_buffer = file->output_buffer->str; io_op->io_size = file->output_buffer->len; @@ -785,11 +890,8 @@ iterate_close_state_machine (GDaemonFileInputStream *file, IOOperationData *io_o if (io_op->io_res < file->output_buffer->len) { - memcpy (file->output_buffer->str, - file->output_buffer->str + io_op->io_res, - file->output_buffer->len - io_op->io_res); - g_string_truncate (file->output_buffer, - file->output_buffer->len - io_op->io_res); + 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; @@ -806,7 +908,7 @@ iterate_close_state_machine (GDaemonFileInputStream *file, IOOperationData *io_o { op->sent_cancel = TRUE; append_request (file, G_VFS_DAEMON_SOCKET_PROTOCOL_REQUEST_CANCEL, - op->seq_nr, 0, NULL); + op->seq_nr, 0, 0, NULL); op->state = CLOSE_STATE_WROTE_REQUEST; io_op->io_buffer = file->output_buffer->str; io_op->io_size = file->output_buffer->len; @@ -1013,6 +1115,7 @@ iterate_seek_state_machine (GDaemonFileInputStream *file, IOOperationData *io_op append_request (file, request, op->offset & 0xffffffff, op->offset >> 32, + 0, &op->seq_nr); op->state = SEEK_STATE_WROTE_REQUEST; op->sent_seek = FALSE; @@ -1039,13 +1142,19 @@ iterate_seek_state_machine (GDaemonFileInputStream *file, IOOperationData *io_op file->seek_generation++; op->sent_seek = TRUE; + /* Clear any pre-read data blocks */ + while (file->pre_reads) + { + PreRead *pre = file->pre_reads->data; + file->pre_reads = g_list_delete_link (file->pre_reads, + file->pre_reads); + pre_read_free (pre); + } + if (io_op->io_res < file->output_buffer->len) { - memcpy (file->output_buffer->str, - file->output_buffer->str + io_op->io_res, - file->output_buffer->len - io_op->io_res); - g_string_truncate (file->output_buffer, - file->output_buffer->len - io_op->io_res); + 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; @@ -1062,7 +1171,7 @@ iterate_seek_state_machine (GDaemonFileInputStream *file, IOOperationData *io_op { op->sent_cancel = TRUE; append_request (file, G_VFS_DAEMON_SOCKET_PROTOCOL_REQUEST_CANCEL, - op->seq_nr, 0, NULL); + op->seq_nr, 0, 0, NULL); op->state = SEEK_STATE_WROTE_REQUEST; io_op->io_buffer = file->output_buffer->str; io_op->io_size = file->output_buffer->len; @@ -1228,15 +1337,273 @@ g_daemon_file_input_stream_seek (GFileInputStream *stream, return op.ret_val; } +static StateOp +iterate_query_state_machine (GDaemonFileInputStream *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 (file->input_state == INPUT_STATE_IN_BLOCK) + { + op->state = QUERY_STATE_HANDLE_INPUT_BLOCK; + break; + } + else if (file->input_state == INPUT_STATE_IN_REPLY_HEADER) + { + op->state = QUERY_STATE_HANDLE_HEADER; + break; + } + g_assert_not_reached (); + break; + + /* No op */ + case QUERY_STATE_HANDLE_INPUT_BLOCK: + g_assert (file->input_state == INPUT_STATE_IN_BLOCK); + + if (file->input_block_size == 0) + { + file->input_state = INPUT_STATE_IN_REPLY_HEADER; + op->state = QUERY_STATE_HANDLE_INPUT; + break; + } + + if (file->seek_generation == + file->input_block_seek_generation) + { + op->state = QUERY_STATE_READ_BLOCK; + io_op->io_buffer = g_malloc (file->input_block_size); + io_op->io_size = file->input_block_size; + io_op->io_allow_cancel = FALSE; + return STATE_OP_READ; + } + else + { + op->state = QUERY_STATE_SKIP_BLOCK; + io_op->io_buffer = NULL; + io_op->io_size = file->input_block_size; + io_op->io_allow_cancel = !op->sent_cancel; + return STATE_OP_SKIP; + } + break; + + /* Read block data */ + case QUERY_STATE_SKIP_BLOCK: + if (io_op->io_cancelled) + { + op->state = QUERY_STATE_HANDLE_INPUT; + break; + } + + g_assert (io_op->io_res <= file->input_block_size); + file->input_block_size -= io_op->io_res; + + if (file->input_block_size == 0) + file->input_state = INPUT_STATE_IN_REPLY_HEADER; + + op->state = QUERY_STATE_HANDLE_INPUT; + break; + + /* Read block data */ + case QUERY_STATE_READ_BLOCK: + if (io_op->io_cancelled) + { + g_free (io_op->io_buffer); + op->state = QUERY_STATE_HANDLE_INPUT; + break; + } + + if (io_op->io_res > 0) + { + PreRead *pre; + + g_assert (io_op->io_res <= file->input_block_size); + file->input_block_size -= io_op->io_res; + if (file->input_block_size == 0) + file->input_state = INPUT_STATE_IN_REPLY_HEADER; + + pre = g_new (PreRead, 1); + pre->data = io_op->io_buffer; + pre->len = io_op->io_res; + pre->seek_generation = file->input_block_seek_generation; + + file->pre_reads = g_list_append (file->pre_reads, pre); + } + else + g_free (io_op->io_buffer); + + op->state = QUERY_STATE_HANDLE_INPUT; + break; + + /* read header data, (or manual io_len/res = 0) */ + case QUERY_STATE_HANDLE_HEADER: + if (io_op->io_cancelled) + { + op->state = QUERY_STATE_HANDLE_INPUT; + break; + } + + 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_DATA) + { + g_string_truncate (file->input_buffer, 0); + file->input_state = INPUT_STATE_IN_BLOCK; + file->input_block_size = reply.arg1; + file->input_block_seek_generation = reply.arg2; + op->state = QUERY_STATE_HANDLE_INPUT_BLOCK; + break; + } + 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 = QUERY_STATE_HANDLE_HEADER; + 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_input_stream_query_info (GFileInputStream *stream, char *attributes, GCancellable *cancellable, GError **error) { - g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("The query info operation is not supported")); + GDaemonFileInputStream *file; + QueryOperation op; + + file = G_DAEMON_FILE_INPUT_STREAM (stream); + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) + return NULL; + + 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 */ + + if (op.info == NULL) + g_propagate_error (error, op.ret_error); - return NULL; + return op.info; } /************************************************************************ diff --git a/common/Makefile.am b/common/Makefile.am index 56cc74e1..380dc654 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -17,6 +17,7 @@ libgvfscommon_la_SOURCES = \ gmounttracker.c gmounttracker.h \ gvfsdaemonprotocol.c gvfsdaemonprotocol.h \ gvfsicon.h gvfsicon.c \ + gvfsfileinfo.c gvfsfileinfo.h \ $(NULL) libgvfscommon_la_LIBADD = \ diff --git a/common/gvfsdaemonprotocol.h b/common/gvfsdaemonprotocol.h index 38f7d0d3..dedf86ba 100644 --- a/common/gvfsdaemonprotocol.h +++ b/common/gvfsdaemonprotocol.h @@ -113,6 +113,7 @@ typedef struct { #define G_VFS_DAEMON_SOCKET_PROTOCOL_REQUEST_CANCEL 3 #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 /* read, readahead reply: @@ -123,6 +124,10 @@ type, pos (64), error: type, code, size, data (size bytes, 2 strings: domain, message) + +info: +type, 0, size, data + */ typedef struct { @@ -139,6 +144,7 @@ typedef struct { #define G_VFS_DAEMON_SOCKET_PROTOCOL_REPLY_SEEK_POS 2 #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_FILE_INFO_INNER_TYPE_AS_STRING \ DBUS_TYPE_ARRAY_AS_STRING \ diff --git a/common/gvfsfileinfo.c b/common/gvfsfileinfo.c new file mode 100644 index 00000000..df563cc7 --- /dev/null +++ b/common/gvfsfileinfo.c @@ -0,0 +1,278 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 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> + */ + +#include <config.h> + +#include <gio/gio.h> +#include <string.h> +#include "gvfsfileinfo.h" + +static void +put_string (GDataOutputStream *out, + const char *str) +{ + gsize len; + + len = strlen (str); + if (len > G_MAXUINT16) + { + g_warning ("GFileInfo string to large, (%d bytes)\n", (int)len); + len = 0; + str = ""; + } + + g_data_output_stream_put_uint16 (out, len, + NULL, NULL); + g_data_output_stream_put_string (out, str, NULL, NULL); +} + +static char * +read_string (GDataInputStream *in) +{ + gsize len; + char *str; + + len = g_data_input_stream_read_uint16 (in, NULL, NULL); + str = g_malloc (len + 1); + g_input_stream_read_all (G_INPUT_STREAM (in), str, len, &len, NULL, NULL); + str[len] = 0; + return str; +} + +char * +gvfs_file_info_marshal (GFileInfo *info, + gsize *size) +{ + GOutputStream *memstream; + GDataOutputStream *out; + GFileAttributeType type; + GFileAttributeStatus status; + GObject *obj; + char **attrs, *attr; + char *data; + int i; + + memstream = g_memory_output_stream_new (NULL, 0, g_realloc, NULL); + + out = g_data_output_stream_new (memstream); + g_object_unref (memstream); + + attrs = g_file_info_list_attributes (info, NULL); + + g_data_output_stream_put_uint32 (out, + g_strv_length (attrs), + NULL, NULL); + + for (i = 0; attrs[i] != NULL; i++) + { + attr = attrs[i]; + + type = g_file_info_get_attribute_type (info, attr); + status = g_file_info_get_attribute_status (info, attr); + + put_string (out, attr); + g_data_output_stream_put_byte (out, type, + NULL, NULL); + g_data_output_stream_put_byte (out, status, + NULL, NULL); + + switch (type) + { + case G_FILE_ATTRIBUTE_TYPE_STRING: + put_string (out, g_file_info_get_attribute_string (info, attr)); + break; + case G_FILE_ATTRIBUTE_TYPE_BYTE_STRING: + put_string (out, g_file_info_get_attribute_byte_string (info, attr)); + break; + case G_FILE_ATTRIBUTE_TYPE_BOOLEAN: + g_data_output_stream_put_byte (out, + g_file_info_get_attribute_boolean (info, attr), + NULL, NULL); + break; + case G_FILE_ATTRIBUTE_TYPE_UINT32: + g_data_output_stream_put_uint32 (out, + g_file_info_get_attribute_uint32 (info, attr), + NULL, NULL); + break; + case G_FILE_ATTRIBUTE_TYPE_INT32: + g_data_output_stream_put_int32 (out, + g_file_info_get_attribute_int32 (info, attr), + NULL, NULL); + break; + case G_FILE_ATTRIBUTE_TYPE_UINT64: + g_data_output_stream_put_uint64 (out, + g_file_info_get_attribute_uint64 (info, attr), + NULL, NULL); + break; + case G_FILE_ATTRIBUTE_TYPE_INT64: + g_data_output_stream_put_int64 (out, + g_file_info_get_attribute_int64 (info, attr), + NULL, NULL); + break; + case G_FILE_ATTRIBUTE_TYPE_OBJECT: + obj = g_file_info_get_attribute_object (info, attr); + if (obj == NULL) + { + g_data_output_stream_put_byte (out, 0, + NULL, NULL); + } + else if (G_IS_ICON (obj)) + { + char *icon_str; + + icon_str = g_icon_to_string (G_ICON (obj)); + g_data_output_stream_put_byte (out, 1, + NULL, NULL); + put_string (out, icon_str); + g_free (icon_str); + } + else + { + g_warning ("Unsupported GFileInfo object type %s\n", + g_type_name_from_instance ((GTypeInstance *)obj)); + g_data_output_stream_put_byte (out, 0, + NULL, NULL); + } + break; + case G_FILE_ATTRIBUTE_TYPE_INVALID: + default: + break; + } + } + + data = g_memory_output_stream_get_data (G_MEMORY_OUTPUT_STREAM (memstream)); + *size = g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM (memstream)); + g_object_unref (out); + g_strfreev (attrs); + return data; +} + +GFileInfo * +gvfs_file_info_demarshal (char *data, + gsize size) +{ + guint32 num_attrs, i; + GInputStream *memstream; + GDataInputStream *in; + GFileInfo *info; + char *attr, *str; + GFileAttributeType type; + GFileAttributeStatus status; + GObject *obj; + int objtype; + + memstream = g_memory_input_stream_new_from_data (data, size, NULL); + in = g_data_input_stream_new (memstream); + g_object_unref (memstream); + + info = g_file_info_new (); + num_attrs = g_data_input_stream_read_uint32 (in, NULL, NULL); + + for (i = 0; i < num_attrs; i++) + { + attr = read_string (in); + type = g_data_input_stream_read_byte (in, NULL, NULL); + status = g_data_input_stream_read_byte (in, NULL, NULL); + + /* TODO: There is no way to set the status. This is required for + g_file_set_attributes_from_info() */ + + switch (type) + { + case G_FILE_ATTRIBUTE_TYPE_STRING: + str = read_string (in); + g_file_info_set_attribute_string (info, attr, str); + g_free (str); + break; + case G_FILE_ATTRIBUTE_TYPE_BYTE_STRING: + str = read_string (in); + g_file_info_set_attribute_byte_string (info, attr, str); + g_free (str); + break; + case G_FILE_ATTRIBUTE_TYPE_BOOLEAN: + g_file_info_set_attribute_boolean (info, attr, + g_data_input_stream_read_byte (in, + NULL, + NULL)); + break; + case G_FILE_ATTRIBUTE_TYPE_UINT32: + g_file_info_set_attribute_uint32 (info, attr, + g_data_input_stream_read_uint32 (in, + NULL, + NULL)); + break; + case G_FILE_ATTRIBUTE_TYPE_INT32: + g_file_info_set_attribute_int32 (info, attr, + g_data_input_stream_read_int32 (in, + NULL, + NULL)); + break; + case G_FILE_ATTRIBUTE_TYPE_UINT64: + g_file_info_set_attribute_uint64 (info, attr, + g_data_input_stream_read_uint64 (in, + NULL, + NULL)); + break; + case G_FILE_ATTRIBUTE_TYPE_INT64: + g_file_info_set_attribute_int64 (info, attr, + g_data_input_stream_read_int64 (in, + NULL, + NULL)); + break; + case G_FILE_ATTRIBUTE_TYPE_OBJECT: + objtype = g_data_input_stream_read_byte (in, NULL, NULL); + obj = NULL; + + if (objtype == 1) + { + char *icon_str; + + icon_str = read_string (in); + obj = (GObject *)g_icon_new_for_string (icon_str, NULL); + g_free (icon_str); + } + else + { + g_warning ("Unsupported GFileInfo object type %d\n", objtype); + g_free (attr); + goto out; + } + g_file_info_set_attribute_object (info, attr, obj); + if (obj) + g_object_unref (obj); + break; + case G_FILE_ATTRIBUTE_TYPE_INVALID: + default: + g_warning ("Unsupported GFileInfo attribute type %d\n", type); + g_free (attr); + goto out; + break; + } + g_free (attr); + } + + out: + g_object_unref (in); + return info; +} + + diff --git a/common/gvfsfileinfo.h b/common/gvfsfileinfo.h new file mode 100644 index 00000000..970f9e4f --- /dev/null +++ b/common/gvfsfileinfo.h @@ -0,0 +1,37 @@ +/* GIO - GLib Input, Output and Streaming Library + * + * Copyright (C) 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_FILE_INFO_H__ +#define __G_VFS_FILE_INFO_H__ + +#include <glib.h> + +G_BEGIN_DECLS + +char * gvfs_file_info_marshal (GFileInfo *info, + gsize *size); +GFileInfo *gvfs_file_info_demarshal (char *data, + gsize size); + +G_END_DECLS + +#endif /* __G_VFS_FILE_INFO_H__ */ diff --git a/daemon/Makefile.am b/daemon/Makefile.am index 2bd201f2..dc21955d 100644 --- a/daemon/Makefile.am +++ b/daemon/Makefile.am @@ -134,6 +134,7 @@ libdaemon_la_SOURCES = \ gvfsjobseekwrite.c gvfsjobseekwrite.h \ gvfsjobclosewrite.c gvfsjobclosewrite.h \ gvfsjobqueryinfo.c gvfsjobqueryinfo.h \ + gvfsjobqueryinforead.c gvfsjobqueryinforead.h \ gvfsjobqueryfsinfo.c gvfsjobqueryfsinfo.h \ gvfsjobenumerate.c gvfsjobenumerate.h \ gvfsjobsetdisplayname.c gvfsjobsetdisplayname.h \ diff --git a/daemon/gvfsbackend.h b/daemon/gvfsbackend.h index fd8224b3..e1393aaf 100644 --- a/daemon/gvfsbackend.h +++ b/daemon/gvfsbackend.h @@ -57,6 +57,7 @@ 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; @@ -237,6 +238,16 @@ struct _GVfsBackendClass GFileQueryInfoFlags flags, GFileInfo *info, GFileAttributeMatcher *attribute_matcher); + void (*query_info_on_read)(GVfsBackend *backend, + GVfsJobQueryInfoRead *job, + GVfsBackendHandle handle, + GFileInfo *info, + GFileAttributeMatcher *attribute_matcher); + gboolean (*try_query_info_on_read)(GVfsBackend *backend, + GVfsJobQueryInfoRead *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 b0fb28e4..f6616696 100644 --- a/daemon/gvfsbackendtest.c +++ b/daemon/gvfsbackendtest.c @@ -250,6 +250,42 @@ do_seek_on_read (GVfsBackend *backend, } static void +do_query_info_on_read (GVfsBackend *backend, + GVfsJobQueryInfoRead *job, + GVfsBackendHandle handle, + GFileInfo *info, + GFileAttributeMatcher *attribute_matcher) +{ + int fd, res; + struct stat statbuf; + + fd = GPOINTER_TO_INT (handle); + + res = fstat (fd, &statbuf); + + if (res == -1) + { + int errsv = errno; + + g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, + g_io_error_from_errno (errsv), + "Error querying info in file: %s", + g_strerror (errsv)); + } + else + { + g_file_info_set_size (info, statbuf.st_size); + g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_DEVICE, + statbuf.st_dev); + g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED, statbuf.st_mtime); + g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_ACCESS, statbuf.st_atime); + g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_CHANGED, statbuf.st_ctime); + + g_vfs_job_succeeded (G_VFS_JOB (job)); + } +} + +static void do_close_read (GVfsBackend *backend, GVfsJobCloseRead *job, GVfsBackendHandle handle) @@ -283,7 +319,7 @@ do_query_info (GVfsBackend *backend, file = g_vfs_get_file_for_path (local_vfs, filename); error = NULL; - info2 = g_file_query_info (file, NULL, flags, + info2 = g_file_query_info (file, job->attributes, flags, NULL, &error); if (info2) @@ -346,6 +382,7 @@ g_vfs_backend_test_class_init (GVfsBackendTestClass *klass) backend_class->try_open_for_read = try_open_for_read; backend_class->try_read = try_read; 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->query_info = do_query_info; backend_class->try_enumerate = try_enumerate; diff --git a/daemon/gvfschannel.c b/daemon/gvfschannel.c index 01c40e67..4647bcbb 100644 --- a/daemon/gvfschannel.c +++ b/daemon/gvfschannel.c @@ -40,6 +40,7 @@ #include <gvfsdaemonutils.h> #include <gvfsjobcloseread.h> #include <gvfsjobclosewrite.h> +#include <gvfsfileinfo.h> static void g_vfs_channel_job_source_iface_init (GVfsJobSourceIface *iface); @@ -669,6 +670,26 @@ g_vfs_channel_send_error (GVfsChannel *channel, g_vfs_channel_send_reply (channel, NULL, data, data_len); } +/* Might be called on an i/o thread + */ +void +g_vfs_channel_send_info (GVfsChannel *channel, + GFileInfo *info) +{ + GVfsDaemonSocketProtocolReply reply; + char *data; + gsize data_len; + + data = gvfs_file_info_marshal (info, &data_len); + + reply.type = g_htonl (G_VFS_DAEMON_SOCKET_PROTOCOL_REPLY_INFO); + reply.seq_nr = g_htonl (g_vfs_channel_get_current_seq_nr (channel)); + reply.arg1 = 0; + reply.arg2 = g_htonl (data_len); + + g_vfs_channel_send_reply (channel, &reply, data, data_len); +} + int g_vfs_channel_steal_remote_fd (GVfsChannel *channel) { diff --git a/daemon/gvfschannel.h b/daemon/gvfschannel.h index f60b20a2..0dc92fce 100644 --- a/daemon/gvfschannel.h +++ b/daemon/gvfschannel.h @@ -76,6 +76,8 @@ gboolean g_vfs_channel_has_job (GVfsChannel GVfsJob * g_vfs_channel_get_job (GVfsChannel *channel); void g_vfs_channel_send_error (GVfsChannel *channel, GError *error); +void g_vfs_channel_send_info (GVfsChannel *channel, + GFileInfo *info); void g_vfs_channel_send_reply (GVfsChannel *channel, GVfsDaemonSocketProtocolReply *reply, const void *data, diff --git a/daemon/gvfsjobqueryinforead.c b/daemon/gvfsjobqueryinforead.c new file mode 100644 index 00000000..7d8453a9 --- /dev/null +++ b/daemon/gvfsjobqueryinforead.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 "gvfsreadchannel.h" +#include "gvfsjobqueryinforead.h" +#include "gvfsdaemonutils.h" + +G_DEFINE_TYPE (GVfsJobQueryInfoRead, g_vfs_job_query_info_read, 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_read_finalize (GObject *object) +{ + GVfsJobQueryInfoRead *job; + + job = G_VFS_JOB_QUERY_INFO_READ (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_read_parent_class)->finalize) + (*G_OBJECT_CLASS (g_vfs_job_query_info_read_parent_class)->finalize) (object); +} + +static void +g_vfs_job_query_info_read_class_init (GVfsJobQueryInfoReadClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GVfsJobClass *job_class = G_VFS_JOB_CLASS (klass); + + gobject_class->finalize = g_vfs_job_query_info_read_finalize; + + job_class->run = run; + job_class->try = try; + job_class->send_reply = send_reply; +} + +static void +g_vfs_job_query_info_read_init (GVfsJobQueryInfoRead *job) +{ +} + +GVfsJob * +g_vfs_job_query_info_read_new (GVfsReadChannel *channel, + GVfsBackendHandle handle, + const char *attrs, + GVfsBackend *backend) +{ + GVfsJobQueryInfoRead *job; + + job = g_object_new (G_VFS_TYPE_JOB_QUERY_INFO_READ, + 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 thread */ +static void +send_reply (GVfsJob *job) +{ + GVfsJobQueryInfoRead *op_job = G_VFS_JOB_QUERY_INFO_READ (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) +{ + GVfsJobQueryInfoRead *op_job = G_VFS_JOB_QUERY_INFO_READ (job); + GVfsBackendClass *class = G_VFS_BACKEND_GET_CLASS (op_job->backend); + + if (class->query_info_on_read == NULL) + { + g_vfs_job_failed (job, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + _("Operation not supported by backend")); + return; + } + + class->query_info_on_read (op_job->backend, + op_job, + op_job->handle, + op_job->file_info, + op_job->attribute_matcher); +} + +static gboolean +try (GVfsJob *job) +{ + GVfsJobQueryInfoRead *op_job = G_VFS_JOB_QUERY_INFO_READ (job); + GVfsBackendClass *class = G_VFS_BACKEND_GET_CLASS (op_job->backend); + + if (class->try_query_info_on_read == NULL) + return FALSE; + + return class->try_query_info_on_read (op_job->backend, + op_job, + op_job->handle, + op_job->file_info, + op_job->attribute_matcher); +} diff --git a/daemon/gvfsjobqueryinforead.h b/daemon/gvfsjobqueryinforead.h new file mode 100644 index 00000000..1fdfe4d1 --- /dev/null +++ b/daemon/gvfsjobqueryinforead.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_READ_H__ +#define __G_VFS_JOB_QUERY_INFO_READ_H__ + +#include <gvfsjob.h> +#include <gvfsbackend.h> +#include <gvfsreadchannel.h> + +G_BEGIN_DECLS + +#define G_VFS_TYPE_JOB_QUERY_INFO_READ (g_vfs_job_query_info_read_get_type ()) +#define G_VFS_JOB_QUERY_INFO_READ(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_VFS_TYPE_JOB_QUERY_INFO_READ, GVfsJobQueryInfoRead)) +#define G_VFS_JOB_QUERY_INFO_READ_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_VFS_TYPE_JOB_QUERY_INFO_READ, GVfsJobQueryInfoReadClass)) +#define G_VFS_IS_JOB_QUERY_INFO_READ(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_VFS_TYPE_JOB_QUERY_INFO_READ)) +#define G_VFS_IS_JOB_QUERY_INFO_READ_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_VFS_TYPE_JOB_QUERY_INFO_READ)) +#define G_VFS_JOB_QUERY_INFO_READ_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_VFS_TYPE_JOB_QUERY_INFO_READ, GVfsJobQueryInfoReadClass)) + +typedef struct _GVfsJobQueryInfoReadClass GVfsJobQueryInfoReadClass; + +struct _GVfsJobQueryInfoRead +{ + GVfsJob parent_instance; + + GVfsReadChannel *channel; + GVfsBackend *backend; + GVfsBackendHandle handle; + char *attributes; + GFileAttributeMatcher *attribute_matcher; + + GFileInfo *file_info; +}; + +struct _GVfsJobQueryInfoReadClass +{ + GVfsJobClass parent_class; +}; + +GType g_vfs_job_query_info_read_get_type (void) G_GNUC_CONST; + +GVfsJob *g_vfs_job_query_info_read_new (GVfsReadChannel *channel, + GVfsBackendHandle handle, + const char *attrs, + GVfsBackend *backend); +G_END_DECLS + +#endif /* __G_VFS_JOB_QUERY_INFO_READ_H__ */ diff --git a/daemon/gvfsreadchannel.c b/daemon/gvfsreadchannel.c index c6e079b0..1f043b85 100644 --- a/daemon/gvfsreadchannel.c +++ b/daemon/gvfsreadchannel.c @@ -37,7 +37,9 @@ #include <gvfsdaemonutils.h> #include <gvfsjobread.h> #include <gvfsjobseekread.h> +#include <gvfsjobqueryinforead.h> #include <gvfsjobcloseread.h> +#include <gvfsfileinfo.h> struct _GVfsReadChannel { @@ -134,6 +136,7 @@ read_channel_handle_request (GVfsChannel *channel, GVfsBackendHandle backend_handle; GVfsBackend *backend; GVfsReadChannel *read_channel; + char *attrs; read_channel = G_VFS_READ_CHANNEL (channel); backend_handle = g_vfs_channel_get_backend_handle (channel); @@ -168,6 +171,16 @@ read_channel_handle_request (GVfsChannel *channel, ((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_read_new (read_channel, + backend_handle, + attrs, + backend); + + g_free (attrs); + break; default: g_set_error (error, G_IO_ERROR, diff --git a/test/Makefile.am b/test/Makefile.am index 7ddcd8ca..70c0cb7a 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -10,6 +10,7 @@ AM_LDFLAGS = \ $(GLIB_LIBS) noinst_PROGRAMS = \ + test-query-info-stream \ benchmark-gvfs-small-files \ benchmark-gvfs-big-files \ benchmark-posix-small-files \ diff --git a/test/test-query-info-stream.c b/test/test-query-info-stream.c new file mode 100644 index 00000000..6bd7ffcb --- /dev/null +++ b/test/test-query-info-stream.c @@ -0,0 +1,226 @@ +/* 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 <stdio.h> +#include <unistd.h> +#include <locale.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> + +#include <glib.h> +#include <gio/gio.h> + +/* Fill test data with 0..200, repeadedly. + * This is not a power of two to avoid possible + * effects with base-2 i/o buffer sizes that could + * hide bugs */ +#define DATA_MODULO 200 + +static gboolean +verify_block (guchar *data, guchar *start, gsize size) +{ + guchar d; + gsize i; + + d = 0; + if (start) + d = *start; + for (i = 0; i < size; i++) + { + if (data[i] != d) + return FALSE; + + d++; + if (d >= DATA_MODULO) + d = 0; + } + + if (start) + *start = d; + + return TRUE; +} + +static guchar * +allocate_block (gsize size) +{ + guchar *data; + gsize i; + guchar d; + + data = g_malloc (size); + d = 0; + for (i = 0; i < size; i++) + { + data[i] = d; + d++; + if (d >= DATA_MODULO) + d = 0; + } + return data; +} + +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 (GFileInputStream *in) +{ + GFileInfo *info; + GError *error; + goffset file_size; + + error = NULL; + info = g_file_input_stream_query_info (in, "*", NULL, &error); + if (info == NULL) + { + g_print ("error querying info: %s\n", error->message); + exit (1); + } + + if (!g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_STANDARD_SIZE)) + { + g_print ("couldn't read size attribute\n"); + exit (1); + } + + file_size = g_file_info_get_size (info); + g_print ("file size: %d\n", (int)file_size); + if (file_size != 100*1000) + { + g_print ("wrong file size\n"); + exit (1); + } +} + +int +main (int argc, char *argv[]) +{ + GFile *file; + GFileInputStream *in; + GError *error; + gssize res; + gsize read_size; + guchar *buffer; + guchar start; + gboolean do_create_file; + + g_type_init (); + + do_create_file = FALSE; + + if (argc > 1) + { + if (strcmp(argv[1], "-c") == 0) + { + do_create_file = TRUE; + argc--; + argv++; + } + } + + if (argc != 2) + { + g_print ("need file arg"); + return 1; + } + + file = g_file_new_for_commandline_arg (argv[1]); + + if (do_create_file) + create_file (file, 100*1000); + + error = NULL; + + in = g_file_read (file, NULL, &error); + if (in == NULL) + { + g_print ("error reading file: %s\n", error->message); + exit (1); + } + + check_query_info (in); + + buffer = malloc (100*1000); + + start = 0; + read_size = 0; + do + { + res = g_input_stream_read (G_INPUT_STREAM (in), + buffer, + 150, + NULL, &error); + if (res == 0) + break; + + if (res < 0) + { + g_print ("error reading: %s\n", error->message); + exit (1); + } + + g_print ("res: %d\n", (int)res); + + if (!verify_block (buffer, &start, res)) + { + g_print ("error in block starting at %d\n", (int)read_size); + exit (1); + } + + read_size += res; + + check_query_info (in); + } + while (1); + + if (read_size != 100*1000) + { + g_print ("Didn't read entire file\n"); + exit (1); + } + + + return 0; +} |