gvfs r2257 - in trunk: . client common daemon test



Author: alexl
Date: Fri Feb 27 12:59:47 2009
New Revision: 2257
URL: http://svn.gnome.org/viewvc/gvfs?rev=2257&view=rev

Log:
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.



Added:
   trunk/common/gvfsfileinfo.c
   trunk/common/gvfsfileinfo.h
   trunk/daemon/gvfsjobqueryinforead.c
   trunk/daemon/gvfsjobqueryinforead.h
   trunk/test/test-query-info-stream.c
Modified:
   trunk/ChangeLog
   trunk/client/gdaemonfileinputstream.c
   trunk/common/Makefile.am
   trunk/common/gvfsdaemonprotocol.h
   trunk/daemon/Makefile.am
   trunk/daemon/gvfsbackend.h
   trunk/daemon/gvfsbackendtest.c
   trunk/daemon/gvfschannel.c
   trunk/daemon/gvfschannel.h
   trunk/daemon/gvfsreadchannel.c
   trunk/test/Makefile.am

Modified: trunk/client/gdaemonfileinputstream.c
==============================================================================
--- trunk/client/gdaemonfileinputstream.c	(original)
+++ trunk/client/gdaemonfileinputstream.c	Fri Feb 27 12:59:47 2009
@@ -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 @@
   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 @@
   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 @@
   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_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 @@
   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 @@
 
 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 @@
   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 @@
   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 @@
 iterate_read_state_machine (GDaemonFileInputStream *file, IOOperationData *io_op, ReadOperation *op)
 {
   gsize len;
+  PreRead *pre;
 
   while (TRUE)
     {
@@ -487,6 +551,40 @@
 	{
 	  /* 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 @@
 	    }
 
 	  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 @@
 	  
 	  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 @@
 	    {
 	      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 @@
 	{
 	  /* 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 @@
 
 	  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 @@
 	    {
 	      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 @@
 	  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 @@
 	    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 @@
 	    {
 	      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 @@
   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;
 }
 
 /************************************************************************

Modified: trunk/common/Makefile.am
==============================================================================
--- trunk/common/Makefile.am	(original)
+++ trunk/common/Makefile.am	Fri Feb 27 12:59:47 2009
@@ -17,6 +17,7 @@
 	gmounttracker.c gmounttracker.h \
 	gvfsdaemonprotocol.c gvfsdaemonprotocol.h \
 	gvfsicon.h gvfsicon.c \
+	gvfsfileinfo.c gvfsfileinfo.h \
 	$(NULL)
 
 libgvfscommon_la_LIBADD =	\

Modified: trunk/common/gvfsdaemonprotocol.h
==============================================================================
--- trunk/common/gvfsdaemonprotocol.h	(original)
+++ trunk/common/gvfsdaemonprotocol.h	Fri Feb 27 12:59:47 2009
@@ -113,6 +113,7 @@
 #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 @@
 
 error:
 type, code, size, data (size bytes, 2 strings: domain, message)
+
+info:
+type,    0, size, data 
+
 */
 
 typedef struct {
@@ -139,6 +144,7 @@
 #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			 \

Added: trunk/common/gvfsfileinfo.c
==============================================================================
--- (empty file)
+++ trunk/common/gvfsfileinfo.c	Fri Feb 27 12:59:47 2009
@@ -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;
+}
+
+

Added: trunk/common/gvfsfileinfo.h
==============================================================================
--- (empty file)
+++ trunk/common/gvfsfileinfo.h	Fri Feb 27 12:59:47 2009
@@ -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__ */

Modified: trunk/daemon/Makefile.am
==============================================================================
--- trunk/daemon/Makefile.am	(original)
+++ trunk/daemon/Makefile.am	Fri Feb 27 12:59:47 2009
@@ -134,6 +134,7 @@
 	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 \

Modified: trunk/daemon/gvfsbackend.h
==============================================================================
--- trunk/daemon/gvfsbackend.h	(original)
+++ trunk/daemon/gvfsbackend.h	Fri Feb 27 12:59:47 2009
@@ -57,6 +57,7 @@
 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 @@
 				 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,

Modified: trunk/daemon/gvfsbackendtest.c
==============================================================================
--- trunk/daemon/gvfsbackendtest.c	(original)
+++ trunk/daemon/gvfsbackendtest.c	Fri Feb 27 12:59:47 2009
@@ -250,6 +250,42 @@
 }
 
 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 @@
   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 @@
   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;

Modified: trunk/daemon/gvfschannel.c
==============================================================================
--- trunk/daemon/gvfschannel.c	(original)
+++ trunk/daemon/gvfschannel.c	Fri Feb 27 12:59:47 2009
@@ -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_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)
 {

Modified: trunk/daemon/gvfschannel.h
==============================================================================
--- trunk/daemon/gvfschannel.h	(original)
+++ trunk/daemon/gvfschannel.h	Fri Feb 27 12:59:47 2009
@@ -76,6 +76,8 @@
 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,

Added: trunk/daemon/gvfsjobqueryinforead.c
==============================================================================
--- (empty file)
+++ trunk/daemon/gvfsjobqueryinforead.c	Fri Feb 27 12:59:47 2009
@@ -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);
+}

Added: trunk/daemon/gvfsjobqueryinforead.h
==============================================================================
--- (empty file)
+++ trunk/daemon/gvfsjobqueryinforead.h	Fri Feb 27 12:59:47 2009
@@ -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__ */

Modified: trunk/daemon/gvfsreadchannel.c
==============================================================================
--- trunk/daemon/gvfsreadchannel.c	(original)
+++ trunk/daemon/gvfsreadchannel.c	Fri Feb 27 12:59:47 2009
@@ -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 @@
   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 @@
 				     ((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,

Modified: trunk/test/Makefile.am
==============================================================================
--- trunk/test/Makefile.am	(original)
+++ trunk/test/Makefile.am	Fri Feb 27 12:59:47 2009
@@ -10,6 +10,7 @@
 	$(GLIB_LIBS)
 
 noinst_PROGRAMS = \
+	test-query-info-stream    \
 	benchmark-gvfs-small-files    \
 	benchmark-gvfs-big-files      \
 	benchmark-posix-small-files   \

Added: trunk/test/test-query-info-stream.c
==============================================================================
--- (empty file)
+++ trunk/test/test-query-info-stream.c	Fri Feb 27 12:59:47 2009
@@ -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;
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]