summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Larsson <alexl@redhat.com>2009-02-27 12:59:47 +0000
committerAlexander Larsson <alexl@src.gnome.org>2009-02-27 12:59:47 +0000
commit28713ed6b20cb617bce93e639b764617a118e1dd (patch)
tree8e840db7df291d1c60a55eb0aba9364ae89bde73
parent4c1cbdac8d59b1bf53319bc9e5a165c8b11adacb (diff)
downloadgvfs-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--ChangeLog31
-rw-r--r--client/gdaemonfileinputstream.c419
-rw-r--r--common/Makefile.am1
-rw-r--r--common/gvfsdaemonprotocol.h6
-rw-r--r--common/gvfsfileinfo.c278
-rw-r--r--common/gvfsfileinfo.h37
-rw-r--r--daemon/Makefile.am1
-rw-r--r--daemon/gvfsbackend.h11
-rw-r--r--daemon/gvfsbackendtest.c39
-rw-r--r--daemon/gvfschannel.c21
-rw-r--r--daemon/gvfschannel.h2
-rw-r--r--daemon/gvfsjobqueryinforead.c144
-rw-r--r--daemon/gvfsjobqueryinforead.h67
-rw-r--r--daemon/gvfsreadchannel.c13
-rw-r--r--test/Makefile.am1
-rw-r--r--test/test-query-info-stream.c226
16 files changed, 1270 insertions, 27 deletions
diff --git a/ChangeLog b/ChangeLog
index e73e97ea..4ef258f6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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;
+}