[gvfs] afp: split out volume specific functions into a GVfsAfpVolume object.



commit fa288893d51478daa7190bbb554779404a205157
Author: Carl-Anton Ingmarsson <ca ingmarsson gmail com>
Date:   Wed Nov 23 23:53:18 2011 +0100

    afp: split out volume specific functions into a GVfsAfpVolume object.

 daemon/Makefile.am            |    8 +-
 daemon/gvfsafpconnection.c    |   40 +
 daemon/gvfsafpconnection.h    |    1 +
 daemon/gvfsafpserver.c        |  499 +++++---
 daemon/gvfsafpserver.h        |   25 +-
 daemon/gvfsafputils.c         |   12 +
 daemon/gvfsafputils.h         |   10 +-
 daemon/gvfsafpvolume.c        | 3033 +++++++++++++++++++++++++++++++++++++++++
 daemon/gvfsafpvolume.h        |  281 ++++
 daemon/gvfsbackendafp.c       | 2649 ++++--------------------------------
 daemon/gvfsbackendafpbrowse.c |   12 -
 11 files changed, 4004 insertions(+), 2566 deletions(-)
---
diff --git a/daemon/Makefile.am b/daemon/Makefile.am
index 954b67e..55cf029 100644
--- a/daemon/Makefile.am
+++ b/daemon/Makefile.am
@@ -485,7 +485,9 @@ gvfsd_afp_browse_SOURCES = \
 	gvfsafpconnection.h \
 	gvfsafpconnection.c \
 	gvfsafpserver.h \
-	gvfsafpserver.c
+	gvfsafpserver.c \
+	gvfsafpvolume.h \
+	gvfsafpvolume.c
 
 gvfsd_afp_browse_CPPFLAGS = \
 	-DBACKEND_HEADER=gvfsbackendafpbrowse.h \
@@ -505,7 +507,9 @@ gvfsd_afp_SOURCES = \
 	gvfsafpconnection.h \
 	gvfsafpconnection.c \
 	gvfsafpserver.h \
-	gvfsafpserver.c
+	gvfsafpserver.c \
+	gvfsafpvolume.h \
+	gvfsafpvolume.c
 
 gvfsd_afp_CPPFLAGS = \
 	-DBACKEND_HEADER=gvfsbackendafp.h \
diff --git a/daemon/gvfsafpconnection.c b/daemon/gvfsafpconnection.c
index 5c2312d..be489c5 100644
--- a/daemon/gvfsafpconnection.c
+++ b/daemon/gvfsafpconnection.c
@@ -511,6 +511,46 @@ g_vfs_afp_command_put_afp_name (GVfsAfpCommand *comm, GVfsAfpName *afp_name)
   }
 }
 
+static GVfsAfpName *
+filename_to_afp_pathname (const char *filename)
+{
+  gsize len;
+  char *str;
+  gint i;
+
+  while (*filename == '/')
+    filename++;
+  
+  len = strlen (filename);
+  
+  str = g_malloc (len); 
+
+  for (i = 0; i < len; i++)
+  {
+    if (filename[i] == '/')
+      str[i] = '\0';
+    else
+      str[i] = filename[i];
+  }
+  
+
+  return g_vfs_afp_name_new (0x08000103, str, len);
+}
+
+void
+g_vfs_afp_command_put_pathname (GVfsAfpCommand *comm, const char *filename)
+{
+  GVfsAfpName *pathname;
+  
+  /* PathType */
+  g_vfs_afp_command_put_byte (comm, AFP_PATH_TYPE_UTF8_NAME);
+
+  /* Pathname */
+  pathname = filename_to_afp_pathname (filename);
+  g_vfs_afp_command_put_afp_name (comm, pathname);
+  g_vfs_afp_name_unref (pathname);
+}
+
 void
 g_vfs_afp_command_pad_to_even (GVfsAfpCommand *comm)
 { 
diff --git a/daemon/gvfsafpconnection.h b/daemon/gvfsafpconnection.h
index d02b1f8..f70ff2e 100644
--- a/daemon/gvfsafpconnection.h
+++ b/daemon/gvfsafpconnection.h
@@ -303,6 +303,7 @@ void            g_vfs_afp_command_put_uint64    (GVfsAfpCommand *comm, guint64 v
 
 void            g_vfs_afp_command_put_pascal   (GVfsAfpCommand *comm, const char *str);
 void            g_vfs_afp_command_put_afp_name (GVfsAfpCommand *comm, GVfsAfpName *afp_name);
+void            g_vfs_afp_command_put_pathname (GVfsAfpCommand *comm, const char *filename);
 
 void            g_vfs_afp_command_pad_to_even  (GVfsAfpCommand *comm);
 
diff --git a/daemon/gvfsafpserver.c b/daemon/gvfsafpserver.c
index 6ecd18e..8dbd307 100644
--- a/daemon/gvfsafpserver.c
+++ b/daemon/gvfsafpserver.c
@@ -841,6 +841,82 @@ get_server_parms (GVfsAfpServer *server,
   return TRUE;
 }
 
+static gboolean
+get_userinfo (GVfsAfpServer *server,
+              GCancellable  *cancellable,
+              GError       **error)
+{
+  GVfsAfpCommand *comm;
+  guint16 bitmap;
+  gboolean res;
+
+  GVfsAfpReply *reply;
+  AfpResultCode res_code;
+
+  comm = g_vfs_afp_command_new (AFP_COMMAND_GET_USER_INFO);
+  /* Flags, ThisUser = 1 */
+  g_vfs_afp_command_put_byte (comm, 0x01);
+  /* UserId */
+  g_vfs_afp_command_put_int32 (comm, 0);
+  /* Bitmap */
+  bitmap = AFP_GET_USER_INFO_BITMAP_GET_UID_BIT | AFP_GET_USER_INFO_BITMAP_GET_GID_BIT;
+  g_vfs_afp_command_put_uint16 (comm, bitmap);
+
+  res = g_vfs_afp_connection_send_command_sync (server->conn,
+                                                comm, cancellable, error);
+  g_object_unref (comm);
+  if (!res)
+    return FALSE;
+
+  reply = g_vfs_afp_connection_read_reply_sync (server->conn,
+                                                cancellable, error);
+  if (!reply)
+    return FALSE;
+
+  res_code = g_vfs_afp_reply_get_result_code (reply);
+  if (res_code != AFP_RESULT_NO_ERROR)
+  {
+    g_object_unref (reply);
+
+    switch (res_code)
+    {
+      case AFP_RESULT_ACCESS_DENIED:
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+                     _("Permission denied"));
+        break;
+        break;
+      case AFP_RESULT_CALL_NOT_SUPPORTED:
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                     _("Command is not supported by server"));
+        break;
+      case AFP_RESULT_PWD_EXPIRED_ERR:
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+                     _("User's password has expired"));
+        break;
+      case AFP_RESULT_PWD_NEEDS_CHANGE_ERR:
+        g_set_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+                     _("User's password needs to be changed"));
+        break;
+
+      default:
+        g_propagate_error (error, afp_result_code_to_gerror (res_code));
+        break;
+    }
+    return FALSE;
+  }
+
+  /* Bitmap */
+  g_vfs_afp_reply_read_uint16 (reply, NULL);
+  /* UID */
+  g_vfs_afp_reply_read_uint32 (reply, &server->user_id);
+  /* GID */
+  g_vfs_afp_reply_read_uint32 (reply, &server->group_id);
+
+  g_object_unref (reply);
+  
+  return TRUE;
+}
+
 gboolean
 g_vfs_afp_server_login (GVfsAfpServer *server,
                         const char     *initial_user,
@@ -982,14 +1058,16 @@ try_login:
   g_free (olduser);
 
   if (!res)
-  {
-    g_free (user);
-    g_free (password);
+    goto error;
 
-    g_propagate_error (error, err);
-    return FALSE;
-  }
+    /* Get server parms */
+  if (!get_server_parms (server, cancellable, &err))
+    goto error;
 
+  /* Get user info */
+  if (!get_userinfo (server, cancellable, &err))
+    goto error;
+  
   if (prompt && !anonymous)
   {
     /* a prompt was created, so we have to save the password */
@@ -1004,10 +1082,6 @@ try_login:
                                  password_save);
     g_free (prompt);
   }
-
-  /* Get server parms */
-  if (!get_server_parms (server, cancellable, error))
-    return FALSE;
   
   if (logged_in_user)
   {
@@ -1022,6 +1096,12 @@ try_login:
   g_free (password);
 
   return TRUE;
+
+error:
+  g_free (user);
+  g_free (password);
+  g_propagate_error (error, err);
+  return FALSE;
 }
 
 /*
@@ -1039,174 +1119,6 @@ g_vfs_afp_server_time_to_local_time (GVfsAfpServer *server,
   return server_time + server->time_diff;
 }
 
-static void
-get_vol_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
-{
-  GVfsAfpConnection *connection = G_VFS_AFP_CONNECTION (source_object);
-  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
-  GVfsAfpServer *server = G_VFS_AFP_SERVER (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
-
-  GVfsAfpReply *reply;
-  GError *err = NULL;
-  AfpResultCode res_code;
-  
-  guint16 vol_bitmap;
-  GFileInfo *info;
-
-  reply = g_vfs_afp_connection_send_command_finish (connection, res, &err);
-  if (!reply)
-  {
-    g_simple_async_result_take_error (simple, err);
-    goto done;
-  }
-
-  res_code = g_vfs_afp_reply_get_result_code (reply);
-  if (res_code != AFP_RESULT_NO_ERROR)
-  {
-    g_object_unref (reply);
-
-    g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
-    goto done;
-  }
-
-  g_vfs_afp_reply_read_uint16 (reply, &vol_bitmap);
-
-  info = g_file_info_new ();
-
-  if (vol_bitmap & AFP_VOLUME_BITMAP_ATTRIBUTE_BIT)
-  {
-    guint16 vol_attrs_bitmap;
-    
-    g_vfs_afp_reply_read_uint16 (reply, &vol_attrs_bitmap);
-    
-    g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY,
-                                       vol_attrs_bitmap & AFP_VOLUME_ATTRIBUTES_BITMAP_READ_ONLY);
-  }
-
-  if (vol_bitmap & AFP_VOLUME_BITMAP_CREATE_DATE_BIT)
-  {
-    gint32 create_date;
-    gint64 create_date_local;
-
-    g_vfs_afp_reply_read_int32 (reply, &create_date);
-
-    create_date_local = g_vfs_afp_server_time_to_local_time (server, create_date);
-    g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_CREATED,
-                                      create_date_local);
-  }
-
-  if (vol_bitmap & AFP_VOLUME_BITMAP_MOD_DATE_BIT)
-  {
-    gint32 mod_date;
-    gint64 mod_date_local;
-
-    g_vfs_afp_reply_read_int32 (reply, &mod_date);
-
-    mod_date_local = g_vfs_afp_server_time_to_local_time (server, mod_date);
-    g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED,
-                                      mod_date_local);
-  }
-
-  if (vol_bitmap & AFP_VOLUME_BITMAP_EXT_BYTES_FREE_BIT)
-  {
-    guint64 bytes_free;
-
-    g_vfs_afp_reply_read_uint64 (reply, &bytes_free);
-    g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE,
-                                      bytes_free);
-  }
-
-  if (vol_bitmap & AFP_VOLUME_BITMAP_EXT_BYTES_TOTAL_BIT)
-  {
-    guint64 bytes_total;
-
-    g_vfs_afp_reply_read_uint64 (reply, &bytes_total);
-    g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE,
-                                      bytes_total);
-  }
-
-  g_object_unref (reply);
-  
-  g_simple_async_result_set_op_res_gpointer (simple, info, g_object_unref);
-
-done:
-  g_simple_async_result_complete (simple);
-  g_object_unref (simple);
-}
-
-/*
- * g_vfs_afp_server_get_vol_parms:
- * 
- * @server: a #GVfsAfpServer
- * @volume_id: id of the volume whose parameters should be received.
- * @vol_bitmap: bitmap describing the parameters that should be received.
- * @cancellable: optional #GCancellable object, %NULL to ignore.
- * @callback: callback to call when the request is satisfied.
- * @user_data: the data to pass to callback function.
- * 
- * Asynchronously retrives the parameters specified by @vol_bitmap of the volume
- * with id @volume_id.
- */
-void
-g_vfs_afp_server_get_vol_parms (GVfsAfpServer       *server,
-                                guint16              volume_id,
-                                guint16              vol_bitmap,
-                                GCancellable        *cancellable,
-                                GAsyncReadyCallback  callback,
-                                gpointer             user_data)
-{
-  GVfsAfpCommand *comm;
-  GSimpleAsyncResult *simple;
-
-  comm = g_vfs_afp_command_new (AFP_COMMAND_GET_VOL_PARMS);
-  /* pad byte */
-  g_vfs_afp_command_put_byte (comm, 0);
-  /* Volume ID */
-  g_vfs_afp_command_put_uint16 (comm, volume_id);
-  /* Volume Bitmap */
-  g_vfs_afp_command_put_uint16 (comm, vol_bitmap);
-
-  simple = g_simple_async_result_new (G_OBJECT (server), callback, user_data,
-                                      g_vfs_afp_server_get_vol_parms);
-                                      
-  
-  g_vfs_afp_connection_send_command (server->conn, comm, NULL, get_vol_parms_cb,
-                                     cancellable, simple);
-  g_object_unref (comm);
-}
-
-/*
- * g_vfs_afp_server_get_vol_parms_finish:
- * 
- * @server: a #GVfsAfpServer.
- * @result: a #GAsyncResult.
- * @error: a #GError, %NULL to ignore.
- * 
- * Finalizes the asynchronous operation started by
- * g_vfs_afp_server_get_vol_parms.
- * 
- * Returns: (transfer full): A #GFileInfo with the requested parameters or %NULL
- * on error.
- */
-GFileInfo *
-g_vfs_afp_server_get_vol_parms_finish (GVfsAfpServer  *server,
-                                       GAsyncResult   *result,
-                                       GError         **error)
-{
-  GSimpleAsyncResult *simple;
-  
-  g_return_val_if_fail (g_simple_async_result_is_valid (result,
-                                                        G_OBJECT (server),
-                                                        g_vfs_afp_server_get_vol_parms),
-                        NULL);
-
-  simple = (GSimpleAsyncResult *)result;
-
-  if (g_simple_async_result_propagate_error (simple, error))
-    return NULL;
-
-  return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
-}
 
 static void
 volume_data_free (GVfsAfpVolumeData *vol_data)
@@ -1312,7 +1224,7 @@ g_vfs_afp_server_get_volumes (GVfsAfpServer       *server,
 }
 
 /*
- * g_vfs_afp_server_get_vol_parms_finish:
+ * g_vfs_afp_server_get_volumes_finish:
  * 
  * @server: a #GVfsAfpServer.
  * @result: a #GAsyncResult.
@@ -1343,4 +1255,233 @@ g_vfs_afp_server_get_volumes_finish (GVfsAfpServer  *server,
     return NULL;
 
   return g_ptr_array_ref ((GPtrArray *)g_simple_async_result_get_op_res_gpointer (simple));
+}
+
+GVfsAfpVolume *
+g_vfs_afp_server_mount_volume_sync (GVfsAfpServer *server,
+                                    const char    *volume_name,
+                                    GCancellable  *cancellable,
+                                    GError **error)
+{
+  GVfsAfpVolume *volume;
+
+  volume = g_vfs_afp_volume_new (server);
+  if (!g_vfs_afp_volume_mount_sync (volume, volume_name, cancellable, error))
+  {
+    g_object_unref (volume);
+    return NULL;
+  }
+
+  return volume;
+}
+
+static void
+set_access_attributes_trusted (GFileInfo *info,
+                               guint32 perm)
+{
+  g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ,
+				     perm & 0x4);
+  g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
+				     perm & 0x2);
+  g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE,
+				     perm & 0x1);
+}
+
+/* For files we don't own we can't trust a negative response to this check, as
+   something else could allow us to do the operation, for instance an ACL
+   or some sticky bit thing */
+static void
+set_access_attributes (GFileInfo *info,
+                       guint32 perm)
+{
+  if (perm & 0x4)
+    g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ,
+				       TRUE);
+  if (perm & 0x2)
+    g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
+				       TRUE);
+  if (perm & 0x1)
+    g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE,
+				       TRUE);
+}
+
+void
+g_vfs_afp_server_fill_info (GVfsAfpServer *server,
+                            GFileInfo     *info,
+                            GVfsAfpReply  *reply,
+                            gboolean       directory,
+                            guint16        bitmap)
+{
+  goffset start_pos;
+
+  if (directory)
+  {
+    GIcon *icon;
+    
+    g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
+    g_file_info_set_content_type (info, "inode/directory");
+
+    icon = g_themed_icon_new ("folder");
+    g_file_info_set_icon (info, icon);
+    g_object_unref (icon);
+  }
+  else
+    g_file_info_set_file_type (info, G_FILE_TYPE_REGULAR);
+
+  
+  start_pos = g_vfs_afp_reply_get_pos (reply);
+
+  if (bitmap & AFP_FILEDIR_BITMAP_ATTRIBUTE_BIT)
+  {
+    guint16 attributes;
+
+    g_vfs_afp_reply_read_uint16 (reply, &attributes);
+    
+    if (attributes & AFP_FILEDIR_ATTRIBUTES_BITMAP_INVISIBLE_BIT)
+      g_file_info_set_is_hidden (info, TRUE);
+  }
+
+  if (bitmap & AFP_FILEDIR_BITMAP_PARENT_DIR_ID_BIT)
+  {
+    guint32 parent_dir_id;
+
+    g_vfs_afp_reply_read_uint32 (reply, &parent_dir_id);
+    g_file_info_set_attribute_uint32 (info, "afp::parent-dir-id", parent_dir_id);
+  }
+  
+  if (bitmap & AFP_FILEDIR_BITMAP_CREATE_DATE_BIT)
+  {
+    gint32 create_date;
+    gint64 create_date_local;
+
+    g_vfs_afp_reply_read_int32 (reply, &create_date);
+    
+    create_date_local = g_vfs_afp_server_time_to_local_time (server, create_date);
+    g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_CREATED,
+                                      create_date_local);
+  }
+
+  if (bitmap & AFP_FILEDIR_BITMAP_MOD_DATE_BIT)
+  {
+    gint32 mod_date;
+    guint64 mod_date_unix;
+    char *etag;
+
+    g_vfs_afp_reply_read_int32 (reply, &mod_date);
+    mod_date_unix = g_vfs_afp_server_time_to_local_time (server, mod_date);
+    
+    g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED,
+                                      mod_date_unix);
+
+    etag = g_strdup_printf ("%"G_GUINT64_FORMAT, mod_date_unix);
+    g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_ETAG_VALUE, etag);
+    g_free (etag);
+  }
+
+  if (bitmap & AFP_FILEDIR_BITMAP_NODE_ID_BIT)
+  {
+    guint32 node_id;
+
+    g_vfs_afp_reply_read_uint32 (reply, &node_id);
+    g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_AFP_NODE_ID, node_id);
+  }
+  
+  /* Directory specific attributes */
+  if (directory)
+  {
+    if (bitmap & AFP_DIR_BITMAP_OFFSPRING_COUNT_BIT)
+    {
+      guint16 offspring_count;
+
+      g_vfs_afp_reply_read_uint16 (reply, &offspring_count);
+      g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_AFP_CHILDREN_COUNT,
+                                        offspring_count);
+    }
+  }
+  
+  /* File specific attributes */
+  else
+  {
+    if (bitmap & AFP_FILE_BITMAP_EXT_DATA_FORK_LEN_BIT)
+    {
+      guint64 fork_len;
+
+      g_vfs_afp_reply_read_uint64 (reply, &fork_len);
+      g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE,
+                                        fork_len);
+    }
+  }
+  
+  if (bitmap & AFP_FILEDIR_BITMAP_UTF8_NAME_BIT)
+  {
+    guint16 UTF8Name_offset;
+    goffset old_pos;
+    GVfsAfpName *afp_name;
+    char *utf8_name;
+
+    g_vfs_afp_reply_read_uint16 (reply, &UTF8Name_offset);
+    /* Pad */
+    g_vfs_afp_reply_read_uint32 (reply, NULL);
+
+    old_pos = g_vfs_afp_reply_get_pos (reply);
+    g_vfs_afp_reply_seek (reply, start_pos + UTF8Name_offset, G_SEEK_SET);
+
+    g_vfs_afp_reply_read_afp_name (reply, TRUE, &afp_name);
+    utf8_name = g_vfs_afp_name_get_string (afp_name);    
+    g_vfs_afp_name_unref (afp_name);
+
+    g_file_info_set_name (info, utf8_name);
+    g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
+                                      utf8_name);
+
+    /* Set file as hidden if it begins with a dot */
+    if (utf8_name[0] == '.')
+      g_file_info_set_is_hidden (info, TRUE);
+
+    if (!directory)
+    {
+      char *content_type;
+      GIcon *icon;
+
+      content_type = g_content_type_guess (utf8_name, NULL, 0, NULL);
+      g_file_info_set_content_type (info, content_type);
+      g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE,
+                                        content_type);
+
+      icon = g_content_type_get_icon (content_type);
+      g_file_info_set_icon (info, icon);
+
+      g_object_unref (icon);
+      g_free (content_type);
+    }
+    
+    g_free (utf8_name);
+
+    g_vfs_afp_reply_seek (reply, old_pos, G_SEEK_SET);
+  }
+
+  if (bitmap & AFP_FILEDIR_BITMAP_UNIX_PRIVS_BIT)
+  {
+    guint32 uid, gid, permissions, ua_permissions;
+
+    g_vfs_afp_reply_read_uint32 (reply, &uid);
+    g_vfs_afp_reply_read_uint32 (reply, &gid);
+    g_vfs_afp_reply_read_uint32 (reply, &permissions);
+    /* ua_permissions */
+    g_vfs_afp_reply_read_uint32 (reply, &ua_permissions);
+
+    g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE, permissions);
+    g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID, uid);
+    g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_GID, gid);
+
+    g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_AFP_UA_PERMISSIONS,
+                                      ua_permissions);
+    
+    if (uid == server->user_id)
+      set_access_attributes_trusted (info, (permissions >> 6) & 0x7);
+    else if (gid == server->group_id)
+      set_access_attributes (info, (permissions >> 3) & 0x7);
+    else
+      set_access_attributes (info, (permissions >> 0) & 0x7);
+  }
 }
\ No newline at end of file
diff --git a/daemon/gvfsafpserver.h b/daemon/gvfsafpserver.h
index 0688ff1..25753ca 100644
--- a/daemon/gvfsafpserver.h
+++ b/daemon/gvfsafpserver.h
@@ -27,6 +27,7 @@
 
 #include "gmountsource.h"
 #include "gvfsafpconnection.h"
+#include "gvfsafpvolume.h"
 
 G_BEGIN_DECLS
 
@@ -69,6 +70,9 @@ struct _GVfsAfpServer
   AfpVersion          version;
 
   gint32              time_diff;
+
+  guint32             user_id;
+  guint32             group_id;
 };
 
 GType              g_vfs_afp_server_get_type             (void) G_GNUC_CONST;
@@ -85,16 +89,6 @@ gboolean           g_vfs_afp_server_login                (GVfsAfpServer *afp_ser
 gint64             g_vfs_afp_server_time_to_local_time   (GVfsAfpServer *afp_serv,
                                                           gint32         server_time);
 
-void               g_vfs_afp_server_get_vol_parms        (GVfsAfpServer       *server,
-                                                          guint16              volume_id,
-                                                          guint16              vol_bitmap,
-                                                          GCancellable        *cancellable,
-                                                          GAsyncReadyCallback  callback,
-                                                          gpointer             user_data);
-GFileInfo *        g_vfs_afp_server_get_vol_parms_finish (GVfsAfpServer       *server,
-                                                          GAsyncResult        *result,
-                                                          GError             **error);
-
 typedef struct _GVfsAfpVolumeData GVfsAfpVolumeData;
 struct _GVfsAfpVolumeData
 {
@@ -110,6 +104,17 @@ GPtrArray *        g_vfs_afp_server_get_volumes_finish   (GVfsAfpServer  *server
                                                           GAsyncResult   *result,
                                                           GError         **error);
 
+GVfsAfpVolume *    g_vfs_afp_server_mount_volume_sync (GVfsAfpServer *server,
+                                                       const char    *volume_name,
+                                                       GCancellable  *cancellable,
+                                                       GError        **error);
+
+void               g_vfs_afp_server_fill_info         (GVfsAfpServer *server,
+                                                       GFileInfo     *info,
+                                                       GVfsAfpReply  *reply,
+                                                       gboolean       directory,
+                                                       guint16        bitmap);
+
 G_END_DECLS
 
 #endif /* _GVFSAFPSERVER_H_ */
diff --git a/daemon/gvfsafputils.c b/daemon/gvfsafputils.c
index 9d0872d..7875390 100644
--- a/daemon/gvfsafputils.c
+++ b/daemon/gvfsafputils.c
@@ -126,3 +126,15 @@ afp_result_code_to_gerror (AfpResultCode res_code)
     return g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED,
                         _("Got unknown error code %d from server"), res_code);
 }
+
+gboolean
+is_root (const char *filename)
+{
+  const char *p;
+
+  p = filename;
+  while (*p == '/')
+    p++;
+
+  return *p == 0;
+}
diff --git a/daemon/gvfsafputils.h b/daemon/gvfsafputils.h
index 467cc13..6f4388e 100644
--- a/daemon/gvfsafputils.h
+++ b/daemon/gvfsafputils.h
@@ -82,8 +82,14 @@ typedef enum
   AFP_RESULT_DISK_QUOTA_EXCEEDED  = -5047
 } AfpResultCode;
 
-GError *
-afp_result_code_to_gerror (AfpResultCode res_code);
+#define G_FILE_ATTRIBUTE_AFP_NODE_ID        "afp::node-id"
+#define G_FILE_ATTRIBUTE_AFP_PARENT_DIR_ID  "afp::parent-dir-id"
+#define G_FILE_ATTRIBUTE_AFP_CHILDREN_COUNT "afp::children-count"
+#define G_FILE_ATTRIBUTE_AFP_UA_PERMISSIONS "afp::ua-permisssions"
+
+GError *afp_result_code_to_gerror (AfpResultCode res_code);
+
+gboolean is_root (const char *filename);
 
 G_END_DECLS
 
diff --git a/daemon/gvfsafpvolume.c b/daemon/gvfsafpvolume.c
new file mode 100644
index 0000000..5bc6032
--- /dev/null
+++ b/daemon/gvfsafpvolume.c
@@ -0,0 +1,3033 @@
+ /* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) Carl-Anton Ingmarsson 2011 <ca ingmarsson gmail com>
+ *
+ * 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: Carl-Anton Ingmarsson <ca ingmarsson gmail com>
+ */
+
+#include <glib/gi18n.h>
+
+#include "gvfsafpserver.h"
+
+#include "gvfsafpvolume.h"
+
+
+
+G_DEFINE_TYPE (GVfsAfpVolume, g_vfs_afp_volume, G_TYPE_OBJECT);
+
+struct _GVfsAfpVolumePrivate
+{
+  GVfsAfpServer *server;
+  gboolean mounted;
+
+  guint16 attributes;
+  guint16 volume_id;
+};
+
+static void
+g_vfs_afp_volume_init (GVfsAfpVolume *volume)
+{
+  GVfsAfpVolumePrivate *priv;
+  
+  volume->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (volume, G_VFS_TYPE_AFP_VOLUME,
+                                                     GVfsAfpVolumePrivate);
+  priv->mounted = FALSE;
+}
+
+static void
+g_vfs_afp_volume_finalize (GObject *object)
+{
+  /* TODO: Add deinitalization code here */
+
+  G_OBJECT_CLASS (g_vfs_afp_volume_parent_class)->finalize (object);
+}
+
+static void
+g_vfs_afp_volume_class_init (GVfsAfpVolumeClass *klass)
+{
+  GObjectClass* object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = g_vfs_afp_volume_finalize;
+
+  g_type_class_add_private (klass, sizeof (GVfsAfpVolumePrivate));
+}
+
+GVfsAfpVolume *
+g_vfs_afp_volume_new (GVfsAfpServer *server)
+{
+  GVfsAfpVolume *volume;
+  GVfsAfpVolumePrivate *priv;
+
+  g_return_val_if_fail (G_VFS_IS_AFP_SERVER (server), NULL);
+  
+  volume = g_object_new (G_VFS_TYPE_AFP_VOLUME, NULL);
+  priv = volume->priv;
+
+  priv->server = server;
+
+  return volume;
+}
+
+gboolean
+g_vfs_afp_volume_mount_sync (GVfsAfpVolume *volume,
+                             const char    *volume_name,
+                             GCancellable  *cancellable,
+                             GError       **error)
+{
+  GVfsAfpVolumePrivate *priv;
+  GVfsAfpCommand *comm;
+  GVfsAfpReply *reply;
+  gboolean res;
+  AfpResultCode res_code;
+
+  g_return_val_if_fail (G_VFS_IS_AFP_VOLUME (volume), FALSE);
+  g_return_val_if_fail (volume_name != NULL, FALSE);
+
+  priv = volume->priv;
+  
+  /* Open Volume */
+  comm = g_vfs_afp_command_new (AFP_COMMAND_OPEN_VOL);
+  /* pad byte */
+  g_vfs_afp_command_put_byte (comm, 0);
+  /* Volume Bitmap */
+  g_vfs_afp_command_put_uint16 (comm, AFP_VOLUME_BITMAP_VOL_ID_BIT | AFP_VOLUME_BITMAP_ATTRIBUTE_BIT);
+
+  /* VolumeName */
+  g_vfs_afp_command_put_pascal (comm, volume_name);
+
+  /* TODO: password? */
+
+  res = g_vfs_afp_connection_send_command_sync (priv->server->conn, comm, cancellable,
+                                                error);
+  g_object_unref (comm);
+  if (!res)
+    return FALSE;
+
+  reply = g_vfs_afp_connection_read_reply_sync (priv->server->conn, cancellable, error);
+  if (!reply)
+    return FALSE;
+
+  res_code = g_vfs_afp_reply_get_result_code (reply);
+  if (res_code != AFP_RESULT_NO_ERROR)
+  {
+    g_object_unref (reply);
+    goto generic_error;
+  }
+  
+  /* Volume Bitmap */
+  g_vfs_afp_reply_read_uint16 (reply, NULL);
+  /* Volume Attributes Bitmap */
+  g_vfs_afp_reply_read_uint16 (reply, &priv->attributes);
+  /* Volume ID */
+  g_vfs_afp_reply_read_uint16 (reply, &priv->volume_id);
+  
+  g_object_unref (reply);
+
+  priv->mounted = TRUE;
+  return TRUE;
+
+generic_error:
+  /* Translators: first %s is volumename and second servername */ 
+  g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+               _("Couldn't mount AFP volume %s on %s"), volume_name,
+               priv->server->server_name);
+  return FALSE;
+}
+
+guint16
+g_vfs_afp_volume_get_attributes (GVfsAfpVolume *volume)
+{
+  GVfsAfpVolumePrivate *priv = volume->priv;
+
+  g_return_val_if_fail (priv->mounted, 0);
+  
+  return priv->attributes; 
+}
+
+guint16
+g_vfs_afp_volume_get_id (GVfsAfpVolume *volume)
+{
+  GVfsAfpVolumePrivate *priv = volume->priv;
+
+  g_return_val_if_fail (priv->mounted, 0);
+  
+  return priv->volume_id; 
+}
+
+static void
+get_vol_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  GVfsAfpConnection *connection = G_VFS_AFP_CONNECTION (source_object);
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+  
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
+  GVfsAfpVolumePrivate *priv = volume->priv;
+
+  GVfsAfpReply *reply;
+  GError *err = NULL;
+  AfpResultCode res_code;
+  
+  guint16 vol_bitmap;
+  GFileInfo *info;
+
+  reply = g_vfs_afp_connection_send_command_finish (connection, res, &err);
+  if (!reply)
+  {
+    g_simple_async_result_take_error (simple, err);
+    goto done;
+  }
+
+  res_code = g_vfs_afp_reply_get_result_code (reply);
+  if (res_code != AFP_RESULT_NO_ERROR)
+  {
+    g_object_unref (reply);
+
+    g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
+    goto done;
+  }
+
+  g_vfs_afp_reply_read_uint16 (reply, &vol_bitmap);
+
+  info = g_file_info_new ();
+
+  if (vol_bitmap & AFP_VOLUME_BITMAP_ATTRIBUTE_BIT)
+  {
+    guint16 vol_attrs_bitmap;
+    
+    g_vfs_afp_reply_read_uint16 (reply, &vol_attrs_bitmap);
+    
+    g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_FILESYSTEM_READONLY,
+                                       vol_attrs_bitmap & AFP_VOLUME_ATTRIBUTES_BITMAP_READ_ONLY);
+  }
+
+  if (vol_bitmap & AFP_VOLUME_BITMAP_CREATE_DATE_BIT)
+  {
+    gint32 create_date;
+    gint64 create_date_local;
+
+    g_vfs_afp_reply_read_int32 (reply, &create_date);
+
+    create_date_local = g_vfs_afp_server_time_to_local_time (priv->server, create_date);
+    g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_CREATED,
+                                      create_date_local);
+  }
+
+  if (vol_bitmap & AFP_VOLUME_BITMAP_MOD_DATE_BIT)
+  {
+    gint32 mod_date;
+    gint64 mod_date_local;
+
+    g_vfs_afp_reply_read_int32 (reply, &mod_date);
+
+    mod_date_local = g_vfs_afp_server_time_to_local_time (priv->server, mod_date);
+    g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED,
+                                      mod_date_local);
+  }
+
+  if (vol_bitmap & AFP_VOLUME_BITMAP_EXT_BYTES_FREE_BIT)
+  {
+    guint64 bytes_free;
+
+    g_vfs_afp_reply_read_uint64 (reply, &bytes_free);
+    g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_FREE,
+                                      bytes_free);
+  }
+
+  if (vol_bitmap & AFP_VOLUME_BITMAP_EXT_BYTES_TOTAL_BIT)
+  {
+    guint64 bytes_total;
+
+    g_vfs_afp_reply_read_uint64 (reply, &bytes_total);
+    g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FILESYSTEM_SIZE,
+                                      bytes_total);
+  }
+
+  g_object_unref (reply);
+  
+  g_simple_async_result_set_op_res_gpointer (simple, info, g_object_unref);
+
+done:
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+/*
+ * g_vfs_afp_volume_get_parms:
+ * 
+ * @volume: a #GVfsAfpVolume
+ * @vol_bitmap: bitmap describing the parameters that should be received.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: callback to call when the request is satisfied.
+ * @user_data: the data to pass to callback function.
+ * 
+ * Asynchronously retrieves the parameters specified by @vol_bitmap from @volume
+ */
+void
+g_vfs_afp_volume_get_parms (GVfsAfpVolume       *volume,
+                            guint16              vol_bitmap,
+                            GCancellable        *cancellable,
+                            GAsyncReadyCallback  callback,
+                            gpointer             user_data)
+{
+  GVfsAfpVolumePrivate *priv;
+  GVfsAfpCommand *comm;
+  GSimpleAsyncResult *simple;
+
+  priv = volume->priv;
+  
+  comm = g_vfs_afp_command_new (AFP_COMMAND_GET_VOL_PARMS);
+  /* pad byte */
+  g_vfs_afp_command_put_byte (comm, 0);
+  /* Volume ID */
+  g_vfs_afp_command_put_uint16 (comm, priv->volume_id);
+  /* Volume Bitmap */
+  g_vfs_afp_command_put_uint16 (comm, vol_bitmap);
+
+  simple = g_simple_async_result_new (G_OBJECT (volume), callback, user_data,
+                                      g_vfs_afp_volume_get_parms);
+                                      
+  
+  g_vfs_afp_connection_send_command (priv->server->conn, comm, NULL, get_vol_parms_cb,
+                                     cancellable, simple);
+  g_object_unref (comm);
+}
+
+/*
+ * g_vfs_afp_volume_get_parms_finish:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @result: a #GAsyncResult.
+ * @error: a #GError, %NULL to ignore.
+ * 
+ * Finalizes the asynchronous operation started by
+ * g_vfs_afp_volume_get_parms.
+ * 
+ * Returns: (transfer full): A #GFileInfo with the requested parameters or %NULL
+ * on error.
+ */
+GFileInfo *
+g_vfs_afp_volume_get_parms_finish (GVfsAfpVolume  *volume,
+                                   GAsyncResult   *result,
+                                   GError         **error)
+{
+  GSimpleAsyncResult *simple;
+  
+  g_return_val_if_fail (g_simple_async_result_is_valid (result,
+                                                        G_OBJECT (volume),
+                                                        g_vfs_afp_volume_get_parms),
+                        NULL);
+
+  simple = (GSimpleAsyncResult *)result;
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return NULL;
+
+  return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
+}
+
+typedef struct
+{
+  gint16 fork_refnum;
+  GFileInfo *info;
+} OpenForkData;
+
+static void
+open_fork_data_free (OpenForkData *data)
+{
+  g_object_unref (data->info);
+
+  g_slice_free (OpenForkData, data);
+}
+
+static void
+open_fork_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  GVfsAfpConnection *afp_conn = G_VFS_AFP_CONNECTION (source_object);
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+  
+  GVfsAfpVolume *volume;
+  GVfsAfpVolumePrivate *priv;
+  GVfsAfpReply *reply;
+  GError *err = NULL;
+  AfpResultCode res_code;
+
+  OpenForkData *data;
+  guint16 file_bitmap;
+
+  volume = G_VFS_AFP_VOLUME (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
+  priv = volume->priv;
+  
+  reply = g_vfs_afp_connection_send_command_finish (afp_conn, res, &err);
+  if (!reply)
+  {
+    g_simple_async_result_take_error (simple, err);
+    goto done;
+  }
+
+  res_code = g_vfs_afp_reply_get_result_code (reply);
+  if (res_code != AFP_RESULT_NO_ERROR)
+  {
+    g_object_unref (reply);
+
+    switch (res_code)
+    {
+      case AFP_RESULT_ACCESS_DENIED:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+                                         _("Permission denied"));
+        break;
+      case AFP_RESULT_OBJECT_NOT_FOUND:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                                         _("File doesn't exist"));
+        break;
+      case AFP_RESULT_OBJECT_TYPE_ERR:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
+                                         _("File is directory"));
+        break;
+      case AFP_RESULT_TOO_MANY_FILES_OPEN:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_TOO_MANY_OPEN_FILES,
+                                         _("Too many files open"));
+        break;
+      default:
+        g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
+        break;
+    }
+    goto done;
+  }
+
+  data = g_slice_new (OpenForkData);
+  
+  g_vfs_afp_reply_read_uint16 (reply, &file_bitmap);
+  g_vfs_afp_reply_read_int16  (reply, &data->fork_refnum);
+
+  data->info = g_file_info_new ();
+  g_vfs_afp_server_fill_info (priv->server, data->info, reply, FALSE, file_bitmap);
+  g_object_unref (reply);
+
+  g_simple_async_result_set_op_res_gpointer (simple, data,
+                                             (GDestroyNotify)open_fork_data_free);
+
+done:
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+/*
+ * g_vfs_afp_volume_open_fork:
+ * 
+ * @volume: a #GVfsAfpVolume
+ * @filename: file to open fork for.
+ * @access_mode:
+ * @bitmap:
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: callback to call when the request is satisfied.
+ * @user_data: the data to pass to callback function.
+ * 
+ * Asynchronously opens a fork corresponding to @filename with the requested
+ * access rights.
+ */
+void
+g_vfs_afp_volume_open_fork (GVfsAfpVolume      *volume,
+                            const char         *filename,
+                            guint16             access_mode,
+                            guint16             bitmap,
+                            GCancellable       *cancellable,
+                            GAsyncReadyCallback callback,
+                            gpointer            user_data)
+{
+  GVfsAfpVolumePrivate *priv;
+  GVfsAfpCommand *comm;
+  GSimpleAsyncResult *simple;
+
+  g_return_if_fail (G_VFS_IS_AFP_VOLUME (volume));
+
+  priv = volume->priv;
+  
+  if (is_root (filename))
+  {
+    g_simple_async_report_error_in_idle (G_OBJECT (volume), callback,
+                                         user_data, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
+                                         _("File is directory"));
+    return;
+  }
+
+  comm = g_vfs_afp_command_new (AFP_COMMAND_OPEN_FORK);
+  /* data fork */
+  g_vfs_afp_command_put_byte (comm, 0);
+
+  /* Volume ID */
+  g_vfs_afp_command_put_uint16 (comm, g_vfs_afp_volume_get_id (volume));
+  /* Directory ID */
+  g_vfs_afp_command_put_uint32 (comm, 2);
+
+  /* Bitmap */
+  g_vfs_afp_command_put_uint16 (comm, bitmap);
+
+  /* AccessMode */
+  g_vfs_afp_command_put_uint16 (comm, access_mode);
+
+  /* Pathname */
+  g_vfs_afp_command_put_pathname (comm, filename);
+
+  simple = g_simple_async_result_new (G_OBJECT (volume), callback,
+                                      user_data, g_vfs_afp_volume_open_fork);
+  
+  g_vfs_afp_connection_send_command (priv->server->conn, comm, NULL,
+                                     open_fork_cb, cancellable, simple);
+  g_object_unref (comm);
+}
+
+/*
+ * g_vfs_afp_volume_open_fork_finish:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @result: a #GAsyncResult.
+ * @fork_refnum: (out) the reference id of the newly opened fork.
+ * @info: (out callee-allocates) a #GFileInfo containing the requested parameters.
+ * @error: a #GError, %NULL to ignore.
+ * 
+ * Finalizes the asynchronous operation started by
+ * g_vfs_afp_volume_open_fork.
+ * 
+ * Returns: %TRUE on success, %FALSE on error.
+ */
+gboolean
+g_vfs_afp_volume_open_fork_finish (GVfsAfpVolume  *volume,
+                                   GAsyncResult   *res,
+                                   gint16         *fork_refnum,
+                                   GFileInfo      **info,
+                                   GError         **error)
+{
+  GSimpleAsyncResult *simple;
+  OpenForkData *data;
+
+  g_return_val_if_fail (g_simple_async_result_is_valid (res,
+                                                        G_OBJECT (volume),
+                                                        g_vfs_afp_volume_open_fork),
+                        FALSE);
+
+  simple = (GSimpleAsyncResult *)res;
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+
+  data = g_simple_async_result_get_op_res_gpointer (simple);
+  if (fork_refnum)
+    *fork_refnum = data->fork_refnum;
+  if (info)
+    *info = g_object_ref (data->info);
+
+  return TRUE;
+}
+
+static void
+close_fork_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  GVfsAfpConnection *afp_conn = G_VFS_AFP_CONNECTION (source_object);
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+  GVfsAfpReply *reply;
+  GError *err = NULL;
+  AfpResultCode res_code;
+
+  reply = g_vfs_afp_connection_send_command_finish (afp_conn, res, &err);
+  if (!reply)
+  {
+    g_simple_async_result_take_error (simple, err);
+    goto done;
+  }
+
+  res_code = g_vfs_afp_reply_get_result_code (reply);
+  g_object_unref (reply);
+  
+  if (res_code != AFP_RESULT_NO_ERROR)
+    g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
+
+done:
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+/*
+ * g_vfs_afp_volume_close_fork:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @fork_refnum: the reference id of the fork which is to be closed.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: callback to call when the request is satisfied.
+ * @user_data: the data to pass to callback function.
+ * 
+ * Asynchronously closes an open fork specified by @fork_refnum.
+ */
+void
+g_vfs_afp_volume_close_fork (GVfsAfpVolume       *volume,
+                             gint16               fork_refnum,
+                             GCancellable        *cancellable,
+                             GAsyncReadyCallback  callback,
+                             gpointer             user_data)
+{
+  GVfsAfpVolumePrivate *priv;
+  GVfsAfpCommand *comm;
+  GSimpleAsyncResult *simple;
+
+  g_return_if_fail (G_VFS_IS_AFP_VOLUME (volume));
+
+  priv = volume->priv;
+  
+  comm = g_vfs_afp_command_new (AFP_COMMAND_CLOSE_FORK);
+  /* pad byte */
+  g_vfs_afp_command_put_byte (comm, 0);
+
+  /* OForkRefNum */
+  g_vfs_afp_command_put_int16 (comm, fork_refnum);
+
+  simple = g_simple_async_result_new (G_OBJECT (volume), callback, user_data,
+                                      g_vfs_afp_volume_close_fork);
+  
+  g_vfs_afp_connection_send_command (priv->server->conn, comm, NULL,
+                                     close_fork_cb, cancellable, simple);
+  g_object_unref (comm);
+}
+
+/*
+ * g_vfs_afp_volume_close_fork_finish:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @result: a #GAsyncResult.
+ * @error: a #GError, %NULL to ignore.
+ * 
+ * Finalizes the asynchronous operation started by
+ * g_vfs_afp_volume_close_fork.
+ * 
+ * Returns: %TRUE on success, %FALSE on error.
+ */
+gboolean
+g_vfs_afp_volume_close_fork_finish (GVfsAfpVolume  *volume,
+                                    GAsyncResult   *result,
+                                    GError         **error)
+{
+  GSimpleAsyncResult *simple;
+  
+  g_return_val_if_fail (g_simple_async_result_is_valid (result,
+                                                        G_OBJECT (volume),
+                                                        g_vfs_afp_volume_close_fork),
+                        FALSE);
+
+  simple = (GSimpleAsyncResult *)result;
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+
+  return TRUE;
+}
+
+static void
+delete_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  GVfsAfpConnection *afp_conn = G_VFS_AFP_CONNECTION (source_object);
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+  GVfsAfpReply *reply;
+  GError *err = NULL;
+  AfpResultCode res_code;
+
+  reply = g_vfs_afp_connection_send_command_finish (afp_conn, res, &err);
+  if (!reply)
+  {
+    g_simple_async_result_take_error (simple, err);
+    goto done;
+  }
+
+  res_code = g_vfs_afp_reply_get_result_code (reply);
+  g_object_unref (reply);
+  
+  if (res_code != AFP_RESULT_NO_ERROR)
+  {
+    switch (res_code)
+    {
+      case AFP_RESULT_ACCESS_DENIED:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+                                  _("Permission denied"));
+        break;
+      case AFP_RESULT_FILE_BUSY:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_BUSY,
+                                         _("Target file is open"));
+        break;                           
+      case AFP_RESULT_DIR_NOT_EMPTY:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NOT_EMPTY,
+                                  _("Directory not empty"));
+        break;
+      case AFP_RESULT_OBJECT_LOCKED:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_FAILED,
+                                  _("Target object is marked as not deletable (DeleteInhibit)"));
+        break;
+      case AFP_RESULT_OBJECT_NOT_FOUND:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                                  _("Target object doesn't exist"));
+        break;
+      case AFP_RESULT_VOL_LOCKED:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+                                  _("Volume is read-only"));
+        break;
+      default:
+        g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
+        break;
+    }
+  }
+
+done:
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+/*
+ * g_vfs_afp_volume_delete:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @filename: file to delete.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: callback to call when the request is satisfied.
+ * @user_data: the data to pass to callback function.
+ * 
+ * Asynchronously deletes the file @filename.
+ */
+void
+g_vfs_afp_volume_delete (GVfsAfpVolume       *volume,
+                         const char          *filename,
+                         GCancellable        *cancellable,
+                         GAsyncReadyCallback  callback,
+                         gpointer             user_data)
+{
+  GVfsAfpVolumePrivate *priv;
+  GVfsAfpCommand *comm;
+  GSimpleAsyncResult *simple;
+
+  g_return_if_fail (G_VFS_IS_AFP_VOLUME (volume));
+
+  priv = volume->priv;
+  
+  comm = g_vfs_afp_command_new (AFP_COMMAND_DELETE);
+  /* pad byte */
+  g_vfs_afp_command_put_byte (comm, 0);
+  /* Volume ID */
+  g_vfs_afp_command_put_uint16 (comm, g_vfs_afp_volume_get_id (volume));
+  /* Directory ID 2 == / */
+  g_vfs_afp_command_put_uint32 (comm, 2);
+
+  /* Pathname */
+  g_vfs_afp_command_put_pathname (comm, filename);
+
+  simple = g_simple_async_result_new (G_OBJECT (volume), callback,
+                                      user_data, g_vfs_afp_volume_delete);
+  
+  g_vfs_afp_connection_send_command (priv->server->conn, comm, NULL,
+                                     delete_cb, cancellable, simple);
+  g_object_unref (comm);
+}
+
+/*
+ * g_vfs_afp_volume_delete_finish:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @result: a #GAsyncResult.
+ * @error: a #GError, %NULL to ignore.
+ * 
+ * Finalizes the asynchronous operation started by
+ * g_vfs_afp_volume_delete.
+ * 
+ * Returns: %TRUE on success, %FALSE on error.
+ */
+gboolean
+g_vfs_afp_volume_delete_finish (GVfsAfpVolume  *volume,
+                                GAsyncResult   *result,
+                                GError         **error)
+{
+  GSimpleAsyncResult *simple;
+
+  g_return_val_if_fail (g_simple_async_result_is_valid (result,
+                                                        G_OBJECT (volume),
+                                                        g_vfs_afp_volume_delete),
+                        FALSE);
+
+  simple = (GSimpleAsyncResult *)result;
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+
+  return TRUE;
+}
+
+typedef struct
+{
+  char *filename;
+  gboolean hard_create;
+  GCancellable *cancellable;
+} CreateFileData;
+
+static void
+create_file_data_free (CreateFileData *cfd)
+{
+  g_free (cfd->filename);
+  if (cfd->cancellable)
+    g_object_unref (cfd->cancellable);
+
+  g_slice_free (CreateFileData, cfd);
+}
+
+static void
+create_file_cb (GObject *object, GAsyncResult *res, gpointer user_data)
+{
+  GVfsAfpConnection *conn = G_VFS_AFP_CONNECTION (object);
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+  GVfsAfpReply *reply;
+  GError *err = NULL;
+  AfpResultCode res_code;
+
+  
+  reply = g_vfs_afp_connection_send_command_finish (conn, res, &err);
+  if (!reply)
+  {
+    g_simple_async_result_take_error (simple, err);
+    goto done;
+  }
+
+  res_code = g_vfs_afp_reply_get_result_code (reply);
+  g_object_unref (reply);
+  if (res_code != AFP_RESULT_NO_ERROR)
+  {
+    switch (res_code)
+    {
+      case AFP_RESULT_ACCESS_DENIED:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+                                  _("Permission denied"));
+        break;
+      case AFP_RESULT_DISK_FULL:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NO_SPACE,
+                                  _("Not enough space on volume"));
+        break;
+      case AFP_RESULT_FILE_BUSY:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_EXISTS,
+                                  _("Target file is open"));
+        break;
+      case AFP_RESULT_OBJECT_EXISTS:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_EXISTS,
+                                  _("Target file already exists"));
+        break;
+      case AFP_RESULT_OBJECT_NOT_FOUND:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                                  _("Ancestor directory doesn't exist"));
+        break;
+      case AFP_RESULT_VOL_LOCKED:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+                                  _("Volume is read-only"));
+        break;
+      default:
+        g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
+        break;
+    }
+  }
+
+done:
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+static void
+create_file_get_filedir_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
+  GVfsAfpVolumePrivate *priv = volume->priv;
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+  CreateFileData *cfd = g_simple_async_result_get_op_res_gpointer (simple);
+  
+  GFileInfo *info;
+  GError *err = NULL;
+
+  guint32 dir_id;
+  char *basename;
+  GVfsAfpCommand *comm;
+
+  info = g_vfs_afp_volume_get_filedir_parms_finish (volume, res, &err);
+  if (!info)
+  {
+    g_simple_async_result_take_error (simple, err);
+    g_simple_async_result_complete (simple);
+    g_object_unref (simple);
+    return;
+  }
+
+  dir_id = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_AFP_NODE_ID);
+  g_object_unref (info);
+
+  comm = g_vfs_afp_command_new (AFP_COMMAND_CREATE_FILE);
+  /* soft/hard create */
+  g_vfs_afp_command_put_byte (comm, cfd->hard_create ? 0x80 : 0x00);
+  /* Volume ID */
+  g_vfs_afp_command_put_uint16 (comm, g_vfs_afp_volume_get_id (volume));
+  /* Directory ID */
+  g_vfs_afp_command_put_uint32 (comm, dir_id);
+
+  /* Pathname */
+  basename = g_path_get_basename (cfd->filename);
+  g_vfs_afp_command_put_pathname (comm, basename);
+  g_free (basename);
+
+  g_vfs_afp_connection_send_command (priv->server->conn, comm, NULL,
+                                     create_file_cb, cfd->cancellable, simple);
+  g_object_unref (comm);
+}
+
+/*
+ * g_vfs_afp_volume_create_file:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @filename: path to the new file to create.
+ * @hard_create: if %TRUE this call will overwrite an already existing file.
+ * If %FALSE it will error out instead.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: callback to call when the request is satisfied.
+ * @user_data: the data to pass to callback function.
+ * 
+ * Asynchronously creates a file at @filename.
+ */
+void
+g_vfs_afp_volume_create_file (GVfsAfpVolume      *volume,
+                              const char         *filename,
+                              gboolean            hard_create,
+                              GCancellable       *cancellable,
+                              GAsyncReadyCallback callback,
+                              gpointer            user_data)
+{
+  CreateFileData *cfd;
+  GSimpleAsyncResult *simple;
+  char *dirname;
+
+  cfd = g_slice_new0 (CreateFileData);
+  cfd->filename = g_strdup (filename);
+  cfd->hard_create = hard_create;
+  if (cancellable)
+    cfd->cancellable = g_object_ref (cancellable);
+  
+  simple = g_simple_async_result_new (G_OBJECT (volume), callback, user_data,
+                                      g_vfs_afp_volume_create_file);
+  g_simple_async_result_set_op_res_gpointer (simple, cfd,
+                                             (GDestroyNotify)create_file_data_free);
+
+  dirname = g_path_get_dirname (filename);
+  g_vfs_afp_volume_get_filedir_parms (volume, dirname, 0, AFP_DIR_BITMAP_NODE_ID_BIT,
+                                      cancellable, create_file_get_filedir_parms_cb, simple);
+  g_free (dirname);
+}
+
+/*
+ * g_vfs_afp_volume_create_file_finish:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @result: a #GAsyncResult.
+ * @error: a #GError, %NULL to ignore.
+ * 
+ * Finalizes the asynchronous operation started by
+ * g_vfs_afp_volume_create_file.
+ * 
+ * Returns: %TRUE on success, %FALSE on error.
+ */
+gboolean
+g_vfs_afp_volume_create_file_finish (GVfsAfpVolume  *volume,
+                                     GAsyncResult   *result,
+                                     GError         **error)
+{
+  GSimpleAsyncResult *simple;
+  
+  g_return_val_if_fail (g_simple_async_result_is_valid (result,
+                                                        G_OBJECT (volume),
+                                                        g_vfs_afp_volume_create_file),
+                        FALSE);
+
+  simple = (GSimpleAsyncResult *)result;
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+
+  return TRUE;
+}
+
+typedef struct
+{
+  char *basename;
+  GCancellable *cancellable;
+} CreateDirData;
+
+static void
+create_dir_data_free (CreateDirData *cdd)
+{
+  g_free (cdd->basename);
+  g_object_unref (cdd->cancellable);
+
+  g_slice_free (CreateDirData, cdd);
+}
+
+static void
+make_directory_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  GVfsAfpConnection *afp_conn = G_VFS_AFP_CONNECTION (source_object);
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+  GVfsAfpReply *reply;
+  GError *err = NULL;
+  AfpResultCode res_code;
+
+  reply = g_vfs_afp_connection_send_command_finish (afp_conn, res, &err);
+  if (!reply)
+  {
+    g_simple_async_result_take_error (simple, err);
+    goto done;
+  }
+
+  res_code = g_vfs_afp_reply_get_result_code (reply);
+  g_object_unref (reply);
+
+  if (res_code != AFP_RESULT_NO_ERROR)
+  {
+    switch (res_code)
+    {
+      case AFP_RESULT_ACCESS_DENIED:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+                                  _("Permission denied"));
+        break;
+      case AFP_RESULT_DISK_FULL:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NO_SPACE,
+                                  _("Not enough space on volume"));
+        break;
+      case AFP_RESULT_FLAT_VOL:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                                  _("Volume is flat and doesn't support directories"));
+        break;
+      case AFP_RESULT_OBJECT_NOT_FOUND:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                                  _("Ancestor directory doesn't exist"));
+        break;
+      case AFP_RESULT_OBJECT_EXISTS:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_EXISTS,
+                                  _("Target directory already exists"));
+        break;
+      case AFP_RESULT_VOL_LOCKED:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+                                  _("Volume is read-only"));
+        break;
+      default:
+        g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
+        break;
+    }
+    goto done;
+  }
+
+done:
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+static void
+create_directory_get_filedir_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+  CreateDirData *cdd = g_simple_async_result_get_op_res_gpointer (simple);
+
+  GFileInfo *info = NULL;
+  GError *err = NULL;
+
+  guint32 dir_id;
+  GVfsAfpCommand *comm;
+  
+  info = g_vfs_afp_volume_get_filedir_parms_finish (volume, res, &err);
+  if (!info)
+    goto error;
+
+  if (g_cancellable_set_error_if_cancelled (cdd->cancellable, &err))
+    goto error;
+  
+  dir_id = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_AFP_NODE_ID);
+  g_object_unref (info);
+
+  comm = g_vfs_afp_command_new (AFP_COMMAND_CREATE_DIR);
+  /* pad byte */
+  g_vfs_afp_command_put_byte (comm, 0);
+  /* Volume ID */
+  g_vfs_afp_command_put_uint16 (comm, g_vfs_afp_volume_get_id (volume));
+  /* Directory ID */
+  g_vfs_afp_command_put_uint32 (comm, dir_id);
+
+  /* Pathname */
+  g_vfs_afp_command_put_pathname (comm, cdd->basename);
+  
+  g_vfs_afp_connection_send_command (volume->priv->server->conn, comm, NULL,
+                                     make_directory_cb, cdd->cancellable, simple);
+  g_object_unref (comm);
+  return;
+
+error:
+  g_clear_object (info);
+  g_simple_async_result_take_error (simple, err);
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+/*
+ * g_vfs_afp_volume_create_directory:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @directory: path to the new directory to create.
+ * @hard_create: if %TRUE this call will overwrite an already existing file.
+ * If %FALSE it will error out instead.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: callback to call when the request is satisfied.
+ * @user_data: the data to pass to callback function.
+ * 
+ * Asynchronously creates a directory at @directory.
+ */
+void
+g_vfs_afp_volume_create_directory (GVfsAfpVolume      *volume,
+                                   const char         *directory,
+                                   GCancellable       *cancellable,
+                                   GAsyncReadyCallback callback,
+                                   gpointer            user_data)
+{
+  GSimpleAsyncResult *simple;
+  CreateDirData *cdd;
+  char *dirname;
+
+  g_return_if_fail (G_VFS_IS_AFP_VOLUME (volume));
+
+  simple = g_simple_async_result_new (G_OBJECT (volume), callback, user_data,
+                                      g_vfs_afp_volume_create_directory);
+
+  cdd = g_slice_new (CreateDirData);
+  cdd->basename = g_path_get_basename (directory);
+  cdd->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
+  
+  g_simple_async_result_set_op_res_gpointer (simple, cdd,
+                                             (GDestroyNotify)create_dir_data_free);
+
+  dirname = g_path_get_dirname (directory);
+  g_vfs_afp_volume_get_filedir_parms (volume, dirname, 0,
+                                      AFP_DIR_BITMAP_NODE_ID_BIT,
+                                      cancellable,
+                                      create_directory_get_filedir_parms_cb,
+                                      simple);
+  g_free (dirname);
+}
+
+/*
+ * g_vfs_afp_volume_create_directory_finish:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @result: a #GAsyncResult.
+ * @error: a #GError, %NULL to ignore.
+ * 
+ * Finalizes the asynchronous operation started by
+ * g_vfs_afp_volume_create_directory.
+ * 
+ * Returns: %TRUE on success, %FALSE on error.
+ */
+gboolean
+g_vfs_afp_volume_create_directory_finish (GVfsAfpVolume  *volume,
+                                          GAsyncResult   *result,
+                                          GError         **error)
+{
+  GSimpleAsyncResult *simple;
+  
+  g_return_val_if_fail (g_simple_async_result_is_valid (result,
+                                                        G_OBJECT (volume),
+                                                        g_vfs_afp_volume_create_directory),
+                        FALSE);
+
+  simple = (GSimpleAsyncResult *)result;
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+
+  return TRUE;
+}
+
+typedef struct
+{
+  char *filename;
+  char *new_name;
+  GCancellable *cancellable;
+} RenameData;
+
+static void
+rename_data_free (RenameData *rd)
+{
+  g_free (rd->filename);
+  g_free (rd->new_name);
+  g_object_unref (rd->cancellable);
+
+  g_slice_free (RenameData, rd);
+}
+
+static void
+rename_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  GVfsAfpConnection *conn = G_VFS_AFP_CONNECTION (source_object);
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+  GVfsAfpReply *reply;
+  GError *err = NULL;
+  AfpResultCode res_code;
+
+  reply = g_vfs_afp_connection_send_command_finish (conn, res, &err);
+  if (!reply)
+  {
+    g_simple_async_result_take_error (simple, err);
+    goto done;
+  }
+
+  res_code = g_vfs_afp_reply_get_result_code (reply);
+  g_object_unref (reply);
+
+  if (res_code != AFP_RESULT_NO_ERROR)
+  {
+    switch (res_code)
+    {
+      case AFP_RESULT_ACCESS_DENIED:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+                                         _("Permission denied"));
+        break;
+      case AFP_RESULT_CANT_RENAME:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_INVALID_FILENAME,
+                                  _("Can't rename volume"));
+        break;
+      case AFP_RESULT_OBJECT_EXISTS:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_EXISTS,
+                                  _("Object with that name already exists"));
+        break;
+      case AFP_RESULT_OBJECT_LOCKED:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_FAILED,
+                                  _("Target object is marked as not renameable (RenameInhibit)"));
+        break;
+      case AFP_RESULT_OBJECT_NOT_FOUND:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                                  _("Target object doesn't exist"));
+        break;
+      case AFP_RESULT_VOL_LOCKED:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+                                  _("Volume is read-only"));
+        break;
+      default:
+        g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
+        break;
+    }
+    goto done;
+  }
+
+done:
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+static void
+rename_get_filedir_parms_cb (GObject      *source_object,
+                             GAsyncResult *res,
+                             gpointer      user_data)
+{
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+  RenameData *rd = g_simple_async_result_get_op_res_gpointer (simple);
+
+  GFileInfo *info;
+  GError *err = NULL;
+
+  guint32 dir_id;
+  GVfsAfpCommand *comm;
+  char *basename;
+
+  info = g_vfs_afp_volume_get_filedir_parms_finish (volume, res, &err);
+  if (!info)
+  {
+    g_simple_async_result_take_error (simple, err);
+    g_simple_async_result_complete (simple);
+    g_object_unref (simple);
+    return;
+  }
+
+  dir_id = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_AFP_PARENT_DIR_ID);
+  g_object_unref (info);
+
+  comm = g_vfs_afp_command_new (AFP_COMMAND_RENAME);
+  /* pad byte */
+  g_vfs_afp_command_put_byte (comm, 0);
+  /* Volume ID */
+  g_vfs_afp_command_put_uint16 (comm, g_vfs_afp_volume_get_id (volume));
+  /* Directory ID */
+  g_vfs_afp_command_put_uint32 (comm, dir_id);
+
+  /* Pathname */
+  basename = g_path_get_basename (rd->filename);
+  g_vfs_afp_command_put_pathname (comm, basename);
+  g_free (basename);
+
+  /* NewName */
+  g_vfs_afp_command_put_pathname (comm, rd->new_name);
+
+  g_vfs_afp_connection_send_command (volume->priv->server->conn, comm, NULL,
+                                     rename_cb, rd->cancellable, simple);
+  g_object_unref (comm);
+}
+
+/*
+ * g_vfs_afp_volume_rename:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @filename: path to file to rename.
+ * @new_name: the new name of the file.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: callback to call when the request is satisfied.
+ * @user_data: the data to pass to callback function.
+ * 
+ * Asynchronously renames the file at @filename to @new_name.
+ */
+void
+g_vfs_afp_volume_rename (GVfsAfpVolume      *volume,
+                         const char         *filename,
+                         const char         *new_name,
+                         GCancellable       *cancellable,
+                         GAsyncReadyCallback callback,
+                         gpointer            user_data)
+{
+  GSimpleAsyncResult *simple;
+  RenameData *rd;
+
+  g_return_if_fail (G_VFS_IS_AFP_VOLUME (volume));
+
+  simple = g_simple_async_result_new (G_OBJECT (volume), callback, user_data,
+                                      g_vfs_afp_volume_rename);
+
+  rd = g_slice_new (RenameData);
+  rd->filename = g_strdup (filename);
+  rd->new_name = g_strdup (new_name);
+  rd->cancellable = g_object_ref (cancellable);
+  g_simple_async_result_set_op_res_gpointer (simple, rd,
+                                             (GDestroyNotify)rename_data_free);
+  
+  g_vfs_afp_volume_get_filedir_parms (volume, filename,
+                                      AFP_FILEDIR_BITMAP_PARENT_DIR_ID_BIT,
+                                      AFP_FILEDIR_BITMAP_PARENT_DIR_ID_BIT,
+                                      cancellable, rename_get_filedir_parms_cb,
+                                      simple);
+}
+
+/*
+ * g_vfs_afp_volume_rename_finish:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @result: a #GAsyncResult.
+ * @error: a #GError, %NULL to ignore.
+ * 
+ * Finalizes the asynchronous operation started by
+ * g_vfs_afp_volume_move_and_rename.
+ * 
+ * Returns: %TRUE on success, %FALSE on error.
+ */
+gboolean
+g_vfs_afp_volume_rename_finish (GVfsAfpVolume  *volume,
+                                GAsyncResult   *res,
+                                GError        **error)
+{
+  GSimpleAsyncResult *simple;
+
+  g_return_val_if_fail (g_simple_async_result_is_valid (res, G_OBJECT (volume),
+                                                        g_vfs_afp_volume_rename),
+                        FALSE);
+
+  simple = (GSimpleAsyncResult *)res;
+  
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+
+  return TRUE;
+}
+
+static void
+move_and_rename_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  GVfsAfpConnection *afp_conn = G_VFS_AFP_CONNECTION (source_object);
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+  GVfsAfpReply *reply;
+  GError *err = NULL;
+
+  AfpResultCode res_code;
+
+  reply = g_vfs_afp_connection_send_command_finish (afp_conn, res, &err);
+  if (!reply)
+  {
+    g_simple_async_result_take_error (simple, err);
+    goto done;
+  }
+
+  res_code = g_vfs_afp_reply_get_result_code (reply);
+  g_object_unref (reply);
+  
+  if (res_code != AFP_RESULT_NO_ERROR)
+  {
+    switch (res_code)
+    {
+      case AFP_RESULT_ACCESS_DENIED:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+                                         _("Permission denied"));
+        break;
+      case AFP_RESULT_CANT_MOVE:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_WOULD_RECURSE,
+                                         _("Can't move directory into one of its descendants"));
+        break;
+      case AFP_RESULT_INSIDE_SHARE_ERR:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_FAILED,
+                                         _("Can't move sharepoint into a shared directory"));
+        break;
+      case AFP_RESULT_INSIDE_TRASH_ERR:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_FAILED,
+                                         _("Can't move a shared directory into the Trash"));
+        break;
+      case AFP_RESULT_OBJECT_EXISTS:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_EXISTS,
+                                         _("Target file already exists"));
+        break;
+      case AFP_RESULT_OBJECT_LOCKED:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                                         _("Object being moved is marked as not renameable (RenameInhibit)"));
+        break;
+      case AFP_RESULT_OBJECT_NOT_FOUND:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                                         _("Object being moved doesn't exist"));
+        break;
+      default:
+        g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
+        break;
+    }
+  }
+
+done:
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+/*
+ * g_vfs_afp_volume_move_and_rename:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @source: the source path of the file to move.
+ * @destination: destination path.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: callback to call when the request is satisfied.
+ * @user_data: the data to pass to callback function.
+ * 
+ * Asynchronously moves (and renames) the file at @source to @destination.
+ */
+void
+g_vfs_afp_volume_move_and_rename (GVfsAfpVolume      *volume,
+                                  const char         *source,
+                                  const char         *destination,
+                                  GCancellable       *cancellable,
+                                  GAsyncReadyCallback callback,
+                                  gpointer            user_data)
+{
+  GVfsAfpVolumePrivate *priv;
+  GVfsAfpCommand *comm;
+  char *dirname, *basename;
+  GSimpleAsyncResult *simple;
+
+  g_return_if_fail (G_VFS_IS_AFP_VOLUME (volume));
+
+  priv = volume->priv;
+  
+  comm = g_vfs_afp_command_new (AFP_COMMAND_MOVE_AND_RENAME);
+  /* pad byte */
+  g_vfs_afp_command_put_byte (comm, 0);
+
+  /* VolumeID */
+  g_vfs_afp_command_put_uint16 (comm, g_vfs_afp_volume_get_id (volume));
+
+  /* SourceDirectoryID 2 == / */
+  g_vfs_afp_command_put_uint32 (comm, 2);
+  /* DestDirectoryID 2 == / */
+  g_vfs_afp_command_put_uint32 (comm, 2);
+
+  /* SourcePathname */
+  g_vfs_afp_command_put_pathname (comm, source);
+
+  /* DestPathname */
+  dirname = g_path_get_dirname (destination);
+  g_vfs_afp_command_put_pathname (comm, dirname);
+  g_free (dirname);
+
+  /* NewName */
+  basename = g_path_get_basename (destination);
+  g_vfs_afp_command_put_pathname (comm, basename);
+  g_free (basename);
+
+  simple = g_simple_async_result_new (G_OBJECT (volume), callback,
+                                      user_data, g_vfs_afp_volume_move_and_rename);
+  
+  g_vfs_afp_connection_send_command (priv->server->conn, comm, NULL,
+                                     move_and_rename_cb, cancellable, simple);
+  g_object_unref (comm);
+}
+
+/*
+ * g_vfs_afp_volume_move_and_rename_finish:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @result: a #GAsyncResult.
+ * @error: a #GError, %NULL to ignore.
+ * 
+ * Finalizes the asynchronous operation started by
+ * g_vfs_afp_volume_move_and_rename.
+ * 
+ * Returns: %TRUE on success, %FALSE on error.
+ */
+gboolean
+g_vfs_afp_volume_move_and_rename_finish (GVfsAfpVolume  *volume,
+                                         GAsyncResult   *res,
+                                         GError        **error)
+{
+  GSimpleAsyncResult *simple;
+
+  g_return_val_if_fail (g_simple_async_result_is_valid (res, G_OBJECT (volume),
+                                                        g_vfs_afp_volume_move_and_rename),
+                        FALSE);
+
+  simple = (GSimpleAsyncResult *)res;
+  
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+
+  return TRUE;
+}
+
+static void
+copy_file_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  GVfsAfpConnection *afp_conn = G_VFS_AFP_CONNECTION (source_object);
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+  GVfsAfpReply *reply;
+  GError *err = NULL;
+
+  AfpResultCode res_code;
+
+  reply = g_vfs_afp_connection_send_command_finish (afp_conn, res, &err);
+  if (!reply)
+  {
+    g_simple_async_result_take_error (simple, err);
+    goto done;
+  }
+
+  res_code = g_vfs_afp_reply_get_result_code (reply);
+  g_object_unref (reply);
+
+  if (res_code != AFP_RESULT_NO_ERROR)
+  {
+    switch (res_code)
+    {
+      case AFP_RESULT_ACCESS_DENIED:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+                                         _("Permission denied"));
+        break;
+      case AFP_RESULT_CALL_NOT_SUPPORTED:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                                         _("Server doesn't support the FPCopyFile operation"));
+        break;
+      case AFP_RESULT_DENY_CONFLICT:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_FAILED,
+                                         _("Unable to open source file for reading"));
+        break;
+      case AFP_RESULT_DISK_FULL:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NO_SPACE,
+                                         _("Not enough space on volume"));
+        break;
+      case AFP_RESULT_OBJECT_EXISTS:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_EXISTS,
+                                         _("Target file already exists"));
+        break;
+      case AFP_RESULT_OBJECT_NOT_FOUND:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                                         _("Source file and/or destination directory doesn't exist"));
+        break;
+      case AFP_RESULT_OBJECT_TYPE_ERR:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
+                                         _("Source file is a directory"));
+        break;
+      default:
+        g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
+        break;
+    }
+  }
+        
+done:
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+/*
+ * g_vfs_afp_volume_copy_file:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @source: the source path of the file to copy.
+ * @destination: destination path.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: callback to call when the request is satisfied.
+ * @user_data: the data to pass to callback function.
+ * 
+ * Asynchronously copies the file at @source to @destination.
+ */
+void
+g_vfs_afp_volume_copy_file (GVfsAfpVolume      *volume,
+                            const char         *source,
+                            const char         *destination,
+                            GCancellable       *cancellable,
+                            GAsyncReadyCallback callback,
+                            gpointer            user_data)
+{
+  GVfsAfpVolumePrivate *priv;
+  
+  GVfsAfpCommand *comm;
+  char *dirname, *basename;
+  GSimpleAsyncResult *simple;
+
+  g_return_if_fail (G_VFS_IS_AFP_VOLUME (volume));
+
+  priv = volume->priv;
+  
+  comm = g_vfs_afp_command_new (AFP_COMMAND_COPY_FILE);
+  /* pad byte */
+  g_vfs_afp_command_put_byte (comm, 0);
+
+  /* SourceVolumeID */
+  g_vfs_afp_command_put_uint16 (comm, g_vfs_afp_volume_get_id (volume));
+  /* SourceDirectoryID 2 == / */
+  g_vfs_afp_command_put_uint32 (comm, 2);
+
+  /* DestVolumeID */
+  g_vfs_afp_command_put_uint16 (comm, g_vfs_afp_volume_get_id (volume));
+  /* DestDirectoryID 2 == / */
+  g_vfs_afp_command_put_uint32 (comm, 2);
+
+  /* SourcePathname */
+  g_vfs_afp_command_put_pathname (comm, source);
+
+  /* DestPathname */
+  dirname = g_path_get_dirname (destination);
+  g_vfs_afp_command_put_pathname (comm, dirname);
+  g_free (dirname);
+
+  /* NewName */
+  basename = g_path_get_basename (destination);
+  g_vfs_afp_command_put_pathname (comm, basename);
+  g_free (basename);
+
+  simple = g_simple_async_result_new (G_OBJECT (volume), callback,
+                                      user_data, g_vfs_afp_volume_copy_file);
+
+  g_vfs_afp_connection_send_command (priv->server->conn, comm, NULL,
+                                     copy_file_cb, cancellable, simple);
+  g_object_unref (comm);
+}
+
+/*
+ * g_vfs_afp_volume_copy_file_finish:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @result: a #GAsyncResult.
+ * @error: a #GError, %NULL to ignore.
+ * 
+ * Finalizes the asynchronous operation started by
+ * g_vfs_afp_volume_copy_file.
+ * 
+ * Returns: %TRUE on success, %FALSE on error.
+ */
+gboolean
+g_vfs_afp_volume_copy_file_finish (GVfsAfpVolume *volume,
+                                   GAsyncResult  *res,
+                                   GError       **error)
+{
+  GSimpleAsyncResult *simple;
+
+  g_return_val_if_fail (g_simple_async_result_is_valid (res, G_OBJECT (volume),
+                                                        g_vfs_afp_volume_copy_file),
+                        FALSE);
+
+  simple = (GSimpleAsyncResult *)res;
+  
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+
+  return TRUE;
+}
+
+typedef struct
+{
+  AfpMapIDFunction function;
+  char *name;
+} MapIDData;
+
+static void
+map_id_data_free (MapIDData *mid)
+{
+  g_free (mid->name);
+
+  g_slice_free (MapIDData, mid);
+}
+
+static void
+map_id_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  GVfsAfpConnection *afp_conn = G_VFS_AFP_CONNECTION (source_object);
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+  GVfsAfpReply *reply;
+  GError *err = NULL;
+
+  AfpResultCode res_code;
+  MapIDData *map_data;
+
+  reply = g_vfs_afp_connection_send_command_finish (afp_conn, res, &err);
+  if (!reply)
+  {
+    g_simple_async_result_take_error (simple, err);
+    g_simple_async_result_complete (simple);
+    return;
+  }
+
+  res_code = g_vfs_afp_reply_get_result_code (reply);
+  if (res_code != AFP_RESULT_NO_ERROR)
+  {
+    switch (res_code)
+    {
+      case AFP_RESULT_ITEM_NOT_FOUND:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_FAILED,
+                                         _("ID not found"));
+        break;
+      default:
+        g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
+        break;
+    }
+
+    g_simple_async_result_complete (simple);
+    return;
+  }
+
+  map_data = g_simple_async_result_get_op_res_gpointer (simple);
+  
+  if (map_data->function == AFP_MAP_ID_FUNCTION_USER_UUID_TO_UTF8_NAME ||
+      map_data->function == AFP_MAP_ID_FUNCTION_GROUP_UUID_TO_UTF8_NAME)
+  {
+    /* objType */
+    g_vfs_afp_reply_read_uint32 (reply, NULL);
+    /* id */
+    g_vfs_afp_reply_read_uint32 (reply, NULL);
+  }
+
+  if (map_data->function == AFP_MAP_ID_FUNCTION_USER_ID_TO_NAME ||
+      map_data->function == AFP_MAP_ID_FUNCTION_GROUP_ID_TO_NAME)
+  {
+    g_vfs_afp_reply_read_pascal (reply, &map_data->name);
+  }
+  else
+  {
+    GVfsAfpName *afp_name;
+
+    g_vfs_afp_reply_read_afp_name (reply, FALSE, &afp_name);
+    map_data->name = g_vfs_afp_name_get_string (afp_name);
+    g_vfs_afp_name_unref (afp_name);
+  }
+
+  g_simple_async_result_complete (simple);
+}
+
+/*
+ * g_vfs_afp_volume_map_id:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @map_function: a #AfpMapIDFunction.
+ * @id: the id to be mapped to a name.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: callback to call when the request is satisfied.
+ * @user_data: the data to pass to callback function.
+ * 
+ * Asynchronously maps a user id, group id or uuid to a name.
+ */
+void
+g_vfs_afp_volume_map_id (GVfsAfpVolume       *volume,
+                         AfpMapIDFunction     map_function,
+                         gint64               id,
+                         GCancellable        *cancellable,
+                         GAsyncReadyCallback  callback,
+                         gpointer             user_data)
+{
+  GVfsAfpVolumePrivate *priv;
+  GVfsAfpCommand *comm;
+  GSimpleAsyncResult *simple;
+  MapIDData *map_data;
+
+  g_return_if_fail (G_VFS_IS_AFP_VOLUME (volume));
+
+  priv = volume->priv;
+  
+  comm = g_vfs_afp_command_new (AFP_COMMAND_MAP_ID);
+
+  /* SubFunction*/
+  g_vfs_afp_command_put_byte (comm, map_function);
+
+  /* ID */
+  if (map_function == AFP_MAP_ID_FUNCTION_USER_ID_TO_NAME ||
+      map_function == AFP_MAP_ID_FUNCTION_GROUP_ID_TO_NAME)
+    g_vfs_afp_command_put_int32 (comm, id);
+  else
+    g_vfs_afp_command_put_int64 (comm, id);
+
+  simple = g_simple_async_result_new (G_OBJECT (volume), callback,
+                                      user_data, g_vfs_afp_volume_map_id);
+
+  map_data = g_slice_new0 (MapIDData);
+  map_data->function = map_function;
+  g_simple_async_result_set_op_res_gpointer (simple, map_data,
+                                             (GDestroyNotify)map_id_data_free);
+  
+  g_vfs_afp_connection_send_command (priv->server->conn, comm, NULL,
+                                     map_id_cb, cancellable, simple);
+  g_object_unref (comm);
+}
+
+/*
+ * g_vfs_afp_volume_map_id_finish:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @result: a #GAsyncResult.
+ * @map_function: (out) optional out parameter to get the #AfpMapIDFunction
+ * which was used, %NULL to ignore.
+ * @error: a #GError, %NULL to ignore.
+ * 
+ * Finalizes the asynchronous operation started by
+ * g_vfs_afp_volume_map_id.
+ * 
+ * Returns: (transfer full): A string with the name of the id or %NULL
+ * on error.
+ */
+char *
+g_vfs_afp_volume_map_id_finish (GVfsAfpVolume   *volume,
+                                GAsyncResult     *res,
+                                AfpMapIDFunction *map_function,
+                                GError          **error)
+{
+  GSimpleAsyncResult *simple;
+  MapIDData *map_data;
+
+  g_return_val_if_fail (g_simple_async_result_is_valid (res, G_OBJECT (volume),
+                                                        g_vfs_afp_volume_map_id),
+                        NULL);
+
+  simple = (GSimpleAsyncResult *)res;
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return NULL;
+
+  map_data = g_simple_async_result_get_op_res_gpointer (simple);
+
+  if (map_function)
+    *map_function = map_data->function;
+  
+  return g_strdup (map_data->name);
+}
+
+static void
+get_filedir_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  GVfsAfpConnection *afp_conn = G_VFS_AFP_CONNECTION (source_object);
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
+
+  GVfsAfpReply *reply;
+  GError *err = NULL;
+  AfpResultCode res_code;
+
+  guint16 file_bitmap, dir_bitmap, bitmap;
+  guint8 FileDir;
+  gboolean directory;
+  GFileInfo *info;
+
+  reply = g_vfs_afp_connection_send_command_finish (afp_conn, res, &err);
+  if (!reply)
+  {
+    g_simple_async_result_take_error (simple, err);
+    goto done;
+  }
+
+  res_code = g_vfs_afp_reply_get_result_code (reply);
+  if (res_code != AFP_RESULT_NO_ERROR)
+  {
+    g_object_unref (reply);
+    
+    switch (res_code)
+    {
+      case AFP_RESULT_OBJECT_NOT_FOUND:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                                         _("File doesn't exist"));
+        break;
+      default:
+        g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
+        break;
+    }
+    goto done;
+  }
+
+  g_vfs_afp_reply_read_uint16 (reply, &file_bitmap);
+  g_vfs_afp_reply_read_uint16 (reply, &dir_bitmap);
+
+  g_vfs_afp_reply_read_byte (reply, &FileDir);
+  /* Pad Byte */
+  g_vfs_afp_reply_read_byte (reply, NULL);
+  
+  directory = (FileDir & 0x80); 
+  bitmap =  directory ? dir_bitmap : file_bitmap;
+
+  info = g_file_info_new ();
+  g_vfs_afp_server_fill_info (volume->priv->server, info, reply, directory, bitmap);
+  
+  g_object_unref (reply);
+
+  g_simple_async_result_set_op_res_gpointer (simple, info, g_object_unref);
+
+done:
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+/*
+ * g_vfs_afp_volume_get_filedir_parms:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @filename: file or directory whose parameters should be retreived.
+ * @file_bitmap: bitmap describing the parameters to retrieve if @filename is a
+ * file.
+ * @dir_bitmap: bitmap describing the parameters to retrieve if @filename is a
+ * directory.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: callback to call when the request is satisfied.
+ * @user_data: the data to pass to callback function.
+ * 
+ * Asynchronously retrieves the parameters described by @file_bitmap or
+ * @dir_bitmap of the file/directory at @filename.
+ */
+void
+g_vfs_afp_volume_get_filedir_parms (GVfsAfpVolume       *volume,
+                                    const char          *filename,
+                                    guint16              file_bitmap,
+                                    guint16              dir_bitmap,
+                                    GCancellable        *cancellable,
+                                    GAsyncReadyCallback  callback,
+                                    gpointer             user_data)
+{
+  GVfsAfpVolumePrivate *priv;
+  GVfsAfpCommand *comm;
+  GSimpleAsyncResult *simple;
+
+  g_return_if_fail (G_VFS_IS_AFP_VOLUME (volume));
+
+  priv = volume->priv;
+  
+  comm = g_vfs_afp_command_new (AFP_COMMAND_GET_FILE_DIR_PARMS);
+  /* pad byte */
+  g_vfs_afp_command_put_byte (comm, 0);
+  /* VolumeID */
+  g_vfs_afp_command_put_uint16 (comm, g_vfs_afp_volume_get_id (volume));
+  /* Directory ID 2 == / */
+  g_vfs_afp_command_put_uint32 (comm, 2);
+  /* FileBitmap */  
+  g_vfs_afp_command_put_uint16 (comm, file_bitmap);
+  /* DirectoryBitmap */  
+  g_vfs_afp_command_put_uint16 (comm, dir_bitmap);
+  /* PathName */
+  g_vfs_afp_command_put_pathname (comm, filename);
+
+  simple = g_simple_async_result_new (G_OBJECT (volume), callback, user_data,
+                                      g_vfs_afp_volume_get_filedir_parms);
+                                      
+
+  g_vfs_afp_connection_send_command (priv->server->conn, comm, NULL,
+                                     get_filedir_parms_cb, cancellable,
+                                     simple);
+  g_object_unref (comm);
+}
+
+/*
+ * g_vfs_afp_volume_get_filedir_parms_finish:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @result: a #GAsyncResult.
+ * @error: a #GError, %NULL to ignore.
+ * 
+ * Finalizes the asynchronous operation started by
+ * g_vfs_afp_volume_get_fork_parms.
+ * 
+ * Returns: (transfer full): A #GFileInfo with the requested parameters or %NULL
+ * on error.
+ */
+GFileInfo *
+g_vfs_afp_volume_get_filedir_parms_finish (GVfsAfpVolume  *volume,
+                                           GAsyncResult   *result,
+                                           GError         **error)
+{
+  GSimpleAsyncResult *simple;
+  
+  g_return_val_if_fail (g_simple_async_result_is_valid (result,
+                                                        G_OBJECT (volume),
+                                                        g_vfs_afp_volume_get_filedir_parms),
+                        NULL);
+
+  simple = (GSimpleAsyncResult *)result;
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return NULL;
+
+  return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
+}
+
+static void
+get_fork_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  GVfsAfpConnection *afp_conn = G_VFS_AFP_CONNECTION (source_object);
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
+  GVfsAfpVolumePrivate *priv = volume->priv;
+
+  GVfsAfpReply *reply;
+  GError *err = NULL;
+  AfpResultCode res_code;
+
+  guint16 file_bitmap;
+  GFileInfo *info;
+
+  reply = g_vfs_afp_connection_send_command_finish (afp_conn, res, &err);
+  if (!reply)
+  {
+    g_simple_async_result_take_error (simple, err);
+    goto done;
+  }
+
+  res_code = g_vfs_afp_reply_get_result_code (reply);
+  if (res_code != AFP_RESULT_NO_ERROR)
+  {
+    g_object_unref (reply);
+
+    g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
+    goto done;
+  }
+
+  g_vfs_afp_reply_read_uint16 (reply, &file_bitmap);
+
+  info = g_file_info_new ();
+  g_vfs_afp_server_fill_info (priv->server, info, reply, FALSE, file_bitmap);
+
+  g_object_unref (reply);
+
+  g_simple_async_result_set_op_res_gpointer (simple, info, g_object_unref);
+
+done:
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+/*
+ * g_vfs_afp_volume_get_fork_parms:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @fork_refnume: the reference id of the fork.
+ * @file_bitmap: bitmap describing the parameters to retrieve.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: callback to call when the request is satisfied.
+ * @user_data: the data to pass to callback function.
+ * 
+ * Asynchronously retrieves the parameters described by @file_bitmap of the fork
+ * with reference id @fork_refnum.
+ */
+void
+g_vfs_afp_volume_get_fork_parms (GVfsAfpVolume       *volume,
+                                 gint16               fork_refnum,
+                                 guint16              file_bitmap,
+                                 GCancellable        *cancellable,
+                                 GAsyncReadyCallback  callback,
+                                 gpointer             user_data)
+{
+  GVfsAfpVolumePrivate *priv;
+  GVfsAfpCommand *comm;
+  GSimpleAsyncResult *simple;
+
+  g_return_if_fail (G_VFS_IS_AFP_VOLUME (volume));
+
+  priv = volume->priv;
+  
+  comm = g_vfs_afp_command_new (AFP_COMMAND_GET_FORK_PARMS);
+  /* pad byte */
+  g_vfs_afp_command_put_byte (comm, 0);
+  /* OForkRefNum */
+  g_vfs_afp_command_put_int16 (comm, fork_refnum);
+  /* Bitmap */  
+  g_vfs_afp_command_put_uint16 (comm, file_bitmap);
+
+  simple = g_simple_async_result_new (G_OBJECT (volume), callback, user_data,
+                                      g_vfs_afp_volume_get_fork_parms);
+                                      
+
+  g_vfs_afp_connection_send_command (priv->server->conn, comm, NULL,
+                                     get_fork_parms_cb, cancellable,
+                                     simple);
+  g_object_unref (comm);
+}
+
+/*
+ * g_vfs_afp_volume_get_fork_parms_finish:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @result: a #GAsyncResult.
+ * @error: a #GError, %NULL to ignore.
+ * 
+ * Finalizes the asynchronous operation started by
+ * g_vfs_afp_volume_get_fork_parms.
+ * 
+ * Returns: (transfer full): A #GFileInfo with the requested parameters or %NULL
+ * on error.
+ */
+GFileInfo *
+g_vfs_afp_volume_get_fork_parms_finish (GVfsAfpVolume  *volume,
+                                        GAsyncResult   *result,
+                                        GError         **error)
+{
+  GSimpleAsyncResult *simple;
+  
+  g_return_val_if_fail (g_simple_async_result_is_valid (result,
+                                                        G_OBJECT (volume),
+                                                        g_vfs_afp_volume_get_fork_parms),
+                        NULL);
+
+  simple = (GSimpleAsyncResult *)result;
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return NULL;
+
+  return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
+}
+
+static void
+set_fork_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  GVfsAfpConnection *conn = G_VFS_AFP_CONNECTION (source_object);
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+  GVfsAfpReply *reply;
+  GError *err = NULL;
+  AfpResultCode res_code;
+
+  reply = g_vfs_afp_connection_send_command_finish (conn, res, &err);
+  if (!reply)
+  {
+    g_simple_async_result_take_error (simple, err);
+    goto done;
+  }
+
+  res_code = g_vfs_afp_reply_get_result_code (reply);
+  g_object_unref (reply);
+  if (res_code != AFP_RESULT_NO_ERROR)
+  {
+    switch (res_code)
+    {
+      case AFP_RESULT_ACCESS_DENIED:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_FAILED,
+                                         _("Permission denied"));
+        break;
+      case AFP_RESULT_DISK_FULL:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NO_SPACE,
+                                         _("Not enough space on volume"));
+        break;
+      case AFP_RESULT_LOCK_ERR:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_FAILED,
+                                         _("Range lock conflict exists"));
+        break;
+      default:
+        g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
+        break;
+    }
+    goto done;
+  }
+
+done:
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+/*
+ * g_vfs_afp_volume_set_fork_size:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @fork_refnume: the reference id of the fork.
+ * @size: the new size of the fork,
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: callback to call when the request is satisfied.
+ * @user_data: the data to pass to callback function.
+ * 
+ * Asynchronously sets the size of the fork referenced by @fork_refnum.
+ */
+void
+g_vfs_afp_volume_set_fork_size (GVfsAfpVolume       *volume,
+                                gint16               fork_refnum,
+                                gint64               size,
+                                GCancellable        *cancellable,
+                                GAsyncReadyCallback  callback,
+                                gpointer             user_data)
+{
+  GVfsAfpVolumePrivate *priv;
+  GVfsAfpCommand *comm;
+  GSimpleAsyncResult *simple;
+
+  g_return_if_fail (G_VFS_IS_AFP_VOLUME (volume));
+
+  priv = volume->priv;
+  
+  comm = g_vfs_afp_command_new (AFP_COMMAND_SET_FORK_PARMS);
+  /* pad byte */
+  g_vfs_afp_command_put_byte (comm, 0);
+
+  /* OForkRefNum */
+  g_vfs_afp_command_put_int16 (comm, fork_refnum);
+  /* Bitmap */
+  g_vfs_afp_command_put_uint16 (comm, AFP_FILE_BITMAP_EXT_DATA_FORK_LEN_BIT);
+  /* ForkLen */
+  g_vfs_afp_command_put_int64 (comm, size);
+
+  simple = g_simple_async_result_new (G_OBJECT (volume), callback, user_data,
+                                      g_vfs_afp_volume_set_fork_size);
+  
+  g_vfs_afp_connection_send_command (priv->server->conn, comm, NULL,
+                                     set_fork_parms_cb, cancellable, simple);
+  g_object_unref (comm);
+}
+
+/*
+ * g_vfs_afp_volume_set_fork_size_finish:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @result: a #GAsyncResult.
+ * @error: a #GError, %NULL to ignore.
+ * 
+ * Finalizes the asynchronous operation started by
+ * g_vfs_afp_volume_set_fork_size.
+ * 
+ * Returns: (transfer full): %TRUE on success, %FALSE otherwise.
+ */
+gboolean
+g_vfs_afp_volume_set_fork_size_finish (GVfsAfpVolume  *volume,
+                                       GAsyncResult   *result,
+                                       GError         **error)
+{
+  GSimpleAsyncResult *simple;
+  
+  g_return_val_if_fail (g_simple_async_result_is_valid (result,
+                                                        G_OBJECT (volume),
+                                                        g_vfs_afp_volume_set_fork_size),
+                        FALSE);
+
+  simple = (GSimpleAsyncResult *)result;
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+
+  return TRUE;
+}
+
+static void
+set_unix_privs_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  GVfsAfpConnection *afp_conn = G_VFS_AFP_CONNECTION (source_object);
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+  GVfsAfpReply *reply;
+  GError *err = NULL;
+  AfpResultCode res_code;
+
+  reply = g_vfs_afp_connection_send_command_finish (afp_conn, res, &err);
+  if (!reply)
+  {
+    g_simple_async_result_take_error (simple, err);
+    goto done;
+  }
+
+  res_code = g_vfs_afp_reply_get_result_code (reply);
+  g_object_unref (reply);
+  if (res_code != AFP_RESULT_NO_ERROR)
+  {
+    switch (res_code)
+    {
+      case AFP_RESULT_ACCESS_DENIED:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+                                         _("Permission denied"));
+        break;
+      case AFP_RESULT_OBJECT_NOT_FOUND:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                                         _("Target object doesn't exist"));
+        break;
+      case AFP_RESULT_VOL_LOCKED:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+                                         _("Volume is read-only"));
+        break;
+      default:
+        g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
+        break;
+    }
+    goto done;
+  }
+
+done:
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+/*
+ * g_vfs_afp_volume_set_unix_privs:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @filename: file or directory whose unix privileges should be set.
+ * @uid: the new user id of the file.
+ * @gid: the new group id of the file.
+ * @permissions: the new unix permissions of the file.
+ * @ua_permissions: the new AFP access right of the file.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: callback to call when the request is satisfied.
+ * @user_data: the data to pass to callback function.
+ * 
+ * Asynchronously sets new unix permissions on the file/directory pointed to by
+ * @filename.
+ */
+void
+g_vfs_afp_volume_set_unix_privs (GVfsAfpVolume       *volume,
+                                 const char          *filename,
+                                 guint32              uid,
+                                 guint32              gid,
+                                 guint32              permissions,
+                                 guint32              ua_permissions,
+                                 GCancellable        *cancellable,
+                                 GAsyncReadyCallback  callback,
+                                 gpointer             user_data)
+{
+  GVfsAfpVolumePrivate *priv;
+  GVfsAfpCommand *comm;
+  GSimpleAsyncResult *simple;
+
+  g_return_if_fail (G_VFS_IS_AFP_VOLUME (volume));
+
+  priv = volume->priv;
+  
+  comm = g_vfs_afp_command_new (AFP_COMMAND_SET_FILEDIR_PARMS);
+  /* pad byte */
+  g_vfs_afp_command_put_byte (comm, 0);
+
+  /* VolumeID */
+  g_vfs_afp_command_put_uint16 (comm, g_vfs_afp_volume_get_id (volume));
+  /* DirectoryID 2 == / */
+  g_vfs_afp_command_put_uint32 (comm, 2);
+  /* Bitmap */
+  g_vfs_afp_command_put_uint16 (comm, AFP_FILEDIR_BITMAP_UNIX_PRIVS_BIT);
+  /* Pathname */
+  g_vfs_afp_command_put_pathname (comm, filename);
+  /* pad to even */
+  g_vfs_afp_command_pad_to_even (comm);
+
+  /* UID */
+  g_vfs_afp_command_put_uint32 (comm, uid);
+  /* GID */
+  g_vfs_afp_command_put_uint32 (comm, gid);
+  /* Permissions */
+  g_vfs_afp_command_put_uint32 (comm, permissions);
+  /* UAPermissions */
+  g_vfs_afp_command_put_uint32 (comm, ua_permissions);
+
+  simple = g_simple_async_result_new (G_OBJECT (volume), callback,
+                                      user_data, g_vfs_afp_volume_set_unix_privs);
+
+  g_vfs_afp_connection_send_command (priv->server->conn, comm, NULL,
+                                     set_unix_privs_cb, cancellable, simple);
+  g_object_unref (comm);
+}
+
+/*
+ * g_vfs_afp_volume_set_unix_privs_finish:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @result: a #GAsyncResult.
+ * @error: a #GError, %NULL to ignore.
+ * 
+ * Finalizes the asynchronous operation started by
+ * g_vfs_afp_volume_set_unix_privs.
+ * 
+ * Returns: %TRUE on success, %FALSE on error.
+ */
+gboolean
+g_vfs_afp_volume_set_unix_privs_finish (GVfsAfpVolume  *volume,
+                                        GAsyncResult   *res,
+                                        GError        **error)
+{
+  GSimpleAsyncResult *simple;
+  
+  g_return_val_if_fail (g_simple_async_result_is_valid (res, G_OBJECT (volume),
+                                                        g_vfs_afp_volume_set_unix_privs),
+                        FALSE);
+
+  simple = (GSimpleAsyncResult *)res;
+  
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+
+  return TRUE;
+}
+
+static const gint16 ENUMERATE_REQ_COUNT           = G_MAXINT16;
+static const gint16 ENUMERATE_EXT_MAX_REPLY_SIZE  = G_MAXINT16; 
+static const gint32 ENUMERATE_EXT2_MAX_REPLY_SIZE = G_MAXINT32;
+
+static void
+enumerate_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  GVfsAfpConnection *conn = G_VFS_AFP_CONNECTION (source_object);
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+  
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
+  GVfsAfpVolumePrivate *priv = volume->priv;
+
+  GVfsAfpReply *reply;
+  GError *err = NULL;
+  AfpResultCode res_code;
+  
+  guint16 file_bitmap;
+  guint16  dir_bitmap;
+  gint16 count, i;
+  GPtrArray *infos;
+
+  reply = g_vfs_afp_connection_send_command_finish (conn, res, &err);
+  if (!reply)
+  {
+    g_simple_async_result_take_error (simple, err);
+    goto done;
+  }
+
+  res_code = g_vfs_afp_reply_get_result_code (reply);
+  if (res_code != AFP_RESULT_NO_ERROR)
+  {
+    g_object_unref (reply);
+    
+    switch (res_code)
+    {
+      case AFP_RESULT_OBJECT_NOT_FOUND:
+        g_simple_async_result_set_op_res_gpointer (simple, NULL, NULL);
+        break;
+        
+      case AFP_RESULT_ACCESS_DENIED:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
+                                         _("Permission denied"));
+        break;
+      case AFP_RESULT_DIR_NOT_FOUND:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                                         _("Directory doesn't exist"));
+        break;
+      case AFP_RESULT_OBJECT_TYPE_ERR:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY,
+                                         _("Target object is not a directory"));
+        break;
+      default:
+        g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
+        break;
+    }
+    goto done;
+  }
+
+  g_vfs_afp_reply_read_uint16 (reply, &file_bitmap);
+  g_vfs_afp_reply_read_uint16 (reply, &dir_bitmap);
+
+  g_vfs_afp_reply_read_int16 (reply, &count);
+  infos = g_ptr_array_new_full (count, g_object_unref);
+  
+  for (i = 0; i < count; i++)
+  {
+    goffset start_pos;
+    guint16 struct_length;
+    guint8 FileDir;
+    
+    gboolean directory;
+    guint16 bitmap;
+    GFileInfo *info;
+
+    start_pos = g_vfs_afp_reply_get_pos (reply);
+    
+    g_vfs_afp_reply_read_uint16 (reply, &struct_length);
+    g_vfs_afp_reply_read_byte (reply, &FileDir);
+    /* pad byte */
+    g_vfs_afp_reply_read_byte (reply, NULL);
+
+    directory = (FileDir & 0x80); 
+    bitmap =  directory ? dir_bitmap : file_bitmap;
+    
+    info = g_file_info_new ();
+    g_vfs_afp_server_fill_info (priv->server, info, reply, directory, bitmap);
+    g_ptr_array_add (infos, info);
+
+    g_vfs_afp_reply_seek (reply, start_pos + struct_length, G_SEEK_SET);
+  }
+  g_object_unref (reply);
+
+  g_simple_async_result_set_op_res_gpointer (simple, infos, NULL);
+  
+done:
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+/*
+ * g_vfs_afp_volume_enumerate:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: callback to call when the request is satisfied.
+ * @user_data: the data to pass to callback function.
+ * 
+ * Asynchronously enumerates the files in @directory starting at index
+ * @start_index.
+ */
+void
+g_vfs_afp_volume_enumerate (GVfsAfpVolume       *volume,
+                            const char          *directory,
+                            gint64               start_index,
+                            guint16              file_bitmap,
+                            guint16              dir_bitmap,
+                            GCancellable        *cancellable,
+                            GAsyncReadyCallback  callback,
+                            gpointer             user_data)
+{
+  GVfsAfpVolumePrivate *priv;
+  gint32 max;
+  
+  GVfsAfpCommand *comm;
+  GSimpleAsyncResult *simple;
+
+  g_return_if_fail (G_VFS_IS_AFP_VOLUME (volume));
+
+  priv = volume->priv;
+
+  simple = g_simple_async_result_new (G_OBJECT (volume), callback,
+                                      user_data, g_vfs_afp_volume_enumerate);
+  
+  max = (priv->server->version >= AFP_VERSION_3_1) ? G_MAXINT32 : G_MAXINT16;
+  /* Can't enumerate any more files */
+  if (start_index > max)
+  {
+    g_simple_async_result_set_op_res_gpointer (simple, NULL, NULL);
+    g_simple_async_result_complete_in_idle (simple);
+    return;
+  }
+  
+  if (priv->server->version >= AFP_VERSION_3_1)
+    comm = g_vfs_afp_command_new (AFP_COMMAND_ENUMERATE_EXT2);
+  else
+    comm = g_vfs_afp_command_new (AFP_COMMAND_ENUMERATE_EXT);
+  
+  /* pad byte */
+  g_vfs_afp_command_put_byte (comm, 0);
+
+  /* Volume ID */
+  g_vfs_afp_command_put_uint16 (comm, g_vfs_afp_volume_get_id (volume));
+  /* Directory ID 2 == / */
+  g_vfs_afp_command_put_uint32 (comm, 2);
+
+  /* File Bitmap */
+  g_vfs_afp_command_put_uint16 (comm, file_bitmap);
+  
+  /* Dir Bitmap */
+  g_vfs_afp_command_put_uint16 (comm, dir_bitmap);
+
+  /* Req Count */
+  g_vfs_afp_command_put_int16 (comm, ENUMERATE_REQ_COUNT);
+
+  
+  /* StartIndex and MaxReplySize */
+  if (priv->server->version >= AFP_VERSION_3_1)
+  {
+    g_vfs_afp_command_put_int32 (comm, start_index);
+    g_vfs_afp_command_put_int32 (comm, ENUMERATE_EXT2_MAX_REPLY_SIZE);
+  }
+  else
+  {
+    g_vfs_afp_command_put_int16 (comm, start_index);
+    g_vfs_afp_command_put_int16 (comm, ENUMERATE_EXT_MAX_REPLY_SIZE);
+  }
+  
+  /* Pathname */
+  g_vfs_afp_command_put_pathname (comm, directory);
+  
+  g_vfs_afp_connection_send_command (priv->server->conn, comm, NULL,
+                                     enumerate_cb, cancellable, simple);
+  g_object_unref (comm);
+}
+
+/*
+ * g_vfs_afp_volume_enumerate_finish:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @result: a #GAsyncResult.
+ * @infos: (out) (element-type G.FileInfo): array of #GFileInfo objects or %NULL
+ * when no more files could be found.
+ * @error: a #GError, %NULL to ignore.
+ * 
+ * Finalizes the asynchronous operation started by
+ * g_vfs_afp_volume_enumerate.
+ * 
+ * Returns: %TRUE on success, %FALSE on error.
+ */
+gboolean
+g_vfs_afp_volume_enumerate_finish (GVfsAfpVolume  *volume,
+                                   GAsyncResult   *res,
+                                   GPtrArray      **infos,
+                                   GError        **error)
+{
+  GSimpleAsyncResult *simple;
+  
+  g_return_val_if_fail (g_simple_async_result_is_valid (res, G_OBJECT (volume),
+                                                        g_vfs_afp_volume_enumerate),
+                        FALSE);
+  g_return_val_if_fail (infos != NULL, FALSE);
+  
+  simple = (GSimpleAsyncResult *)res;
+  
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+
+  *infos = g_simple_async_result_get_op_res_gpointer (simple);
+  
+  return TRUE;
+}
+
+static void
+close_replace_exchange_files_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  GVfsAfpConnection *conn = G_VFS_AFP_CONNECTION (source_object);
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+  GVfsAfpReply *reply;
+  GError *err = NULL;
+  AfpResultCode res_code;
+
+  reply = g_vfs_afp_connection_send_command_finish (conn, res, &err);
+  if (!reply)
+  {
+    g_simple_async_result_take_error (simple, err);
+    goto done;
+  }
+  
+  res_code = g_vfs_afp_reply_get_result_code (reply);
+  g_object_unref (reply);
+  
+  if (res_code != AFP_RESULT_NO_ERROR)
+  {
+    switch (res_code)
+    {
+      case AFP_RESULT_ACCESS_DENIED:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_FAILED,
+                                         _("Permission denied"));
+        break;
+      case AFP_RESULT_ID_NOT_FOUND:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
+                                         _("File doesn't exist"));
+        break;
+      case AFP_RESULT_OBJECT_TYPE_ERR:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
+                                         _("File is directory"));
+        break;   
+      default:
+        g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
+        break;
+    }
+    goto done;
+  }
+
+done:
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+  
+/*
+ * g_vfs_afp_volume_exchange_files:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @source: path to source file to exchange.
+ * @destination: path to destination file to exchange.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: callback to call when the request is satisfied.
+ * @user_data: the data to pass to callback function.
+ * 
+ * Asynchronously exchanges the file system metadata of the two files @source
+ * and @destination.
+ */
+void
+g_vfs_afp_volume_exchange_files (GVfsAfpVolume       *volume,
+                                 const char          *source,
+                                 const char          *destination,
+                                 GCancellable        *cancellable,
+                                 GAsyncReadyCallback  callback,
+                                 gpointer             user_data)
+{
+  GVfsAfpVolumePrivate *priv;
+  GVfsAfpCommand *comm;
+  GSimpleAsyncResult *simple;
+
+  g_return_if_fail (G_VFS_IS_AFP_VOLUME (volume));
+
+  priv = volume->priv;
+  
+  comm = g_vfs_afp_command_new (AFP_COMMAND_EXCHANGE_FILES);
+  /* pad byte */
+  g_vfs_afp_command_put_byte (comm, 0);
+
+  /* Volume ID */
+  g_vfs_afp_command_put_uint16 (comm, g_vfs_afp_volume_get_id (volume));
+  /* SourceDirectory ID 2 == / */
+  g_vfs_afp_command_put_uint32 (comm, 2);
+  /* DestDirectory ID 2 == / */
+  g_vfs_afp_command_put_uint32 (comm, 2);
+
+  /* SourcePath */
+  g_vfs_afp_command_put_pathname (comm, source);
+  /* DestPath */
+  g_vfs_afp_command_put_pathname (comm, destination);
+
+  simple = g_simple_async_result_new (G_OBJECT (volume), callback, user_data,
+                                      g_vfs_afp_volume_exchange_files);
+  
+  g_vfs_afp_connection_send_command (priv->server->conn, comm, NULL,
+                                     close_replace_exchange_files_cb,
+                                     cancellable, simple);
+  g_object_unref (comm); 
+}
+
+/*
+ * g_vfs_afp_volume_exchange_files_finish:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @result: a #GAsyncResult.
+ * @error: a #GError, %NULL to ignore.
+ * 
+ * Finalizes the asynchronous operation started by
+ * g_vfs_afp_volume_exchange_files.
+ * 
+ * Returns: %TRUE on success, %FALSE on error.
+ */
+gboolean
+g_vfs_afp_volume_exchange_files_finish (GVfsAfpVolume  *volume,
+                                        GAsyncResult   *res,
+                                        GError        **error)
+{
+  GSimpleAsyncResult *simple;
+  
+  g_return_val_if_fail (g_simple_async_result_is_valid (res, G_OBJECT (volume),
+                                                        g_vfs_afp_volume_exchange_files),
+                        FALSE);
+  
+  simple = (GSimpleAsyncResult *)res;
+  
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+  
+  return TRUE;
+}
+
+static void
+write_ext_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  GVfsAfpConnection *conn = G_VFS_AFP_CONNECTION (source_object);
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+  GVfsAfpReply *reply;
+  GError *err = NULL;
+  AfpResultCode res_code;
+  gint64 *last_written;
+
+  reply = g_vfs_afp_connection_send_command_finish (conn, res, &err);
+  if (!reply)
+  {
+    g_simple_async_result_take_error (simple, err);
+    goto done;
+  }
+
+  res_code = g_vfs_afp_reply_get_result_code (reply);
+  if (!(res_code == AFP_RESULT_NO_ERROR || res_code == AFP_RESULT_LOCK_ERR))
+  {
+    g_object_unref (reply);
+
+    switch (res_code)
+    {
+      case AFP_RESULT_ACCESS_DENIED:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_FAILED,
+                                  _("File is not open for write access"));
+        break;
+      case AFP_RESULT_DISK_FULL:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NO_SPACE,
+                                  _("Not enough space on volume"));
+        break;
+      default:
+        g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
+        break;
+    }
+    goto done;
+  }
+
+  last_written = g_new (gint64, 1);
+  g_vfs_afp_reply_read_int64 (reply, last_written);
+  g_object_unref (reply);
+  
+  g_simple_async_result_set_op_res_gpointer (simple, last_written, g_free); 
+
+done:
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+/*
+ * g_vfs_afp_volume_write_to_fork:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @fork_refnume: reference id of the fork to write to.
+ * @buffer: buffer containing the data to write. Must be valid during the whole
+ * call.
+ * @buffer_size: size of @buffer.
+ * @offset: offset in file where the data should be written.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: callback to call when the request is satisfied.
+ * @user_data: the data to pass to callback function.
+ * 
+ * Asynchronously writes the data in @buffer to the fork referenced by
+ * @fork_refnum.
+ */
+void
+g_vfs_afp_volume_write_to_fork (GVfsAfpVolume       *volume,
+                                guint16              fork_refnum,
+                                char                *buffer,
+                                gsize                buffer_size,
+                                gint64               offset,
+                                GCancellable        *cancellable,
+                                GAsyncReadyCallback  callback,
+                                gpointer             user_data)
+{
+  GVfsAfpCommand *comm;
+  guint32 req_count;
+  GSimpleAsyncResult *simple;
+
+  g_return_if_fail (G_VFS_IS_AFP_VOLUME (volume));
+  
+  comm = g_vfs_afp_command_new (AFP_COMMAND_WRITE_EXT);
+  /* StartEndFlag = 0 */
+  g_vfs_afp_command_put_byte (comm, 0);
+
+  /* OForkRefNum */
+  g_vfs_afp_command_put_int16 (comm, fork_refnum);
+  /* Offset */
+  g_vfs_afp_command_put_int64 (comm, offset);
+  /* ReqCount */
+  req_count = MIN (buffer_size, G_MAXUINT32);
+  g_vfs_afp_command_put_int64 (comm, req_count);
+
+  g_vfs_afp_command_set_buffer (comm, buffer, buffer_size);
+
+  simple = g_simple_async_result_new (G_OBJECT (volume), callback, user_data,
+                                      g_vfs_afp_volume_write_to_fork);
+  
+  g_vfs_afp_connection_send_command (volume->priv->server->conn, comm, NULL,
+                                     write_ext_cb, cancellable, simple);
+  g_object_unref (comm);
+}
+
+/*
+ * g_vfs_afp_volume_write_to_fork_finish:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @result: a #GAsyncResult.
+ * @last_written: (out) (allow-none): offset of the last written byte.
+ * @error: a #GError, %NULL to ignore.
+ * 
+ * Finalizes the asynchronous operation started by
+ * g_vfs_afp_volume_write_to_fork.
+ * 
+ * Returns: %TRUE on success, %FALSE on error.
+ */
+gboolean
+g_vfs_afp_volume_write_to_fork_finish (GVfsAfpVolume  *volume,
+                                       GAsyncResult   *res,
+                                       gint64         *last_written,
+                                       GError        **error)
+{
+  GSimpleAsyncResult *simple;
+  
+  g_return_val_if_fail (g_simple_async_result_is_valid (res, G_OBJECT (volume),
+                                                        g_vfs_afp_volume_write_to_fork),
+                        FALSE);
+  
+  simple = (GSimpleAsyncResult *)res;
+  
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+
+  if (last_written)
+    *last_written = *((gint64 *)g_simple_async_result_get_op_res_gpointer (simple));
+  
+  return TRUE;
+}
+
+static void
+read_ext_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+  GVfsAfpConnection *conn = G_VFS_AFP_CONNECTION (source_object);
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+  GVfsAfpReply *reply;
+  GError *err = NULL;
+  AfpResultCode res_code;
+
+  reply = g_vfs_afp_connection_send_command_finish (conn, res, &err);
+  if (!reply)
+  {
+    g_simple_async_result_take_error (simple, err);
+    goto done;
+  }
+
+  res_code = g_vfs_afp_reply_get_result_code (reply);
+  if (!(res_code == AFP_RESULT_NO_ERROR || res_code == AFP_RESULT_LOCK_ERR ||
+        res_code == AFP_RESULT_EOF_ERR))
+  {
+    g_object_unref (reply);
+
+    switch (res_code)
+    {
+      case AFP_RESULT_ACCESS_DENIED:
+        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_FAILED,
+                                  _("File is not open for read access"));
+        break;
+      default:
+        g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
+        break;
+    }
+    goto done;
+  }
+
+  g_simple_async_result_set_op_res_gssize (simple, g_vfs_afp_reply_get_size (reply));
+  g_object_unref (reply);
+
+done:
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+/*
+ * g_vfs_afp_volume_read_from_fork:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @fork_refnume: reference id of the fork to write to.
+ * @buffer: buffer to read data into. Must be valid during the whole call. 
+ * @bytes_requested: number of bytes that should be read.
+ * @offset: offset in file from where the data should be read.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: callback to call when the request is satisfied.
+ * @user_data: the data to pass to callback function.
+ * 
+ * Asynchronously reads data from the fork referenced by @fork_refnum.
+ */
+void
+g_vfs_afp_volume_read_from_fork (GVfsAfpVolume       *volume,
+                                 guint16              fork_refnum,
+                                 char                *buffer,
+                                 gsize                bytes_requested,
+                                 gint64               offset,
+                                 GCancellable        *cancellable,
+                                 GAsyncReadyCallback  callback,
+                                 gpointer             user_data)
+{
+  GVfsAfpCommand *comm;
+  guint32 req_count;
+  GSimpleAsyncResult *simple;
+
+  g_return_if_fail (G_VFS_IS_AFP_VOLUME (volume));
+  
+  comm = g_vfs_afp_command_new (AFP_COMMAND_READ_EXT);
+  /* pad byte */
+  g_vfs_afp_command_put_byte (comm, 0);
+
+  /* OForkRefNum */
+  g_vfs_afp_command_put_int16 (comm, fork_refnum);
+  /* Offset */
+  g_vfs_afp_command_put_int64 (comm, offset);
+  /* ReqCount */
+  req_count = MIN (bytes_requested, G_MAXUINT32);
+  g_vfs_afp_command_put_int64 (comm, req_count);
+
+  simple = g_simple_async_result_new (G_OBJECT (volume), callback, user_data,
+                                      g_vfs_afp_volume_read_from_fork);
+  
+  g_vfs_afp_connection_send_command (volume->priv->server->conn, comm, buffer,
+                                     read_ext_cb, cancellable, simple);
+  g_object_unref (comm);
+}
+
+/*
+ * g_vfs_afp_volume_read_from_fork_finish:
+ * 
+ * @volume: a #GVfsAfpVolume.
+ * @result: a #GAsyncResult.
+ * @bytes_read: (out) (allow-none): the number of bytes that were read.
+ * @error: a #GError, %NULL to ignore.
+ * 
+ * Finalizes the asynchronous operation started by
+ * g_vfs_afp_volume_read_from_fork.
+ * 
+ * Returns: %TRUE on success, %FALSE on error.
+ */
+gboolean
+g_vfs_afp_volume_read_from_fork_finish (GVfsAfpVolume  *volume,
+                                        GAsyncResult   *res,
+                                        gsize          *bytes_read,
+                                        GError        **error)
+{
+  GSimpleAsyncResult *simple;
+  
+  g_return_val_if_fail (g_simple_async_result_is_valid (res, G_OBJECT (volume),
+                                                        g_vfs_afp_volume_read_from_fork),
+                        FALSE);
+  
+  simple = (GSimpleAsyncResult *)res;
+  
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+
+  if (bytes_read)
+    *bytes_read = g_simple_async_result_get_op_res_gssize (simple);
+  
+  return TRUE;
+}
\ No newline at end of file
diff --git a/daemon/gvfsafpvolume.h b/daemon/gvfsafpvolume.h
new file mode 100644
index 0000000..aed9dd7
--- /dev/null
+++ b/daemon/gvfsafpvolume.h
@@ -0,0 +1,281 @@
+ /* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) Carl-Anton Ingmarsson 2011 <ca ingmarsson gmail com>
+ *
+ * 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: Carl-Anton Ingmarsson <ca ingmarsson gmail com>
+ */
+
+#ifndef _GVFSAFPVOLUME_H_
+#define _GVFSAFPVOLUME_H_
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GVfsAfpServer GVfsAfpServer;
+
+#define G_VFS_TYPE_AFP_VOLUME             (g_vfs_afp_volume_get_type ())
+#define G_VFS_AFP_VOLUME(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_VFS_TYPE_AFP_VOLUME, GVfsAfpVolume))
+#define G_VFS_AFP_VOLUME_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), G_VFS_TYPE_AFP_VOLUME, GVfsAfpVolumeClass))
+#define G_VFS_IS_AFP_VOLUME(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_VFS_TYPE_AFP_VOLUME))
+#define G_VFS_IS_AFP_VOLUME_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), G_VFS_TYPE_AFP_VOLUME))
+#define G_VFS_AFP_VOLUME_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), G_VFS_TYPE_AFP_VOLUME, GVfsAfpVolumeClass))
+
+typedef struct _GVfsAfpVolumeClass GVfsAfpVolumeClass;
+typedef struct _GVfsAfpVolume GVfsAfpVolume;
+typedef struct _GVfsAfpVolumePrivate GVfsAfpVolumePrivate;
+
+struct _GVfsAfpVolumeClass
+{
+  GObjectClass parent_class;
+};
+
+struct _GVfsAfpVolume
+{
+  GObject parent_instance;
+
+  GVfsAfpVolumePrivate *priv;
+};
+
+GType g_vfs_afp_volume_get_type (void) G_GNUC_CONST;
+
+GVfsAfpVolume *g_vfs_afp_volume_new                 (GVfsAfpServer *server);
+
+gboolean       g_vfs_afp_volume_mount_sync          (GVfsAfpVolume *volume,
+                                                     const char    *volume_name,
+                                                     GCancellable  *cancellable,
+                                                     GError       **error);
+
+guint16        g_vfs_afp_volume_get_attributes      (GVfsAfpVolume *volume);
+guint16        g_vfs_afp_volume_get_id              (GVfsAfpVolume *volume);
+
+void           g_vfs_afp_volume_get_parms           (GVfsAfpVolume        *volume,
+                                                     guint16              vol_bitmap,
+                                                     GCancellable        *cancellable,
+                                                     GAsyncReadyCallback  callback,
+                                                     gpointer             user_data);
+
+GFileInfo *    g_vfs_afp_volume_get_parms_finish    (GVfsAfpVolume       *volume,
+                                                     GAsyncResult        *result,
+                                                     GError             **error);
+
+void           g_vfs_afp_volume_open_fork           (GVfsAfpVolume      *volume,
+                                                     const char         *filename,
+                                                     guint16             access_mode,
+                                                     guint16             bitmap,
+                                                     GCancellable       *cancellable,
+                                                     GAsyncReadyCallback callback,
+                                                     gpointer            user_data);
+
+gboolean       g_vfs_afp_volume_open_fork_finish    (GVfsAfpVolume  *volume,
+                                                     GAsyncResult   *res,
+                                                     gint16         *fork_refnum,
+                                                     GFileInfo      **info,
+                                                     GError         **error);
+
+void           g_vfs_afp_volume_close_fork          (GVfsAfpVolume       *volume,
+                                                     gint16               fork_refnum,
+                                                     GCancellable        *cancellable,
+                                                     GAsyncReadyCallback  callback,
+                                                     gpointer             user_data);
+
+gboolean      g_vfs_afp_volume_close_fork_finish   (GVfsAfpVolume  *volume,
+                                                    GAsyncResult   *result,
+                                                    GError         **error);
+
+void          g_vfs_afp_volume_delete              (GVfsAfpVolume       *volume,
+                                                    const char          *filename,
+                                                    GCancellable        *cancellable,
+                                                    GAsyncReadyCallback  callback,
+                                                    gpointer             user_data);
+
+gboolean      g_vfs_afp_volume_delete_finish       (GVfsAfpVolume  *volume,
+                                                    GAsyncResult   *result,
+                                                    GError         **error);
+
+void          g_vfs_afp_volume_create_file         (GVfsAfpVolume      *volume,
+                                                    const char         *filename,
+                                                    gboolean            hard_create,
+                                                    GCancellable       *cancellable,
+                                                    GAsyncReadyCallback callback,
+                                                    gpointer            user_data);
+
+gboolean     g_vfs_afp_volume_create_file_finish   (GVfsAfpVolume  *volume,
+                                                    GAsyncResult   *result,
+                                                    GError         **error);
+
+void         g_vfs_afp_volume_create_directory     (GVfsAfpVolume      *volume,
+                                                    const char         *directory,
+                                                    GCancellable       *cancellable,
+                                                    GAsyncReadyCallback callback,
+                                                    gpointer            user_data);
+
+gboolean     g_vfs_afp_volume_create_directory_finish (GVfsAfpVolume  *volume,
+                                                       GAsyncResult   *result,
+                                                       GError         **error);
+
+void         g_vfs_afp_volume_copy_file            (GVfsAfpVolume      *volume,
+                                                    const char         *source,
+                                                    const char         *destination,
+                                                    GCancellable       *cancellable,
+                                                    GAsyncReadyCallback callback,
+                                                    gpointer            user_data);
+
+gboolean     g_vfs_afp_volume_copy_file_finish     (GVfsAfpVolume *volume,
+                                                    GAsyncResult  *res,
+                                                    GError       **error);
+
+void         g_vfs_afp_volume_rename               (GVfsAfpVolume      *volume,
+                                                    const char         *filename,
+                                                    const char         *new_name,
+                                                    GCancellable       *cancellable,
+                                                    GAsyncReadyCallback callback,
+                                                    gpointer            user_data);
+
+gboolean     g_vfs_afp_volume_rename_finish        (GVfsAfpVolume  *volume,
+                                                    GAsyncResult   *res,
+                                                    GError        **error);
+
+void         g_vfs_afp_volume_move_and_rename      (GVfsAfpVolume      *volume,
+                                                    const char         *source,
+                                                    const char         *destination,
+                                                    GCancellable       *cancellable,
+                                                    GAsyncReadyCallback callback,
+                                                    gpointer            user_data);
+
+gboolean     g_vfs_afp_volume_move_and_rename_finish (GVfsAfpVolume  *volume,
+                                                      GAsyncResult   *res,
+                                                      GError        **error);
+
+void          g_vfs_afp_volume_map_id              (GVfsAfpVolume       *volume,
+                                                    AfpMapIDFunction     map_function,
+                                                    gint64               id,
+                                                    GCancellable        *cancellable,
+                                                    GAsyncReadyCallback  callback,
+                                                    gpointer             user_data);
+
+char *        g_vfs_afp_volume_map_id_finish       (GVfsAfpVolume   *volume,
+                                                    GAsyncResult     *res,
+                                                    AfpMapIDFunction *map_function,
+                                                    GError          **error);
+
+void          g_vfs_afp_volume_get_filedir_parms   (GVfsAfpVolume       *volume,
+                                                    const char          *filename,
+                                                    guint16              file_bitmap,
+                                                    guint16              dir_bitmap,
+                                                    GCancellable        *cancellable,
+                                                    GAsyncReadyCallback  callback,
+                                                    gpointer             user_data);
+
+GFileInfo *   g_vfs_afp_volume_get_filedir_parms_finish (GVfsAfpVolume  *volume,
+                                                         GAsyncResult   *result,
+                                                         GError         **error);
+
+void          g_vfs_afp_volume_get_fork_parms      (GVfsAfpVolume       *volume,
+                                                    gint16               fork_refnum,
+                                                    guint16              file_bitmap,
+                                                    GCancellable        *cancellable,
+                                                    GAsyncReadyCallback  callback,
+                                                    gpointer             user_data);
+
+GFileInfo *   g_vfs_afp_volume_get_fork_parms_finish (GVfsAfpVolume  *volume,
+                                                      GAsyncResult   *result,
+                                                      GError         **error);
+
+void          g_vfs_afp_volume_set_fork_size         (GVfsAfpVolume       *volume,
+                                                      gint16               fork_refnum,
+                                                      gint64               size,
+                                                      GCancellable        *cancellable,
+                                                      GAsyncReadyCallback  callback,
+                                                      gpointer             user_data);
+
+gboolean      g_vfs_afp_volume_set_fork_size_finish  (GVfsAfpVolume  *volume,
+                                                      GAsyncResult   *result,
+                                                      GError         **error);
+
+void          g_vfs_afp_volume_set_unix_privs      (GVfsAfpVolume       *volume,
+                                                    const char          *filename,
+                                                    guint32              uid,
+                                                    guint32              gid,
+                                                    guint32              permissions,
+                                                    guint32              ua_permissions,
+                                                    GCancellable        *cancellable,
+                                                    GAsyncReadyCallback  callback,
+                                                    gpointer             user_data);
+
+gboolean      g_vfs_afp_volume_set_unix_privs_finish (GVfsAfpVolume  *volume,
+                                                      GAsyncResult   *res,
+                                                      GError        **error);
+
+void          g_vfs_afp_volume_enumerate             (GVfsAfpVolume       *volume,
+                                                      const char          *directory,
+                                                      gint64               start_index,
+                                                      guint16              file_bitmap,
+                                                      guint16              dir_bitmap,
+                                                      GCancellable        *cancellable,
+                                                      GAsyncReadyCallback  callback,
+                                                      gpointer             user_data);
+
+gboolean      g_vfs_afp_volume_enumerate_finish      (GVfsAfpVolume  *volume,
+                                                      GAsyncResult   *res,
+                                                      GPtrArray      **infos,
+                                                      GError        **error);
+
+void          g_vfs_afp_volume_exchange_files        (GVfsAfpVolume       *volume,
+                                                      const char          *source,
+                                                      const char          *destination,
+                                                      GCancellable        *cancellable,
+                                                      GAsyncReadyCallback  callback,
+                                                      gpointer             user_data);
+
+gboolean      g_vfs_afp_volume_exchange_files_finish (GVfsAfpVolume  *volume,
+                                                      GAsyncResult   *res,
+                                                      GError        **error);
+
+void          g_vfs_afp_volume_write_to_fork         (GVfsAfpVolume       *volume,
+                                                      guint16              fork_refnum,
+                                                      char                *buffer,
+                                                      gsize                buffer_size,
+                                                      gint64               offset,
+                                                      GCancellable        *cancellable,
+                                                      GAsyncReadyCallback  callback,
+                                                      gpointer             user_data);
+
+gboolean      g_vfs_afp_volume_write_to_fork_finish  (GVfsAfpVolume  *volume,
+                                                      GAsyncResult   *res,
+                                                      gint64         *last_written,
+                                                      GError        **error);
+
+void          g_vfs_afp_volume_read_from_fork        (GVfsAfpVolume       *volume,
+                                                      guint16              fork_refnum,
+                                                      char                *buffer,
+                                                      gsize                bytes_requested,
+                                                      gint64               offset,
+                                                      GCancellable        *cancellable,
+                                                      GAsyncReadyCallback  callback,
+                                                      gpointer             user_data);
+
+gboolean      g_vfs_afp_volume_read_from_fork_finish (GVfsAfpVolume  *volume,
+                                                      GAsyncResult   *res,
+                                                      gsize          *bytes_read,
+                                                      GError        **error);
+
+
+G_END_DECLS
+
+#endif /* _GVFSAFPVOLUME_H_ */
diff --git a/daemon/gvfsbackendafp.c b/daemon/gvfsbackendafp.c
index d35ee7a..3e79021 100644
--- a/daemon/gvfsbackendafp.c
+++ b/daemon/gvfsbackendafp.c
@@ -53,19 +53,9 @@
 #include "gvfsjobcopy.h"
 
 #include "gvfsafpserver.h"
-#include "gvfsafpconnection.h"
 
 #include "gvfsbackendafp.h"
 
-#define G_FILE_ATTRIBUTE_AFP_NODE_ID        "afp::node-id"
-#define G_FILE_ATTRIBUTE_AFP_PARENT_DIR_ID  "afp::parent-dir-id"
-#define G_FILE_ATTRIBUTE_AFP_CHILDREN_COUNT "afp::children-count"
-#define G_FILE_ATTRIBUTE_AFP_UA_PERMISSIONS "afp::ua-permisssions"
-
-static const gint16 ENUMERATE_REQ_COUNT           = G_MAXINT16;
-static const gint16 ENUMERATE_EXT_MAX_REPLY_SIZE  = G_MAXINT16; 
-static const gint32 ENUMERATE_EXT2_MAX_REPLY_SIZE = G_MAXINT32;
-
 struct _GVfsBackendAfpClass
 {
   GVfsBackendClass parent_class;
@@ -76,13 +66,11 @@ struct _GVfsBackendAfp
   GVfsBackend parent_instance;
 
   GNetworkAddress    *addr;
-  char               *volume;
+  char               *volume_name;
   char               *user;
 
   GVfsAfpServer      *server;
-  
-  guint16             vol_attrs_bitmap;
-  guint16             volume_id;
+  GVfsAfpVolume      *volume;
 
   guint32             user_id;
   guint32             group_id;
@@ -95,27 +83,6 @@ G_DEFINE_TYPE (GVfsBackendAfp, g_vfs_backend_afp, G_VFS_TYPE_BACKEND);
 /*
  * Utility functions
  */
-static void
-job_failed_from_afp_result_code (GVfsJob *job, AfpResultCode res_code)
-{
-  GError *err;
-  
-  err = afp_result_code_to_gerror (res_code);
-  g_vfs_job_failed_from_error (job, err);
-  g_error_free (err);
-}
-
-static gboolean
-is_root (const char *filename)
-{
-  const char *p;
-
-  p = filename;
-  while (*p == '/')
-    p++;
-
-  return *p == 0;
-}
 
 static void
 copy_file_info_into (GFileInfo *src, GFileInfo *dest)
@@ -137,46 +104,6 @@ copy_file_info_into (GFileInfo *src, GFileInfo *dest)
   g_strfreev (attrs);
 }
 
-static GVfsAfpName *
-filename_to_afp_pathname (const char *filename)
-{
-  gsize len;
-  char *str;
-  gint i;
-
-  while (*filename == '/')
-    filename++;
-  
-  len = strlen (filename);
-  
-  str = g_malloc (len); 
-
-  for (i = 0; i < len; i++)
-  {
-    if (filename[i] == '/')
-      str[i] = '\0';
-    else
-      str[i] = filename[i];
-  }
-  
-
-  return g_vfs_afp_name_new (0x08000103, str, len);
-}
-
-static void
-put_pathname (GVfsAfpCommand *comm, const char *filename)
-{
-  GVfsAfpName *pathname;
-  
-  /* PathType */
-  g_vfs_afp_command_put_byte (comm, AFP_PATH_TYPE_UTF8_NAME);
-
-  /* Pathname */
-  pathname = filename_to_afp_pathname (filename);
-  g_vfs_afp_command_put_afp_name (comm, pathname);
-  g_vfs_afp_name_unref (pathname);
-}
-
 typedef enum
 {
   AFP_HANDLE_TYPE_READ_FILE,
@@ -188,6 +115,8 @@ typedef enum
 
 typedef struct
 {
+  GVfsBackendAfp *backend;
+  
   AfpHandleType type;
   gint16 fork_refnum;
   gint64 offset;
@@ -202,11 +131,12 @@ typedef struct
 } AfpHandle;
 
 static AfpHandle *
-afp_handle_new (gint16 fork_refnum)
+afp_handle_new (GVfsBackendAfp *backend, gint16 fork_refnum)
 {
   AfpHandle *afp_handle;
 
   afp_handle = g_slice_new0 (AfpHandle);
+  afp_handle->backend = backend;
   afp_handle->fork_refnum = fork_refnum;
 
   return afp_handle;
@@ -215,1445 +145,10 @@ afp_handle_new (gint16 fork_refnum)
 static void
 afp_handle_free (AfpHandle *afp_handle)
 {
-  g_free (afp_handle->filename);
-  g_free (afp_handle->tmp_filename);
-  
-  g_slice_free (AfpHandle, afp_handle);
-}
-
-static void
-set_access_attributes_trusted (GFileInfo *info,
-                               guint32 perm)
-{
-  g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ,
-				     perm & 0x4);
-  g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
-				     perm & 0x2);
-  g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE,
-				     perm & 0x1);
-}
-
-/* For files we don't own we can't trust a negative response to this check, as
-   something else could allow us to do the operation, for instance an ACL
-   or some sticky bit thing */
-static void
-set_access_attributes (GFileInfo *info,
-                       guint32 perm)
-{
-  if (perm & 0x4)
-    g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ,
-				       TRUE);
-  if (perm & 0x2)
-    g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
-				       TRUE);
-  if (perm & 0x1)
-    g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE,
-				       TRUE);
-}
-
-static void fill_info (GVfsBackendAfp *afp_backend,
-                       GFileInfo *info, GVfsAfpReply *reply,
-                       gboolean directory, guint16 bitmap)
-{
-  goffset start_pos;
-
-  if (directory)
-  {
-    GIcon *icon;
-    
-    g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
-    g_file_info_set_content_type (info, "inode/directory");
-
-    icon = g_themed_icon_new ("folder");
-    g_file_info_set_icon (info, icon);
-    g_object_unref (icon);
-  }
-  else
-    g_file_info_set_file_type (info, G_FILE_TYPE_REGULAR);
-
-  
-  start_pos = g_vfs_afp_reply_get_pos (reply);
-
-  if (bitmap & AFP_FILEDIR_BITMAP_ATTRIBUTE_BIT)
-  {
-    guint16 attributes;
-
-    g_vfs_afp_reply_read_uint16 (reply, &attributes);
-    
-    if (attributes & AFP_FILEDIR_ATTRIBUTES_BITMAP_INVISIBLE_BIT)
-      g_file_info_set_is_hidden (info, TRUE);
-  }
-
-  if (bitmap & AFP_FILEDIR_BITMAP_PARENT_DIR_ID_BIT)
-  {
-    guint32 parent_dir_id;
-
-    g_vfs_afp_reply_read_uint32 (reply, &parent_dir_id);
-    g_file_info_set_attribute_uint32 (info, "afp::parent-dir-id", parent_dir_id);
-  }
-  
-  if (bitmap & AFP_FILEDIR_BITMAP_CREATE_DATE_BIT)
-  {
-    gint32 create_date;
-    gint64 create_date_local;
-
-    g_vfs_afp_reply_read_int32 (reply, &create_date);
-    
-    create_date_local = g_vfs_afp_server_time_to_local_time (afp_backend->server,
-                                                             create_date);
-    g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_CREATED,
-                                      create_date_local);
-  }
-
-  if (bitmap & AFP_FILEDIR_BITMAP_MOD_DATE_BIT)
-  {
-    gint32 mod_date;
-    guint64 mod_date_unix;
-    char *etag;
-
-    g_vfs_afp_reply_read_int32 (reply, &mod_date);
-    mod_date_unix = g_vfs_afp_server_time_to_local_time (afp_backend->server,
-                                                         mod_date);
-    
-    g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED,
-                                      mod_date_unix);
-
-    etag = g_strdup_printf ("%"G_GUINT64_FORMAT, mod_date_unix);
-    g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_ETAG_VALUE, etag);
-    g_free (etag);
-  }
-
-  if (bitmap & AFP_FILEDIR_BITMAP_NODE_ID_BIT)
-  {
-    guint32 node_id;
-
-    g_vfs_afp_reply_read_uint32 (reply, &node_id);
-    g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_AFP_NODE_ID, node_id);
-  }
-  
-  /* Directory specific attributes */
-  if (directory)
-  {
-    if (bitmap & AFP_DIR_BITMAP_OFFSPRING_COUNT_BIT)
-    {
-      guint16 offspring_count;
-
-      g_vfs_afp_reply_read_uint16 (reply, &offspring_count);
-      g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_AFP_CHILDREN_COUNT,
-                                        offspring_count);
-    }
-  }
-  
-  /* File specific attributes */
-  else
-  {
-    if (bitmap & AFP_FILE_BITMAP_EXT_DATA_FORK_LEN_BIT)
-    {
-      guint64 fork_len;
-
-      g_vfs_afp_reply_read_uint64 (reply, &fork_len);
-      g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE,
-                                        fork_len);
-    }
-  }
-  
-  if (bitmap & AFP_FILEDIR_BITMAP_UTF8_NAME_BIT)
-  {
-    guint16 UTF8Name_offset;
-    goffset old_pos;
-    GVfsAfpName *afp_name;
-    char *utf8_name;
-
-    g_vfs_afp_reply_read_uint16 (reply, &UTF8Name_offset);
-    /* Pad */
-    g_vfs_afp_reply_read_uint32 (reply, NULL);
-
-    old_pos = g_vfs_afp_reply_get_pos (reply);
-    g_vfs_afp_reply_seek (reply, start_pos + UTF8Name_offset, G_SEEK_SET);
-
-    g_vfs_afp_reply_read_afp_name (reply, TRUE, &afp_name);
-    utf8_name = g_vfs_afp_name_get_string (afp_name);    
-    g_vfs_afp_name_unref (afp_name);
-
-    g_file_info_set_name (info, utf8_name);
-    g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
-                                      utf8_name);
-
-    /* Set file as hidden if it begins with a dot */
-    if (utf8_name[0] == '.')
-      g_file_info_set_is_hidden (info, TRUE);
-
-    if (!directory)
-    {
-      char *content_type;
-      GIcon *icon;
-
-      content_type = g_content_type_guess (utf8_name, NULL, 0, NULL);
-      g_file_info_set_content_type (info, content_type);
-      g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE,
-                                        content_type);
-
-      icon = g_content_type_get_icon (content_type);
-      g_file_info_set_icon (info, icon);
-
-      g_object_unref (icon);
-      g_free (content_type);
-    }
-    
-    g_free (utf8_name);
-
-    g_vfs_afp_reply_seek (reply, old_pos, G_SEEK_SET);
-  }
-
-  if (bitmap & AFP_FILEDIR_BITMAP_UNIX_PRIVS_BIT)
-  {
-    guint32 uid, gid, permissions, ua_permissions;
-
-    g_vfs_afp_reply_read_uint32 (reply, &uid);
-    g_vfs_afp_reply_read_uint32 (reply, &gid);
-    g_vfs_afp_reply_read_uint32 (reply, &permissions);
-    /* ua_permissions */
-    g_vfs_afp_reply_read_uint32 (reply, &ua_permissions);
-
-    g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE, permissions);
-    g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID, uid);
-    g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_GID, gid);
-
-    g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_AFP_UA_PERMISSIONS,
-                                      ua_permissions);
-    
-    if (uid == afp_backend->user_id)
-      set_access_attributes_trusted (info, (permissions >> 6) & 0x7);
-    else if (gid == afp_backend->group_id)
-      set_access_attributes (info, (permissions >> 3) & 0x7);
-    else
-      set_access_attributes (info, (permissions >> 0) & 0x7);
-  }
-}
-
-typedef struct
-{
-  gint16 fork_refnum;
-  GFileInfo *info;
-} OpenForkData;
-
-static void
-open_fork_data_free (OpenForkData *data)
-{
-  g_object_unref (data->info);
-
-  g_slice_free (OpenForkData, data);
-}
-
-static void
-open_fork_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
-{
-  GVfsAfpConnection *afp_conn = G_VFS_AFP_CONNECTION (source_object);
-  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
-  
-  GVfsBackendAfp *afp_backend;
-  GVfsAfpReply *reply;
-  GError *err = NULL;
-  AfpResultCode res_code;
-
-  OpenForkData *data;
-  guint16 file_bitmap;
-
-  afp_backend = G_VFS_BACKEND_AFP (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
-  
-  reply = g_vfs_afp_connection_send_command_finish (afp_conn, res, &err);
-  if (!reply)
-  {
-    g_simple_async_result_take_error (simple, err);
-    goto done;
-  }
-
-  res_code = g_vfs_afp_reply_get_result_code (reply);
-  if (res_code != AFP_RESULT_NO_ERROR)
-  {
-    g_object_unref (reply);
-
-    switch (res_code)
-    {
-      case AFP_RESULT_ACCESS_DENIED:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
-                                         _("Permission denied"));
-        break;
-      case AFP_RESULT_OBJECT_NOT_FOUND:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
-                                         _("File doesn't exist"));
-        break;
-      case AFP_RESULT_OBJECT_TYPE_ERR:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
-                                         _("File is directory"));
-        break;
-      case AFP_RESULT_TOO_MANY_FILES_OPEN:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_TOO_MANY_OPEN_FILES,
-                                         _("Too many files open"));
-        break;
-      default:
-        g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
-        break;
-    }
-    goto done;
-  }
-
-  data = g_slice_new (OpenForkData);
-  
-  g_vfs_afp_reply_read_uint16 (reply, &file_bitmap);
-  g_vfs_afp_reply_read_int16  (reply, &data->fork_refnum);
-
-  data->info = g_file_info_new ();
-  fill_info (afp_backend, data->info, reply, FALSE, file_bitmap);
-  g_object_unref (reply);
-
-  g_simple_async_result_set_op_res_gpointer (simple, data,
-                                             (GDestroyNotify)open_fork_data_free);
-
-done:
-  g_simple_async_result_complete (simple);
-  g_object_unref (simple);
-}
-
-static void
-open_fork (GVfsBackendAfp     *afp_backend,
-           const char         *filename,
-           guint16             access_mode,
-           guint16             bitmap,
-           GCancellable       *cancellable,
-           GAsyncReadyCallback callback,
-           gpointer            user_data)
-{
-  GVfsAfpCommand *comm;
-  GSimpleAsyncResult *simple;
-
-  if (is_root (filename))
-  {
-    g_simple_async_report_error_in_idle (G_OBJECT (afp_backend), callback,
-                                         user_data, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
-                                         _("File is directory"));
-    return;
-  }
-
-  comm = g_vfs_afp_command_new (AFP_COMMAND_OPEN_FORK);
-  /* data fork */
-  g_vfs_afp_command_put_byte (comm, 0);
-
-  /* Volume ID */
-  g_vfs_afp_command_put_uint16 (comm, afp_backend->volume_id);
-  /* Directory ID */
-  g_vfs_afp_command_put_uint32 (comm, 2);
-
-  /* Bitmap */
-  g_vfs_afp_command_put_uint16 (comm, bitmap);
-
-  /* AccessMode */
-  g_vfs_afp_command_put_uint16 (comm, access_mode);
-
-  /* Pathname */
-  put_pathname (comm, filename);
-
-  simple = g_simple_async_result_new (G_OBJECT (afp_backend), callback,
-                                      user_data, open_fork);
-  
-  g_vfs_afp_connection_send_command (afp_backend->server->conn, comm, NULL,
-                                     open_fork_cb, cancellable, simple);
-  g_object_unref (comm);
-}
-
-static gboolean
-open_fork_finish (GVfsBackendAfp *afp_backend,
-                  GAsyncResult   *res,
-                  gint16         *fork_refnum,
-                  GFileInfo      **info,
-                  GError         **error)
-{
-  GSimpleAsyncResult *simple;
-  OpenForkData *data;
-
-  g_return_val_if_fail (g_simple_async_result_is_valid (res,
-                                                        G_OBJECT (afp_backend),
-                                                        open_fork),
-                        FALSE);
-
-  simple = (GSimpleAsyncResult *)res;
-
-  if (g_simple_async_result_propagate_error (simple, error))
-    return FALSE;
-
-  data = g_simple_async_result_get_op_res_gpointer (simple);
-  if (fork_refnum)
-    *fork_refnum = data->fork_refnum;
-  if (info)
-    *info = g_object_ref (data->info);
-
-  return TRUE;
-}
-
-static void
-close_fork_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
-{
-  GVfsAfpConnection *afp_conn = G_VFS_AFP_CONNECTION (source_object);
-  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
-
-  GVfsAfpReply *reply;
-  GError *err = NULL;
-  AfpResultCode res_code;
-
-  reply = g_vfs_afp_connection_send_command_finish (afp_conn, res, &err);
-  if (!reply)
-  {
-    g_simple_async_result_take_error (simple, err);
-    goto done;
-  }
-
-  res_code = g_vfs_afp_reply_get_result_code (reply);
-  g_object_unref (reply);
-  
-  if (res_code != AFP_RESULT_NO_ERROR)
-    g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
-
-done:
-  g_simple_async_result_complete (simple);
-  g_object_unref (simple);
-}
-
-static void
-close_fork (GVfsBackendAfp      *afp_backend,
-            gint16               fork_refnum,
-            GCancellable        *cancellable,
-            GAsyncReadyCallback  callback,
-            gpointer             user_data)
-{
-  GVfsAfpCommand *comm;
-  GSimpleAsyncResult *simple;
-
-  comm = g_vfs_afp_command_new (AFP_COMMAND_CLOSE_FORK);
-  /* pad byte */
-  g_vfs_afp_command_put_byte (comm, 0);
-
-  /* OForkRefNum */
-  g_vfs_afp_command_put_int16 (comm, fork_refnum);
-
-  simple = g_simple_async_result_new (G_OBJECT (afp_backend), callback, user_data,
-                                      close_fork);
-  
-  g_vfs_afp_connection_send_command (afp_backend->server->conn, comm, NULL,
-                                      close_fork_cb, cancellable,
-                                      simple);
-  g_object_unref (comm);
-}
-
-static gboolean
-close_fork_finish (GVfsBackendAfp *afp_backend,
-                   GAsyncResult   *result,
-                   GError         **error)
-{
-  GSimpleAsyncResult *simple;
-  
-  g_return_val_if_fail (g_simple_async_result_is_valid (result,
-                                                        G_OBJECT (afp_backend),
-                                                        close_fork),
-                        FALSE);
-
-  simple = (GSimpleAsyncResult *)result;
-
-  if (g_simple_async_result_propagate_error (simple, error))
-    return FALSE;
-
-  return TRUE;
-}
-
-static void
-get_fork_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
-{
-  GVfsAfpConnection *afp_conn = G_VFS_AFP_CONNECTION (source_object);
-  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
-
-  GVfsAfpReply *reply;
-  GError *err = NULL;
-  AfpResultCode res_code;
-
-  guint16 file_bitmap;
-  GFileInfo *info;
-
-  reply = g_vfs_afp_connection_send_command_finish (afp_conn, res, &err);
-  if (!reply)
-  {
-    g_simple_async_result_take_error (simple, err);
-    goto done;
-  }
-
-  res_code = g_vfs_afp_reply_get_result_code (reply);
-  if (res_code != AFP_RESULT_NO_ERROR)
-  {
-    g_object_unref (reply);
-
-    g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
-    goto done;
-  }
-
-  g_vfs_afp_reply_read_uint16 (reply, &file_bitmap);
-
-  info = g_file_info_new ();
-  fill_info (afp_backend, info, reply, FALSE, file_bitmap);
-
-  g_object_unref (reply);
-
-  g_simple_async_result_set_op_res_gpointer (simple, info, g_object_unref);
-
-done:
-  g_simple_async_result_complete (simple);
-  g_object_unref (simple);
-}
-
-static void
-get_fork_parms (GVfsBackendAfp      *afp_backend,
-                gint16               fork_refnum,
-                guint16              file_bitmap,
-                GCancellable        *cancellable,
-                GAsyncReadyCallback  callback,
-                gpointer             user_data)
-{
-  GVfsAfpCommand *comm;
-  GSimpleAsyncResult *simple;
-
-  comm = g_vfs_afp_command_new (AFP_COMMAND_GET_FORK_PARMS);
-  /* pad byte */
-  g_vfs_afp_command_put_byte (comm, 0);
-  /* OForkRefNum */
-  g_vfs_afp_command_put_int16 (comm, fork_refnum);
-  /* Bitmap */  
-  g_vfs_afp_command_put_uint16 (comm, file_bitmap);
-
-  simple = g_simple_async_result_new (G_OBJECT (afp_backend), callback, user_data,
-                                      get_fork_parms);
-                                      
-  
-  g_vfs_afp_connection_send_command (afp_backend->server->conn, comm, NULL,
-                                      get_fork_parms_cb, cancellable,
-                                      simple);
-  g_object_unref (comm);
-}
-
-static GFileInfo *
-get_fork_parms_finish (GVfsBackendAfp *afp_backend,
-                       GAsyncResult   *result,
-                       GError         **error)
-{
-  GSimpleAsyncResult *simple;
-  
-  g_return_val_if_fail (g_simple_async_result_is_valid (result,
-                                                        G_OBJECT (afp_backend),
-                                                        get_fork_parms),
-                        NULL);
-
-  simple = (GSimpleAsyncResult *)result;
-
-  if (g_simple_async_result_propagate_error (simple, error))
-    return NULL;
-
-  return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
-}
-
-static void
-get_filedir_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
-{
-  GVfsAfpConnection *afp_conn = G_VFS_AFP_CONNECTION (source_object);
-  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (g_async_result_get_source_object (G_ASYNC_RESULT (simple)));
-
-  GVfsAfpReply *reply;
-  GError *err = NULL;
-  AfpResultCode res_code;
-
-  guint16 file_bitmap, dir_bitmap, bitmap;
-  guint8 FileDir;
-  gboolean directory;
-  GFileInfo *info;
-
-  reply = g_vfs_afp_connection_send_command_finish (afp_conn, res, &err);
-  if (!reply)
-  {
-    g_simple_async_result_take_error (simple, err);
-    goto done;
-  }
-
-  res_code = g_vfs_afp_reply_get_result_code (reply);
-  if (res_code != AFP_RESULT_NO_ERROR)
-  {
-    g_object_unref (reply);
-    
-    switch (res_code)
-    {
-      case AFP_RESULT_OBJECT_NOT_FOUND:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
-                                         _("File doesn't exist"));
-        break;
-      default:
-        g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
-        break;
-    }
-    goto done;
-  }
-
-  g_vfs_afp_reply_read_uint16 (reply, &file_bitmap);
-  g_vfs_afp_reply_read_uint16 (reply, &dir_bitmap);
-
-  g_vfs_afp_reply_read_byte (reply, &FileDir);
-  /* Pad Byte */
-  g_vfs_afp_reply_read_byte (reply, NULL);
-  
-  directory = (FileDir & 0x80); 
-  bitmap =  directory ? dir_bitmap : file_bitmap;
-
-  info = g_file_info_new ();
-  fill_info (afp_backend, info, reply, directory, bitmap);
-  
-  g_object_unref (reply);
-
-  g_simple_async_result_set_op_res_gpointer (simple, info, g_object_unref);
-
-done:
-  g_simple_async_result_complete (simple);
-  g_object_unref (simple);
-}
-
-static void
-get_filedir_parms (GVfsBackendAfp      *afp_backend,
-                   const char          *filename,
-                   guint16              file_bitmap,
-                   guint16              dir_bitmap,
-                   GCancellable        *cancellable,
-                   GAsyncReadyCallback  callback,
-                   gpointer             user_data)
-{
-  GVfsAfpCommand *comm;
-  GSimpleAsyncResult *simple;
-
-  comm = g_vfs_afp_command_new (AFP_COMMAND_GET_FILE_DIR_PARMS);
-  /* pad byte */
-  g_vfs_afp_command_put_byte (comm, 0);
-  /* VolumeID */
-  g_vfs_afp_command_put_uint16 (comm, afp_backend->volume_id);
-  /* Directory ID 2 == / */
-  g_vfs_afp_command_put_uint32 (comm, 2);
-  /* FileBitmap */  
-  g_vfs_afp_command_put_uint16 (comm, file_bitmap);
-  /* DirectoryBitmap */  
-  g_vfs_afp_command_put_uint16 (comm, dir_bitmap);
-  /* PathName */
-  put_pathname (comm, filename);
-
-  simple = g_simple_async_result_new (G_OBJECT (afp_backend), callback, user_data,
-                                      get_filedir_parms);
-                                      
-
-  g_vfs_afp_connection_send_command (afp_backend->server->conn, comm, NULL,
-                                     get_filedir_parms_cb, cancellable,
-                                     simple);
-  g_object_unref (comm);
-}
-
-static GFileInfo *
-get_filedir_parms_finish (GVfsBackendAfp *afp_backend,
-                          GAsyncResult   *result,
-                          GError         **error)
-{
-  GSimpleAsyncResult *simple;
-  
-  g_return_val_if_fail (g_simple_async_result_is_valid (result,
-                                                        G_OBJECT (afp_backend),
-                                                        get_filedir_parms),
-                        NULL);
-
-  simple = (GSimpleAsyncResult *)result;
-
-  if (g_simple_async_result_propagate_error (simple, error))
-    return NULL;
-
-  return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
-}
-
-typedef struct
-{
-  char *filename;
-  gboolean hard_create;
-  GCancellable *cancellable;
-} CreateFileData;
-
-static void
-free_create_file_data (CreateFileData *cfd)
-{
-  g_free (cfd->filename);
-  if (cfd->cancellable)
-    g_object_unref (cfd->cancellable);
-
-  g_slice_free (CreateFileData, cfd);
-}
-
-static void
-create_file_cb (GObject *object, GAsyncResult *res, gpointer user_data)
-{
-  GVfsAfpConnection *afp_conn = G_VFS_AFP_CONNECTION (object);
-  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
-
-  GVfsAfpReply *reply;
-  GError *err = NULL;
-  AfpResultCode res_code;
-
-  
-  reply = g_vfs_afp_connection_send_command_finish (afp_conn, res, &err);
-  if (!reply)
-  {
-    g_simple_async_result_take_error (simple, err);
-    goto done;
-  }
-
-  res_code = g_vfs_afp_reply_get_result_code (reply);
-  g_object_unref (reply);
-  if (res_code != AFP_RESULT_NO_ERROR)
-  {
-    switch (res_code)
-    {
-      case AFP_RESULT_ACCESS_DENIED:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
-                                  _("Permission denied"));
-        break;
-      case AFP_RESULT_DISK_FULL:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NO_SPACE,
-                                  _("Not enough space on volume"));
-        break;
-      case AFP_RESULT_FILE_BUSY:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_EXISTS,
-                                  _("Target file is open"));
-        break;
-      case AFP_RESULT_OBJECT_EXISTS:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_EXISTS,
-                                  _("Target file already exists"));
-        break;
-      case AFP_RESULT_OBJECT_NOT_FOUND:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
-                                  _("Ancestor directory doesn't exist"));
-        break;
-      case AFP_RESULT_VOL_LOCKED:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
-                                  _("Volume is read-only"));
-        break;
-      default:
-        g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
-        break;
-    }
-  }
-
-done:
-  g_simple_async_result_complete (simple);
-  g_object_unref (simple);
-}
-
-static void
-create_file_get_filedir_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
-{
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
-  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
-
-  CreateFileData *cfd = g_simple_async_result_get_op_res_gpointer (simple);
-  
-  GFileInfo *info;
-  GError *err = NULL;
-
-  guint32 dir_id;
-  char *basename;
-  GVfsAfpCommand *comm;
-
-  info = get_filedir_parms_finish (afp_backend, res, &err);
-  if (!info)
-  {
-    g_simple_async_result_take_error (simple, err);
-    g_simple_async_result_complete (simple);
-    g_object_unref (simple);
-    return;
-  }
-
-  dir_id = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_AFP_NODE_ID);
-  g_object_unref (info);
-
-  comm = g_vfs_afp_command_new (AFP_COMMAND_CREATE_FILE);
-  /* soft/hard create */
-  g_vfs_afp_command_put_byte (comm, cfd->hard_create ? 0x80 : 0x00);
-  /* Volume ID */
-  g_vfs_afp_command_put_uint16 (comm, afp_backend->volume_id);
-  /* Directory ID */
-  g_vfs_afp_command_put_uint32 (comm, dir_id);
-
-  /* Pathname */
-  basename = g_path_get_basename (cfd->filename);
-  put_pathname (comm, basename);
-  g_free (basename);
-
-  g_vfs_afp_connection_send_command (afp_backend->server->conn, comm, NULL,
-                                     create_file_cb, cfd->cancellable, simple);
-  g_object_unref (comm);
-}
-
-static void
-create_file (GVfsBackendAfp     *afp_backend,
-             const char         *filename,
-             gboolean            hard_create,
-             GCancellable       *cancellable,
-             GAsyncReadyCallback callback,
-             gpointer            user_data)
-{
-  CreateFileData *cfd;
-  GSimpleAsyncResult *simple;
-  char *dirname;
-
-  cfd = g_slice_new0 (CreateFileData);
-  cfd->filename = g_strdup (filename);
-  cfd->hard_create = hard_create;
-  if (cancellable)
-    cfd->cancellable = g_object_ref (cancellable);
-  
-  simple = g_simple_async_result_new (G_OBJECT (afp_backend), callback, user_data,
-                                      create_file);
-  g_simple_async_result_set_op_res_gpointer (simple, cfd,
-                                             (GDestroyNotify)free_create_file_data);
-
-  dirname = g_path_get_dirname (filename);
-  get_filedir_parms (afp_backend, dirname, 0, AFP_DIR_BITMAP_NODE_ID_BIT,
-                     cancellable, create_file_get_filedir_parms_cb, simple);
-  g_free (dirname);
-}
-
-static gboolean
-create_file_finish (GVfsBackendAfp *afp_backend,
-                    GAsyncResult   *result,
-                    GError         **error)
-{
-  GSimpleAsyncResult *simple;
-  
-  g_return_val_if_fail (g_simple_async_result_is_valid (result,
-                                                        G_OBJECT (afp_backend),
-                                                        create_file),
-                        FALSE);
-
-  simple = (GSimpleAsyncResult *)result;
-
-  if (g_simple_async_result_propagate_error (simple, error))
-    return FALSE;
-
-  return TRUE;
-}
-
-static void
-delete_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
-{
-  GVfsAfpConnection *afp_conn = G_VFS_AFP_CONNECTION (source_object);
-  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
-
-  GVfsAfpReply *reply;
-  GError *err = NULL;
-  AfpResultCode res_code;
-
-  reply = g_vfs_afp_connection_send_command_finish (afp_conn, res, &err);
-  if (!reply)
-  {
-    g_simple_async_result_take_error (simple, err);
-    goto done;
-  }
-
-  res_code = g_vfs_afp_reply_get_result_code (reply);
-  g_object_unref (reply);
-  
-  if (res_code != AFP_RESULT_NO_ERROR)
-  {
-    switch (res_code)
-    {
-      case AFP_RESULT_ACCESS_DENIED:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
-                                  _("Permission denied"));
-        break;
-      case AFP_RESULT_FILE_BUSY:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_BUSY,
-                                         _("Target file is open"));
-        break;                           
-      case AFP_RESULT_DIR_NOT_EMPTY:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NOT_EMPTY,
-                                  _("Directory not empty"));
-        break;
-      case AFP_RESULT_OBJECT_LOCKED:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_FAILED,
-                                  _("Target object is marked as not deletable (DeleteInhibit)"));
-        break;
-      case AFP_RESULT_OBJECT_NOT_FOUND:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
-                                  _("Target object doesn't exist"));
-        break;
-      case AFP_RESULT_VOL_LOCKED:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
-                                  _("Volume is read-only"));
-        break;
-      default:
-        g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
-        break;
-    }
-  }
-
-done:
-  g_simple_async_result_complete (simple);
-  g_object_unref (simple);
-}
-
-static void
-delete (GVfsBackendAfp      *afp_backend,
-        const char          *filename,
-        GCancellable        *cancellable,
-        GAsyncReadyCallback  callback,
-        gpointer             user_data)
-{
-  GVfsAfpCommand *comm;
-  GSimpleAsyncResult *simple;
-
-  comm = g_vfs_afp_command_new (AFP_COMMAND_DELETE);
-  /* pad byte */
-  g_vfs_afp_command_put_byte (comm, 0);
-  /* Volume ID */
-  g_vfs_afp_command_put_uint16 (comm, afp_backend->volume_id);
-  /* Directory ID 2 == / */
-  g_vfs_afp_command_put_uint32 (comm, 2);
-
-  /* Pathname */
-  put_pathname (comm, filename);
-
-  simple = g_simple_async_result_new (G_OBJECT (afp_backend), callback,
-                                      user_data, delete);
-  
-  g_vfs_afp_connection_send_command (afp_backend->server->conn, comm, NULL,
-                                     delete_cb, cancellable, simple);
-  g_object_unref (comm);
-}
-
-static gboolean
-delete_finish (GVfsBackendAfp *afp_backend,
-               GAsyncResult   *result,
-               GError         **error)
-{
-  GSimpleAsyncResult *simple;
-
-  g_return_val_if_fail (g_simple_async_result_is_valid (result,
-                                                        G_OBJECT (afp_backend),
-                                                        delete),
-                        FALSE);
-
-  simple = (GSimpleAsyncResult *)result;
-
-  if (g_simple_async_result_propagate_error (simple, error))
-    return FALSE;
-
-  return TRUE;
-}
-
-typedef struct
-{
-  AfpMapIDFunction function;
-  char *name;
-} MapIDData;
-
-static void
-map_id_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
-{
-  GVfsAfpConnection *afp_conn = G_VFS_AFP_CONNECTION (source_object);
-  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
-
-  GVfsAfpReply *reply;
-  GError *err = NULL;
-
-  AfpResultCode res_code;
-  MapIDData *map_data;
-
-  reply = g_vfs_afp_connection_send_command_finish (afp_conn, res, &err);
-  if (!reply)
-  {
-    g_simple_async_result_take_error (simple, err);
-    g_simple_async_result_complete (simple);
-    return;
-  }
-
-  res_code = g_vfs_afp_reply_get_result_code (reply);
-  if (res_code != AFP_RESULT_NO_ERROR)
-  {
-    switch (res_code)
-    {
-      case AFP_RESULT_ITEM_NOT_FOUND:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_FAILED,
-                                         _("ID not found"));
-        break;
-      default:
-        g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
-        break;
-    }
-
-    g_simple_async_result_complete (simple);
-    return;
-  }
-
-  map_data = g_simple_async_result_get_op_res_gpointer (simple);
-  
-  if (map_data->function == AFP_MAP_ID_FUNCTION_USER_UUID_TO_UTF8_NAME ||
-      map_data->function == AFP_MAP_ID_FUNCTION_GROUP_UUID_TO_UTF8_NAME)
-  {
-    /* objType */
-    g_vfs_afp_reply_read_uint32 (reply, NULL);
-    /* id */
-    g_vfs_afp_reply_read_uint32 (reply, NULL);
-  }
-
-  if (map_data->function == AFP_MAP_ID_FUNCTION_USER_ID_TO_NAME ||
-      map_data->function == AFP_MAP_ID_FUNCTION_GROUP_ID_TO_NAME)
-  {
-    g_vfs_afp_reply_read_pascal (reply, &map_data->name);
-  }
-  else
-  {
-    GVfsAfpName *afp_name;
-
-    g_vfs_afp_reply_read_afp_name (reply, FALSE, &afp_name);
-    map_data->name = g_vfs_afp_name_get_string (afp_name);
-    g_vfs_afp_name_unref (afp_name);
-  }
-
-  g_simple_async_result_complete (simple);
-}
-
-static void
-map_id (GVfsBackendAfp      *afp_backend,
-        AfpMapIDFunction     map_function,
-        gint64               id,
-        GCancellable        *cancellable,
-        GAsyncReadyCallback  callback,
-        gpointer             user_data)
-{
-  GVfsAfpCommand *comm;
-  GSimpleAsyncResult *simple;
-  MapIDData *map_data;
-
-  comm = g_vfs_afp_command_new (AFP_COMMAND_MAP_ID);
-
-  /* SubFunction*/
-  g_vfs_afp_command_put_byte (comm, map_function);
-
-  /* ID */
-  if (map_function == AFP_MAP_ID_FUNCTION_USER_ID_TO_NAME ||
-      map_function == AFP_MAP_ID_FUNCTION_GROUP_ID_TO_NAME)
-    g_vfs_afp_command_put_int32 (comm, id);
-  else
-    g_vfs_afp_command_put_int64 (comm, id);
-
-  simple = g_simple_async_result_new (G_OBJECT (afp_backend), callback,
-                                      user_data, map_id);
-
-  map_data = g_new (MapIDData, 1);
-  map_data->function = map_function;
-  g_simple_async_result_set_op_res_gpointer (simple, map_data, g_free);
-  
-  g_vfs_afp_connection_send_command (afp_backend->server->conn, comm, NULL,
-                                     map_id_cb, cancellable, simple);
-  g_object_unref (comm);
-}
-
-static char *
-map_id_finish (GVfsBackendAfp   *afp_backend,
-               GAsyncResult     *res,
-               AfpMapIDFunction *map_function,
-               GError          **error)
-{
-  GSimpleAsyncResult *simple;
-  MapIDData *map_data;
-
-  g_return_val_if_fail (g_simple_async_result_is_valid (res, G_OBJECT (afp_backend),
-                                                        map_id),
-                        NULL);
-
-  simple = (GSimpleAsyncResult *)res;
-
-  if (g_simple_async_result_propagate_error (simple, error))
-    return NULL;
-
-  map_data = g_simple_async_result_get_op_res_gpointer (simple);
-
-  if (map_function)
-    *map_function = map_data->function;
-  
-  return map_data->name;
-}
-
-static void
-move_and_rename_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
-{
-  GVfsAfpConnection *afp_conn = G_VFS_AFP_CONNECTION (source_object);
-  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
-
-  GVfsAfpReply *reply;
-  GError *err = NULL;
-
-  AfpResultCode res_code;
-
-  reply = g_vfs_afp_connection_send_command_finish (afp_conn, res, &err);
-  if (!reply)
-  {
-    g_simple_async_result_take_error (simple, err);
-    goto done;
-  }
-
-  res_code = g_vfs_afp_reply_get_result_code (reply);
-  g_object_unref (reply);
-  
-  if (res_code != AFP_RESULT_NO_ERROR)
-  {
-    switch (res_code)
-    {
-      case AFP_RESULT_ACCESS_DENIED:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
-                                         _("Permission denied"));
-        break;
-      case AFP_RESULT_CANT_MOVE:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_WOULD_RECURSE,
-                                         _("Can't move directory into one of its descendants"));
-        break;
-      case AFP_RESULT_INSIDE_SHARE_ERR:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_FAILED,
-                                         _("Can't move sharepoint into a shared directory"));
-        break;
-      case AFP_RESULT_INSIDE_TRASH_ERR:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_FAILED,
-                                         _("Can't move a shared directory into the Trash"));
-        break;
-      case AFP_RESULT_OBJECT_EXISTS:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_EXISTS,
-                                         _("Target file already exists"));
-        break;
-      case AFP_RESULT_OBJECT_LOCKED:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
-                                         _("Object being moved is marked as not renameable (RenameInhibit)"));
-        break;
-      case AFP_RESULT_OBJECT_NOT_FOUND:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
-                                         _("Object being moved doesn't exist"));
-        break;
-      default:
-        g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
-        break;
-    }
-  }
-
-done:
-  g_simple_async_result_complete (simple);
-  g_object_unref (simple);
-}
-
-static void
-move_and_rename (GVfsBackendAfp     *afp_backend,
-                 const char         *source,
-                 const char         *destination,
-                 GCancellable       *cancellable,
-                 GAsyncReadyCallback callback,
-                 gpointer            user_data)
-{
-  GVfsAfpCommand *comm;
-  char *dirname, *basename;
-  GSimpleAsyncResult *simple;
-
-  comm = g_vfs_afp_command_new (AFP_COMMAND_MOVE_AND_RENAME);
-  /* pad byte */
-  g_vfs_afp_command_put_byte (comm, 0);
-
-  /* VolumeID */
-  g_vfs_afp_command_put_uint16 (comm, afp_backend->volume_id);
-
-  /* SourceDirectoryID 2 == / */
-  g_vfs_afp_command_put_uint32 (comm, 2);
-  /* DestDirectoryID 2 == / */
-  g_vfs_afp_command_put_uint32 (comm, 2);
-
-  /* SourcePathname */
-  put_pathname (comm, source);
-
-  /* DestPathname */
-  dirname = g_path_get_dirname (destination);
-  put_pathname (comm, dirname);
-  g_free (dirname);
-
-  /* NewName */
-  basename = g_path_get_basename (destination);
-  put_pathname (comm, basename);
-  g_free (basename);
-
-  simple = g_simple_async_result_new (G_OBJECT (afp_backend), callback,
-                                      user_data, move_and_rename);
-  
-  g_vfs_afp_connection_send_command (afp_backend->server->conn, comm, NULL,
-                                     move_and_rename_cb, cancellable, simple);
-  g_object_unref (comm);
-}
-
-static gboolean
-move_and_rename_finish (GVfsBackendAfp *afp_backend,
-                        GAsyncResult   *res,
-                        GError        **error)
-{
-  GSimpleAsyncResult *simple;
-
-  g_return_val_if_fail (g_simple_async_result_is_valid (res, G_OBJECT (afp_backend),
-                                                        move_and_rename),
-                        FALSE);
-
-  simple = (GSimpleAsyncResult *)res;
-  
-  if (g_simple_async_result_propagate_error (simple, error))
-    return FALSE;
-
-  return TRUE;
-}
-
-static void
-copy_file_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
-{
-  GVfsAfpConnection *afp_conn = G_VFS_AFP_CONNECTION (source_object);
-  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
-
-  GVfsAfpReply *reply;
-  GError *err = NULL;
-
-  AfpResultCode res_code;
-
-  reply = g_vfs_afp_connection_send_command_finish (afp_conn, res, &err);
-  if (!reply)
-  {
-    g_simple_async_result_take_error (simple, err);
-    goto done;
-  }
-
-  res_code = g_vfs_afp_reply_get_result_code (reply);
-  g_object_unref (reply);
-
-  if (res_code != AFP_RESULT_NO_ERROR)
-  {
-    switch (res_code)
-    {
-      case AFP_RESULT_ACCESS_DENIED:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
-                                         _("Permission denied"));
-        break;
-      case AFP_RESULT_CALL_NOT_SUPPORTED:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
-                                         _("Server doesn't support the FPCopyFile operation"));
-        break;
-      case AFP_RESULT_DENY_CONFLICT:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_FAILED,
-                                         _("Unable to open source file for reading"));
-        break;
-      case AFP_RESULT_DISK_FULL:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NO_SPACE,
-                                         _("Not enough space on volume"));
-        break;
-      case AFP_RESULT_OBJECT_EXISTS:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_EXISTS,
-                                         _("Target file already exists"));
-        break;
-      case AFP_RESULT_OBJECT_NOT_FOUND:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
-                                         _("Source file and/or destination directory doesn't exist"));
-        break;
-      case AFP_RESULT_OBJECT_TYPE_ERR:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
-                                         _("Source file is a directory"));
-        break;
-      default:
-        g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
-        break;
-    }
-  }
-        
-done:
-  g_simple_async_result_complete (simple);
-  g_object_unref (simple);
-}
-
-static void
-copy_file (GVfsBackendAfp     *afp_backend,
-           const char         *source,
-           const char         *destination,
-           GCancellable       *cancellable,
-           GAsyncReadyCallback callback,
-           gpointer            user_data)
-{
-  GVfsAfpCommand *comm;
-  char *dirname, *basename;
-  GSimpleAsyncResult *simple;
-
-  comm = g_vfs_afp_command_new (AFP_COMMAND_COPY_FILE);
-  /* pad byte */
-  g_vfs_afp_command_put_byte (comm, 0);
-
-  /* SourceVolumeID */
-  g_vfs_afp_command_put_uint16 (comm, afp_backend->volume_id);
-  /* SourceDirectoryID 2 == / */
-  g_vfs_afp_command_put_uint32 (comm, 2);
-
-  /* DestVolumeID */
-  g_vfs_afp_command_put_uint16 (comm, afp_backend->volume_id);
-  /* DestDirectoryID 2 == / */
-  g_vfs_afp_command_put_uint32 (comm, 2);
-
-  /* SourcePathname */
-  put_pathname (comm, source);
-
-  /* DestPathname */
-  dirname = g_path_get_dirname (destination);
-  put_pathname (comm, dirname);
-  g_free (dirname);
-
-  /* NewName */
-  basename = g_path_get_basename (destination);
-  put_pathname (comm, basename);
-  g_free (basename);
-
-  simple = g_simple_async_result_new (G_OBJECT (afp_backend), callback,
-                                      user_data, copy_file);
-
-  g_vfs_afp_connection_send_command (afp_backend->server->conn, comm, NULL,
-                                     copy_file_cb, cancellable, simple);
-  g_object_unref (comm);
-}
-
-static gboolean
-copy_file_finish (GVfsBackendAfp *afp_backend, GAsyncResult *res, GError **error)
-{
-  GSimpleAsyncResult *simple;
-
-  g_return_val_if_fail (g_simple_async_result_is_valid (res, G_OBJECT (afp_backend),
-                                                        copy_file),
-                        FALSE);
-
-  simple = (GSimpleAsyncResult *)res;
-  
-  if (g_simple_async_result_propagate_error (simple, error))
-    return FALSE;
-
-  return TRUE;
-}
-
-static void
-set_unix_privs_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
-{
-  GVfsAfpConnection *afp_conn = G_VFS_AFP_CONNECTION (source_object);
-  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
-
-  GVfsAfpReply *reply;
-  GError *err = NULL;
-  AfpResultCode res_code;
-
-  reply = g_vfs_afp_connection_send_command_finish (afp_conn, res, &err);
-  if (!reply)
-  {
-    g_simple_async_result_take_error (simple, err);
-    goto done;
-  }
-
-  res_code = g_vfs_afp_reply_get_result_code (reply);
-  g_object_unref (reply);
-  if (res_code != AFP_RESULT_NO_ERROR)
-  {
-    switch (res_code)
-    {
-      case AFP_RESULT_ACCESS_DENIED:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
-                                         _("Permission denied"));
-        break;
-      case AFP_RESULT_OBJECT_NOT_FOUND:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
-                                         _("Target object doesn't exist"));
-        break;
-      case AFP_RESULT_VOL_LOCKED:
-        g_simple_async_result_set_error (simple, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
-                                         _("Volume is read-only"));
-        break;
-      default:
-        g_simple_async_result_take_error (simple, afp_result_code_to_gerror (res_code));
-        break;
-    }
-    goto done;
-  }
-
-done:
-  g_simple_async_result_complete (simple);
-  g_object_unref (simple);
-}
-
-static void
-set_unix_privs (GVfsBackendAfp      *afp_backend,
-                const char          *filename,
-                guint32              uid,
-                guint32              gid,
-                guint32              permissions,
-                guint32              ua_permissions,
-                GCancellable        *cancellable,
-                GAsyncReadyCallback  callback,
-                gpointer             user_data)
-{
-  GVfsAfpCommand *comm;
-  GSimpleAsyncResult *simple;
-
-  comm = g_vfs_afp_command_new (AFP_COMMAND_SET_FILEDIR_PARMS);
-  /* pad byte */
-  g_vfs_afp_command_put_byte (comm, 0);
-
-  /* VolumeID */
-  g_vfs_afp_command_put_uint16 (comm, afp_backend->volume_id);
-  /* DirectoryID 2 == / */
-  g_vfs_afp_command_put_uint32 (comm, 2);
-  /* Bitmap */
-  g_vfs_afp_command_put_uint16 (comm, AFP_FILEDIR_BITMAP_UNIX_PRIVS_BIT);
-  /* Pathname */
-  put_pathname (comm, filename);
-  /* pad to even */
-  g_vfs_afp_command_pad_to_even (comm);
-
-  /* UID */
-  g_vfs_afp_command_put_uint32 (comm, uid);
-  /* GID */
-  g_vfs_afp_command_put_uint32 (comm, gid);
-  /* Permissions */
-  g_vfs_afp_command_put_uint32 (comm, permissions);
-  /* UAPermissions */
-  g_vfs_afp_command_put_uint32 (comm, ua_permissions);
-
-  simple = g_simple_async_result_new (G_OBJECT (afp_backend), callback,
-                                      user_data, set_unix_privs);
-
-  g_vfs_afp_connection_send_command (afp_backend->server->conn, comm, NULL,
-                                     set_unix_privs_cb, cancellable, simple);
-  g_object_unref (comm);
-}
-
-static gboolean
-set_unix_privs_finish (GVfsBackendAfp *afp_backend,
-                       GAsyncResult   *res,
-                       GError        **error)
-{
-  GSimpleAsyncResult *simple;
-  
-  g_return_val_if_fail (g_simple_async_result_is_valid (res, G_OBJECT (afp_backend),
-                                                        set_unix_privs),
-                        FALSE);
-
-  simple = (GSimpleAsyncResult *)res;
+  g_free (afp_handle->filename);
+  g_free (afp_handle->tmp_filename);
   
-  if (g_simple_async_result_propagate_error (simple, error))
-    return FALSE;
-
-  return TRUE;
+  g_slice_free (AfpHandle, afp_handle);
 }
 
 /*
@@ -1679,12 +174,12 @@ copy_data_free (CopyData *copy_data)
 static void
 copy_copy_file_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobCopy *job = G_VFS_JOB_COPY (user_data);
 
   GError *err = NULL;
   
-  if (!copy_file_finish (afp_backend, res, &err))
+  if (!g_vfs_afp_volume_copy_file_finish (volume, res, &err))
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
     g_error_free (err);
@@ -1697,20 +192,21 @@ copy_copy_file_cb (GObject *source_object, GAsyncResult *res, gpointer user_data
 static void
 copy_delete_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobCopy *job = G_VFS_JOB_COPY (user_data);
+  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (job->backend);
 
   GError *err = NULL;
   
-  if (!delete_finish (afp_backend, res, &err))
+  if (!g_vfs_afp_volume_delete_finish (volume, res, &err))
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
     g_error_free (err);
     return;
   }
 
-  copy_file (afp_backend, job->source, job->destination,
-             G_VFS_JOB (job)->cancellable, copy_copy_file_cb, job);
+  g_vfs_afp_volume_copy_file (afp_backend->volume, job->source, job->destination,
+                              G_VFS_JOB (job)->cancellable, copy_copy_file_cb, job);
 }
 
 static void
@@ -1726,7 +222,7 @@ do_copy (CopyData *copy_data)
   gboolean dest_exists;
   gboolean dest_is_dir;
   
-  info = get_filedir_parms_finish (afp_backend, copy_data->source_parms_res, &err);
+  info = g_vfs_afp_volume_get_filedir_parms_finish (afp_backend->volume, copy_data->source_parms_res, &err);
   if (!info)
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
@@ -1741,7 +237,7 @@ do_copy (CopyData *copy_data)
   source_is_dir = g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY ? TRUE : FALSE;
   g_object_unref (info);
 
-  info = get_filedir_parms_finish (afp_backend, copy_data->dest_parms_res, &err);
+  info = g_vfs_afp_volume_get_filedir_parms_finish (afp_backend->volume, copy_data->dest_parms_res, &err);
   if (!info)
   {
     if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
@@ -1798,13 +294,13 @@ do_copy (CopyData *copy_data)
 
   if (dest_exists)
   {
-    delete (afp_backend, job->destination, G_VFS_JOB (job)->cancellable,
-            copy_delete_cb, job);
+    g_vfs_afp_volume_delete (afp_backend->volume, job->destination,
+                             G_VFS_JOB (job)->cancellable, copy_delete_cb, job);
   }
   else
   {
-    copy_file (afp_backend, job->source, job->destination,
-               G_VFS_JOB (job)->cancellable, copy_copy_file_cb, job);
+    g_vfs_afp_volume_copy_file (afp_backend->volume, job->source, job->destination,
+                                G_VFS_JOB (job)->cancellable, copy_copy_file_cb, job);
   }
   
 done:
@@ -1848,15 +344,17 @@ try_copy (GVfsBackend *backend,
   copy_data = g_slice_new0 (CopyData);
   copy_data->job = job;
 
-  get_filedir_parms (afp_backend, source, AFP_FILEDIR_BITMAP_ATTRIBUTE_BIT,
-                     AFP_FILEDIR_BITMAP_ATTRIBUTE_BIT,
-                     G_VFS_JOB (job)->cancellable, copy_get_source_parms_cb,
-                     copy_data);
+  g_vfs_afp_volume_get_filedir_parms (afp_backend->volume, source,
+                                      AFP_FILEDIR_BITMAP_ATTRIBUTE_BIT,
+                                      AFP_FILEDIR_BITMAP_ATTRIBUTE_BIT,
+                                      G_VFS_JOB (job)->cancellable, copy_get_source_parms_cb,
+                                      copy_data);
 
-  get_filedir_parms (afp_backend, destination, AFP_FILEDIR_BITMAP_ATTRIBUTE_BIT,
-                     AFP_FILEDIR_BITMAP_ATTRIBUTE_BIT,
-                     G_VFS_JOB (job)->cancellable, copy_get_dest_parms_cb,
-                     copy_data);  
+  g_vfs_afp_volume_get_filedir_parms (afp_backend->volume, destination,
+                                      AFP_FILEDIR_BITMAP_ATTRIBUTE_BIT,
+                                      AFP_FILEDIR_BITMAP_ATTRIBUTE_BIT,
+                                      G_VFS_JOB (job)->cancellable, copy_get_dest_parms_cb,
+                                      copy_data);
 
   return TRUE;
 }
@@ -1864,12 +362,12 @@ try_copy (GVfsBackend *backend,
 static void
 move_move_and_rename_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobMove *job = G_VFS_JOB_MOVE (user_data);
 
   GError *err = NULL;
   
-  if (!move_and_rename_finish (afp_backend, res, &err))
+  if (!g_vfs_afp_volume_move_and_rename_finish (volume, res, &err))
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
     g_error_free (err);
@@ -1882,21 +380,21 @@ move_move_and_rename_cb (GObject *source_object, GAsyncResult *res, gpointer use
 static void
 move_delete_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobMove *job = G_VFS_JOB_MOVE (user_data);
 
   GError *err = NULL;
   
-  if (!delete_finish (afp_backend, res, &err))
+  if (!g_vfs_afp_volume_delete_finish (volume, res, &err))
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
     g_error_free (err);
     return;
   }
 
-  move_and_rename (afp_backend, job->source, job->destination,
-                   G_VFS_JOB (job)->cancellable, move_move_and_rename_cb,
-                   job);
+  g_vfs_afp_volume_move_and_rename (volume, job->source, job->destination,
+                                    G_VFS_JOB (job)->cancellable, move_move_and_rename_cb,
+                                    job);
 }
 
 typedef struct
@@ -1928,7 +426,8 @@ do_move (MoveData *move_data)
   gboolean dest_exists;
   gboolean dest_is_dir;
   
-  info = get_filedir_parms_finish (afp_backend, move_data->source_parms_res, &err);
+  info = g_vfs_afp_volume_get_filedir_parms_finish (afp_backend->volume,
+                                                    move_data->source_parms_res, &err);
   if (!info)
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
@@ -1939,7 +438,8 @@ do_move (MoveData *move_data)
   source_is_dir = g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY ? TRUE : FALSE;
   g_object_unref (info);
 
-  info = get_filedir_parms_finish (afp_backend, move_data->dest_parms_res, &err);
+  info = g_vfs_afp_volume_get_filedir_parms_finish (afp_backend->volume, 
+                                                    move_data->dest_parms_res, &err);
   if (!info)
   {
     if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
@@ -1984,13 +484,13 @@ do_move (MoveData *move_data)
       goto done;
     }
 
-    delete (afp_backend, job->destination, G_VFS_JOB (job)->cancellable,
-            move_delete_cb, job);
+    g_vfs_afp_volume_delete (afp_backend->volume, job->destination,
+                             G_VFS_JOB (job)->cancellable, move_delete_cb, job);
   }
   else
-    move_and_rename (afp_backend, job->source, job->destination,
-                     G_VFS_JOB (job)->cancellable, move_move_and_rename_cb,
-                     job);
+    g_vfs_afp_volume_move_and_rename (afp_backend->volume, job->source, job->destination,
+                                      G_VFS_JOB (job)->cancellable, move_move_and_rename_cb,
+                                      job);
 
 done:
   free_move_data (move_data);
@@ -2033,15 +533,17 @@ try_move (GVfsBackend *backend,
   move_data = g_slice_new0 (MoveData);
   move_data->job = job;
   
-  get_filedir_parms (afp_backend, source, AFP_FILEDIR_BITMAP_ATTRIBUTE_BIT,
-                     AFP_FILEDIR_BITMAP_ATTRIBUTE_BIT,
-                     G_VFS_JOB (job)->cancellable, move_get_source_parms_cb,
-                     move_data);
+  g_vfs_afp_volume_get_filedir_parms (afp_backend->volume, source,
+                                      AFP_FILEDIR_BITMAP_ATTRIBUTE_BIT,
+                                      AFP_FILEDIR_BITMAP_ATTRIBUTE_BIT,
+                                      G_VFS_JOB (job)->cancellable, move_get_source_parms_cb,
+                                      move_data);
 
-  get_filedir_parms (afp_backend, destination, AFP_FILEDIR_BITMAP_ATTRIBUTE_BIT,
-                     AFP_FILEDIR_BITMAP_ATTRIBUTE_BIT,
-                     G_VFS_JOB (job)->cancellable, move_get_dest_parms_cb,
-                     move_data);  
+  g_vfs_afp_volume_get_filedir_parms (afp_backend->volume, destination,
+                                      AFP_FILEDIR_BITMAP_ATTRIBUTE_BIT,
+                                      AFP_FILEDIR_BITMAP_ATTRIBUTE_BIT,
+                                      G_VFS_JOB (job)->cancellable, move_get_dest_parms_cb,
+                                      move_data);  
 
   return TRUE;
 }
@@ -2049,60 +551,19 @@ try_move (GVfsBackend *backend,
 static void
 rename_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsAfpConnection *afp_conn = G_VFS_AFP_CONNECTION (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobSetDisplayName *job = G_VFS_JOB_SET_DISPLAY_NAME (user_data);
 
-  GVfsAfpReply *reply;
   GError *err = NULL;
-  AfpResultCode res_code;
   char *dirname, *newpath;
 
-  reply = g_vfs_afp_connection_send_command_finish (afp_conn, res, &err);
-  if (!reply)
+  if (!g_vfs_afp_volume_rename_finish (volume, res, &err))
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
     g_error_free (err);
     return;
   }
 
-  res_code = g_vfs_afp_reply_get_result_code (reply);
-  g_object_unref (reply);
-
-  if (res_code != AFP_RESULT_NO_ERROR)
-  {
-    switch (res_code)
-    {
-      case AFP_RESULT_ACCESS_DENIED:
-        g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
-                                  _("Permission denied"));
-        break;
-      case AFP_RESULT_CANT_RENAME:
-        g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_INVALID_FILENAME,
-                                  _("Can't rename volume"));
-        break;
-      case AFP_RESULT_OBJECT_EXISTS:
-        g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_EXISTS,
-                                  _("Object with that name already exists"));
-        break;
-      case AFP_RESULT_OBJECT_LOCKED:
-        g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_FAILED,
-                                  _("Target object is marked as not renameable (RenameInhibit)"));
-        break;
-      case AFP_RESULT_OBJECT_NOT_FOUND:
-        g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
-                                  _("Target object doesn't exist"));
-        break;
-      case AFP_RESULT_VOL_LOCKED:
-        g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
-                                  _("Volume is read-only"));
-        break;
-      default:
-        job_failed_from_afp_result_code (G_VFS_JOB (job), res_code);
-        break;
-    }
-    return;
-  }
-
   dirname = g_path_get_dirname (job->filename);
   newpath = g_build_filename (dirname, job->display_name, NULL);
   g_vfs_job_set_display_name_set_new_path (job, newpath);
@@ -2112,53 +573,6 @@ rename_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 
   g_vfs_job_succeeded (G_VFS_JOB (job));
 }
-
-static void
-set_display_name_get_filedir_parms_cb (GObject      *source_object,
-                                       GAsyncResult *res,
-                                       gpointer      user_data)
-{
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
-  GVfsJobSetDisplayName *job = G_VFS_JOB_SET_DISPLAY_NAME (user_data);
-
-  GFileInfo *info;
-  GError *err = NULL;
-
-  guint32 dir_id;
-  GVfsAfpCommand *comm;
-  char *basename;
-
-  info = get_filedir_parms_finish (afp_backend, res, &err);
-  if (!info)
-  {
-    g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
-    g_error_free (err);
-    return;
-  }
-
-  dir_id = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_AFP_PARENT_DIR_ID);
-  g_object_unref (info);
-
-  comm = g_vfs_afp_command_new (AFP_COMMAND_RENAME);
-  /* pad byte */
-  g_vfs_afp_command_put_byte (comm, 0);
-  /* Volume ID */
-  g_vfs_afp_command_put_uint16 (comm, afp_backend->volume_id);
-  /* Directory ID */
-  g_vfs_afp_command_put_uint32 (comm, dir_id);
-
-  /* Pathname */
-  basename = g_path_get_basename (job->filename);
-  put_pathname (comm, basename);
-  g_free (basename);
-
-  /* NewName */
-  put_pathname (comm, job->display_name);
-
-  g_vfs_afp_connection_send_command (afp_backend->server->conn, comm, NULL,
-                                     rename_cb, G_VFS_JOB (job)->cancellable, job);
-  g_object_unref (comm);
-}
   
 static gboolean
 try_set_display_name (GVfsBackend *backend,
@@ -2175,115 +589,29 @@ try_set_display_name (GVfsBackend *backend,
     return TRUE;
   }
 
-  get_filedir_parms (afp_backend, filename, AFP_FILEDIR_BITMAP_PARENT_DIR_ID_BIT,
-                     AFP_FILEDIR_BITMAP_PARENT_DIR_ID_BIT,
-                     G_VFS_JOB (job)->cancellable,
-                     set_display_name_get_filedir_parms_cb, job);
+  g_vfs_afp_volume_rename (afp_backend->volume, filename, display_name,
+                           G_VFS_JOB (job)->cancellable, rename_cb, job);
   return TRUE;
 }
 
 static void
-make_directory_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+create_directory_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsAfpConnection *afp_conn = G_VFS_AFP_CONNECTION (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobMakeDirectory *job = G_VFS_JOB_MAKE_DIRECTORY (user_data);
 
-  GVfsAfpReply *reply;
   GError *err = NULL;
-  AfpResultCode res_code;
 
-  reply = g_vfs_afp_connection_send_command_finish (afp_conn, res, &err);
-  if (!reply)
+  if (!g_vfs_afp_volume_create_directory_finish (volume, res, &err))
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
     g_error_free (err);
     return;
   }
 
-  res_code = g_vfs_afp_reply_get_result_code (reply);
-  g_object_unref (reply);
-
-  if (res_code != AFP_RESULT_NO_ERROR)
-  {
-    switch (res_code)
-    {
-      case AFP_RESULT_ACCESS_DENIED:
-        g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
-                                  _("Permission denied"));
-        break;
-      case AFP_RESULT_DISK_FULL:
-        g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NO_SPACE,
-                                  _("Not enough space on volume"));
-        break;
-      case AFP_RESULT_FLAT_VOL:
-        g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
-                                  _("Volume is flat and doesn't support directories"));
-        break;
-      case AFP_RESULT_OBJECT_NOT_FOUND:
-        g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
-                                  _("Ancestor directory doesn't exist"));
-        break;
-      case AFP_RESULT_OBJECT_EXISTS:
-        g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_EXISTS,
-                                  _("Target directory already exists"));
-        break;
-      case AFP_RESULT_VOL_LOCKED:
-        g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
-                                  _("Volume is read-only"));
-        break;
-      default:
-        job_failed_from_afp_result_code (G_VFS_JOB (job), res_code);
-        break;
-    }
-    return;
-  }
-
   g_vfs_job_succeeded (G_VFS_JOB (job));
 }
 
-static void
-make_directory_get_filedir_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
-{
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
-  GVfsJobMakeDirectory *job = G_VFS_JOB_MAKE_DIRECTORY (user_data);
-
-  GFileInfo *info;
-  GError *err = NULL;
-
-  guint32 dir_id;
-  char *basename;
-  GVfsAfpCommand *comm;
-  
-  info = get_filedir_parms_finish (afp_backend, res, &err);
-  if (!info)
-  {
-    g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
-    g_error_free (err);
-    return;
-  }
-
-  dir_id = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_AFP_NODE_ID);
-  g_object_unref (info);
-
-  comm = g_vfs_afp_command_new (AFP_COMMAND_CREATE_DIR);
-  /* pad byte */
-  g_vfs_afp_command_put_byte (comm, 0);
-  /* Volume ID */
-  g_vfs_afp_command_put_uint16 (comm, afp_backend->volume_id);
-  /* Directory ID */
-  g_vfs_afp_command_put_uint32 (comm, dir_id);
-
-  /* Pathname */
-  basename = g_path_get_basename (job->filename);
-  put_pathname (comm, basename);
-  g_free (basename);
-
-  g_vfs_afp_connection_send_command (afp_backend->server->conn, comm, NULL,
-                                     make_directory_cb,
-                                     G_VFS_JOB (job)->cancellable, job);
-  g_object_unref (comm);
-}
-
 static gboolean 
 try_make_directory (GVfsBackend *backend,
                     GVfsJobMakeDirectory *job,
@@ -2291,26 +619,21 @@ try_make_directory (GVfsBackend *backend,
 {
   GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
 
-  char *dirname;
-  
-  dirname = g_path_get_dirname (filename);
-  get_filedir_parms (afp_backend, dirname, 0, AFP_DIR_BITMAP_NODE_ID_BIT,
-                     G_VFS_JOB (job)->cancellable, make_directory_get_filedir_parms_cb,
-                     job);
-  g_free (dirname);
-  
+  g_vfs_afp_volume_create_directory (afp_backend->volume, job->filename,
+                                     G_VFS_JOB (job)->cancellable,
+                                     create_directory_cb, job);
   return TRUE;
 }
 
 static void
 delete_delete_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobDelete *job = G_VFS_JOB_DELETE (user_data);
 
   GError *err = NULL;
 
-  if (!delete_finish (afp_backend, res, &err))
+  if (!g_vfs_afp_volume_delete_finish (volume, res, &err))
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
     g_error_free (err);
@@ -2327,58 +650,30 @@ try_delete (GVfsBackend *backend,
 {
   GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
 
-  delete (afp_backend, filename, G_VFS_JOB (job)->cancellable,
-          delete_delete_cb, job);
+  g_vfs_afp_volume_delete (afp_backend->volume, filename,
+                           G_VFS_JOB (job)->cancellable, delete_delete_cb, job);
   
   return TRUE;
 }
 
 static void
-write_ext_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+write_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsAfpConnection *afp_conn = G_VFS_AFP_CONNECTION (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobWrite *job = G_VFS_JOB_WRITE (user_data);
   AfpHandle *afp_handle = (AfpHandle *)job->handle;
 
-  GVfsAfpReply *reply;
   GError *err = NULL;
-  AfpResultCode res_code;
   gint64 last_written;
   gsize written_size;
 
-  reply = g_vfs_afp_connection_send_command_finish (afp_conn, res, &err);
-  if (!reply)
+  if (!g_vfs_afp_volume_write_to_fork_finish (volume, res, &last_written, &err))
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
     g_error_free (err);
     return;
   }
 
-  res_code = g_vfs_afp_reply_get_result_code (reply);
-  if (!(res_code == AFP_RESULT_NO_ERROR || res_code == AFP_RESULT_LOCK_ERR))
-  {
-    g_object_unref (reply);
-
-    switch (res_code)
-    {
-      case AFP_RESULT_ACCESS_DENIED:
-        g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_FAILED,
-                                  _("File is not open for write access"));
-        break;
-      case AFP_RESULT_DISK_FULL:
-        g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NO_SPACE,
-                                  _("Not enough space on volume"));
-        break;
-      default:
-        job_failed_from_afp_result_code (G_VFS_JOB (job), res_code);
-        break;
-    }
-    return;
-  }
-
-  g_vfs_afp_reply_read_int64 (reply, &last_written);
-  g_object_unref (reply);
-
   written_size = last_written - afp_handle->offset;
   afp_handle->offset = last_written;
 
@@ -2399,27 +694,9 @@ try_write (GVfsBackend *backend,
   GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
   AfpHandle *afp_handle = (AfpHandle *)handle;
 
-  GVfsAfpCommand *comm;
-  guint32 req_count;
-
-  comm = g_vfs_afp_command_new (AFP_COMMAND_WRITE_EXT);
-  /* StartEndFlag = 0 */
-  g_vfs_afp_command_put_byte (comm, 0);
-
-  /* OForkRefNum */
-  g_vfs_afp_command_put_int16 (comm, afp_handle->fork_refnum);
-  /* Offset */
-  g_vfs_afp_command_put_int64 (comm, afp_handle->offset);
-  /* ReqCount */
-  req_count = MIN (buffer_size, G_MAXUINT32);
-  g_vfs_afp_command_put_int64 (comm, req_count);
-
-  g_vfs_afp_command_set_buffer (comm, buffer, buffer_size);
-
-  g_vfs_afp_connection_send_command (afp_backend->server->conn, comm, NULL,
-                                     write_ext_cb, G_VFS_JOB (job)->cancellable,
-                                     job);
-  g_object_unref (comm);
+  g_vfs_afp_volume_write_to_fork (afp_backend->volume, afp_handle->fork_refnum,
+                                  buffer, buffer_size, afp_handle->offset,
+                                  G_VFS_JOB (job)->cancellable, write_cb, job);
 
   return TRUE;
 }
@@ -2427,7 +704,7 @@ try_write (GVfsBackend *backend,
 static void
 seek_on_write_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobSeekWrite *job = G_VFS_JOB_SEEK_WRITE (user_data);
   AfpHandle *afp_handle = (AfpHandle *)job->handle;
 
@@ -2435,7 +712,7 @@ seek_on_write_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
   GFileInfo *info;
   gsize size;
 
-  info = get_fork_parms_finish (afp_backend, res, &err);
+  info = g_vfs_afp_volume_get_fork_parms_finish (volume, res, &err);
   if (!info)
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
@@ -2504,9 +781,9 @@ try_seek_on_write (GVfsBackend *backend,
   
   else
   {
-    get_fork_parms (afp_backend, afp_handle->fork_refnum,
-                    AFP_FILE_BITMAP_EXT_DATA_FORK_LEN_BIT,
-                    G_VFS_JOB (job)->cancellable, seek_on_write_cb, job);
+    g_vfs_afp_volume_get_fork_parms (afp_backend->volume, afp_handle->fork_refnum,
+                                     AFP_FILE_BITMAP_EXT_DATA_FORK_LEN_BIT,
+                                     G_VFS_JOB (job)->cancellable, seek_on_write_cb, job);
   }
     
   return TRUE;
@@ -2515,7 +792,7 @@ try_seek_on_write (GVfsBackend *backend,
 static void
 seek_on_read_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobSeekRead *job = G_VFS_JOB_SEEK_READ (user_data);
   AfpHandle *afp_handle = (AfpHandle *)job->handle;
 
@@ -2523,7 +800,7 @@ seek_on_read_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
   GFileInfo *info;
   gsize size;
 
-  info = get_fork_parms_finish (afp_backend, res, &err);
+  info = g_vfs_afp_volume_get_fork_parms_finish (volume, res, &err);
   if (!info)
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
@@ -2566,59 +843,32 @@ try_seek_on_read (GVfsBackend *backend,
   GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
   AfpHandle *afp_handle = (AfpHandle *)handle;
 
-  get_fork_parms (afp_backend, afp_handle->fork_refnum,
-                  AFP_FILE_BITMAP_EXT_DATA_FORK_LEN_BIT,
-                  G_VFS_JOB (job)->cancellable, seek_on_read_cb, job);
+  g_vfs_afp_volume_get_fork_parms (afp_backend->volume, afp_handle->fork_refnum,
+                                   AFP_FILE_BITMAP_EXT_DATA_FORK_LEN_BIT,
+                                   G_VFS_JOB (job)->cancellable, seek_on_read_cb, job);
   
   return TRUE;
 }
 
 static void
-read_ext_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+read_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsAfpConnection *afp_conn = G_VFS_AFP_CONNECTION (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobRead *job = G_VFS_JOB_READ (user_data);
   AfpHandle *afp_handle = (AfpHandle *)job->handle;
 
-  GVfsAfpReply *reply;
   GError *err = NULL;
-  AfpResultCode res_code;
-  gsize size;
+  gsize bytes_read;
 
-  reply = g_vfs_afp_connection_send_command_finish (afp_conn, res, &err);
-  if (!reply)
+  if (!g_vfs_afp_volume_read_from_fork_finish (volume, res, &bytes_read, &err))
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
     g_error_free (err);
     return;
   }
 
-  res_code = g_vfs_afp_reply_get_result_code (reply);
-  if (!(res_code == AFP_RESULT_NO_ERROR || res_code == AFP_RESULT_EOF_ERR
-        || res_code == AFP_RESULT_LOCK_ERR))
-  {
-    g_object_unref (reply);
-
-    switch (res_code)
-    {
-      case AFP_RESULT_ACCESS_DENIED:
-        g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_FAILED,
-                                  _("File is not open for read access"));
-        break;
-      default:
-        job_failed_from_afp_result_code (G_VFS_JOB (job), res_code);
-        break;
-    }
-    
-    return;
-  }
-
-  size = g_vfs_afp_reply_get_size (reply);
-
-  afp_handle->offset += size;
-  g_vfs_job_read_set_size (job, size);
-
-  g_object_unref (reply);
+  afp_handle->offset += bytes_read;
+  g_vfs_job_read_set_size (job, bytes_read);
   
   g_vfs_job_succeeded (G_VFS_JOB (job));
 }
@@ -2632,40 +882,23 @@ try_read (GVfsBackend *backend,
 {
   GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
   AfpHandle *afp_handle = (AfpHandle *)handle;
-  
-  GVfsAfpCommand *comm;
-  guint32 req_count;
-
-  comm = g_vfs_afp_command_new (AFP_COMMAND_READ_EXT);
-  /* pad byte */
-  g_vfs_afp_command_put_byte (comm, 0);
-
-  /* OForkRefNum */
-  g_vfs_afp_command_put_int16 (comm, afp_handle->fork_refnum);
-  /* Offset */
-  g_vfs_afp_command_put_int64 (comm, afp_handle->offset);
-  /* ReqCount */
-  req_count = MIN (bytes_requested, G_MAXUINT32);
-  g_vfs_afp_command_put_int64 (comm, req_count);
-
-  g_vfs_afp_connection_send_command (afp_backend->server->conn, comm, buffer,
-                                      read_ext_cb, G_VFS_JOB (job)->cancellable,
-                                      job);
-  g_object_unref (comm);
 
+  g_vfs_afp_volume_read_from_fork (afp_backend->volume, afp_handle->fork_refnum,
+                                   buffer, bytes_requested, afp_handle->offset,
+                                   G_VFS_JOB (job)->cancellable, read_cb, job);
   return TRUE;
 }
 
 static void
 close_replace_get_filedir_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobCloseWrite *job = G_VFS_JOB_CLOSE_WRITE (user_data);
 
   GFileInfo *info;
   GError *err = NULL;
 
-  info = get_filedir_parms_finish (afp_backend, res, &err);
+  info = g_vfs_afp_volume_get_filedir_parms_finish (volume, res, &err);
   if (!info)
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
@@ -2682,23 +915,23 @@ close_replace_get_filedir_parms_cb (GObject *source_object, GAsyncResult *res, g
 static void
 close_replace_delete_backup_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   AfpHandle *afp_handle = (AfpHandle *)user_data;
 
   char *backup_name;
 
   /* We ignore all errors and just try to rename the temporary file anyway */
   backup_name = g_strconcat (afp_handle->filename, "~", NULL);
-  
-  move_and_rename (afp_backend, afp_handle->tmp_filename, backup_name, NULL,
-                   NULL, NULL);
+
+  g_vfs_afp_volume_move_and_rename (volume, afp_handle->tmp_filename, backup_name,
+                                    NULL, NULL, NULL);
   afp_handle_free (afp_handle);
 }
 
 static void
 close_replace_close_fork_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   AfpHandle *afp_handle = (AfpHandle *)user_data;
 
   if (afp_handle->make_backup)
@@ -2706,15 +939,15 @@ close_replace_close_fork_cb (GObject *source_object, GAsyncResult *res, gpointer
     char *backup_name = g_strconcat (afp_handle->filename, "~", NULL);
     
     /* Delete old backup */
-    delete (afp_backend, backup_name, NULL, close_replace_delete_backup_cb,
-            afp_handle);
+    g_vfs_afp_volume_delete (volume, backup_name, NULL,
+                             close_replace_delete_backup_cb, afp_handle);
     g_free (backup_name);
   }
 
   else
   {
     /* Delete temporary file */
-    delete (afp_backend, afp_handle->tmp_filename, NULL, NULL, NULL);
+    g_vfs_afp_volume_delete (volume, afp_handle->tmp_filename, NULL, NULL, NULL);
     
     afp_handle_free (afp_handle);
   }
@@ -2723,17 +956,13 @@ close_replace_close_fork_cb (GObject *source_object, GAsyncResult *res, gpointer
 static void
 close_replace_exchange_files_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsAfpConnection *afp_conn = G_VFS_AFP_CONNECTION (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobCloseWrite *job = G_VFS_JOB_CLOSE_WRITE (user_data);
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (job->backend);
   AfpHandle *afp_handle = (AfpHandle *)job->handle;
 
-  GVfsAfpReply *reply;
   GError *err = NULL;
-  AfpResultCode res_code;
 
-  reply = g_vfs_afp_connection_send_command_finish (afp_conn, res, &err);
-  if (!reply)
+  if (!g_vfs_afp_volume_exchange_files_finish (volume, res, &err))
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
     g_error_free (err);
@@ -2742,51 +971,26 @@ close_replace_exchange_files_cb (GObject *source_object, GAsyncResult *res, gpoi
   }
 
   /* Close fork and remove/rename the temporary file even if the exchange failed */
-  close_fork (afp_backend, afp_handle->fork_refnum, G_VFS_JOB (job)->cancellable,
-              close_replace_close_fork_cb, job->handle);
-  
-  res_code = g_vfs_afp_reply_get_result_code (reply);
-  g_object_unref (reply);
+  g_vfs_afp_volume_close_fork (volume, afp_handle->fork_refnum,
+                               G_VFS_JOB (job)->cancellable,
+                               close_replace_close_fork_cb, job->handle);
   
-  if (res_code != AFP_RESULT_NO_ERROR)
-  {
-    switch (res_code)
-    {
-      case AFP_RESULT_ACCESS_DENIED:
-        g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_FAILED,
-                                  _("Permission denied"));
-        break;
-      case AFP_RESULT_ID_NOT_FOUND:
-        g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
-                                  _("File doesn't exist"));
-        break;
-      case AFP_RESULT_OBJECT_TYPE_ERR:
-        g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
-                                  _("File is directory"));
-        break;   
-      default:
-        job_failed_from_afp_result_code (G_VFS_JOB (job), res_code);
-        break;
-    }
-    return;
-  }
-
   /* Get ETAG */
-  get_filedir_parms (afp_backend, afp_handle->filename,
-                     AFP_FILE_BITMAP_MOD_DATE_BIT, 0,
-                     G_VFS_JOB (job)->cancellable,
-                     close_replace_get_filedir_parms_cb, job);
+  g_vfs_afp_volume_get_filedir_parms (volume, afp_handle->filename,
+                                      AFP_FILE_BITMAP_MOD_DATE_BIT, 0,
+                                      G_VFS_JOB (job)->cancellable,
+                                      close_replace_get_filedir_parms_cb, job);
 }
 
 static void
-close_write_close_fork_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+close_fork_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
-  GVfsJobCloseWrite *job = G_VFS_JOB_CLOSE_WRITE (user_data);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
+  GVfsJob *job = G_VFS_JOB (user_data);
 
   GError *err = NULL;
 
-  if (!close_fork_finish (afp_backend, res, &err))
+  if (!g_vfs_afp_volume_close_fork_finish (volume, res, &err))
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
     g_error_free (err);
@@ -2797,9 +1001,20 @@ close_write_close_fork_cb (GObject *source_object, GAsyncResult *res, gpointer u
 }
 
 static void
+close_fork (GVfsAfpVolume  *volume,
+            GVfsJob        *job,
+            AfpHandle      *afp_handle)
+{
+  g_vfs_afp_volume_close_fork (volume, afp_handle->fork_refnum,
+                               G_VFS_JOB (job)->cancellable,
+                               close_fork_cb, job);
+  afp_handle_free (afp_handle);
+}
+
+static void
 close_write_get_fork_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobCloseWrite *job = G_VFS_JOB_CLOSE_WRITE (user_data);
 
   AfpHandle *afp_handle = (AfpHandle *)job->handle;
@@ -2807,7 +1022,7 @@ close_write_get_fork_parms_cb (GObject *source_object, GAsyncResult *res, gpoint
   GError *err = NULL;
   GFileInfo *info;
 
-  info = get_fork_parms_finish (afp_backend, res, &err);
+  info = g_vfs_afp_volume_get_fork_parms_finish (volume, res, &err);
   if (!info)
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
@@ -2818,26 +1033,20 @@ close_write_get_fork_parms_cb (GObject *source_object, GAsyncResult *res, gpoint
   }
 
   g_vfs_job_close_write_set_etag (job, g_file_info_get_etag (info));
-
-  close_fork (afp_backend, afp_handle->fork_refnum, G_VFS_JOB (job)->cancellable,
-              close_write_close_fork_cb, job);
-  afp_handle_free (afp_handle);
+  close_fork (volume, G_VFS_JOB (job), afp_handle);
 }
   
 static void
-close_replace_set_fork_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
+close_replace_set_fork_size_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsAfpConnection *afp_conn = G_VFS_AFP_CONNECTION (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobCloseWrite *job = G_VFS_JOB_CLOSE_WRITE (user_data);
   GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (job->backend);
   AfpHandle *afp_handle = (AfpHandle *)job->handle;
 
-  GVfsAfpReply *reply;
   GError *err = NULL;
-  AfpResultCode res_code;
 
-  reply = g_vfs_afp_connection_send_command_finish (afp_conn, res, &err);
-  if (!reply)
+  if (!g_vfs_afp_volume_set_fork_size_finish (volume, res, &err))
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
     g_error_free (err);
@@ -2845,36 +1054,11 @@ close_replace_set_fork_parms_cb (GObject *source_object, GAsyncResult *res, gpoi
     return;
   }
 
-  res_code = g_vfs_afp_reply_get_result_code (reply);
-  g_object_unref (reply);
-  if (res_code != AFP_RESULT_NO_ERROR)
-  {
-    switch (res_code)
-    {
-      case AFP_RESULT_ACCESS_DENIED:
-        g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_FAILED,
-                                  _("Permission denied"));
-        break;
-      case AFP_RESULT_DISK_FULL:
-        g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NO_SPACE,
-                                  _("Not enough space on volume"));
-        break;
-      case AFP_RESULT_LOCK_ERR:
-        g_vfs_job_failed_literal (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_FAILED,
-                                  _("Range lock conflict exists"));
-        break;
-      default:
-        job_failed_from_afp_result_code (G_VFS_JOB (job), res_code);
-        break;
-    }
-    afp_handle_free (afp_handle);
-    return;
-  }
-
   /* Get ETAG */
-  get_fork_parms (afp_backend, afp_handle->fork_refnum, AFP_FILE_BITMAP_MOD_DATE_BIT,
-                  G_VFS_JOB (job)->cancellable, close_write_get_fork_parms_cb,
-                  job);
+  g_vfs_afp_volume_get_fork_parms (afp_backend->volume, afp_handle->fork_refnum,
+                                   AFP_FILE_BITMAP_MOD_DATE_BIT,
+                                   G_VFS_JOB (job)->cancellable,
+                                   close_write_get_fork_parms_cb, job);
 }
 
 static gboolean
@@ -2887,78 +1071,29 @@ try_close_write (GVfsBackend *backend,
   
   if (afp_handle->type == AFP_HANDLE_TYPE_REPLACE_FILE_TEMP)
   {
-    GVfsAfpCommand *comm;
-
-    comm = g_vfs_afp_command_new (AFP_COMMAND_EXCHANGE_FILES);
-    /* pad byte */
-    g_vfs_afp_command_put_byte (comm, 0);
-
-    /* Volume ID */
-    g_vfs_afp_command_put_uint16 (comm, afp_backend->volume_id);
-    /* SourceDirectory ID 2 == / */
-    g_vfs_afp_command_put_uint32 (comm, 2);
-    /* DestDirectory ID 2 == / */
-    g_vfs_afp_command_put_uint32 (comm, 2);
-
-    /* SourcePath */
-    put_pathname (comm, afp_handle->filename);
-    /* DestPath */
-    put_pathname (comm, afp_handle->tmp_filename);
-
-    g_vfs_afp_connection_send_command (afp_backend->server->conn, comm, NULL,
-                                       close_replace_exchange_files_cb,
-                                       G_VFS_JOB (job)->cancellable, job);
-    g_object_unref (comm);
+    g_vfs_afp_volume_exchange_files (afp_backend->volume, afp_handle->filename,
+                                     afp_handle->tmp_filename, 
+                                     G_VFS_JOB (job)->cancellable,
+                                     close_replace_exchange_files_cb, job);
   }
   else if (afp_handle->type == AFP_HANDLE_TYPE_REPLACE_FILE_DIRECT)
   {
-    GVfsAfpCommand *comm;
-
-    comm = g_vfs_afp_command_new (AFP_COMMAND_SET_FORK_PARMS);
-    /* pad byte */
-    g_vfs_afp_command_put_byte (comm, 0);
-
-    /* OForkRefNum */
-    g_vfs_afp_command_put_int16 (comm, afp_handle->fork_refnum);
-    /* Bitmap */
-    g_vfs_afp_command_put_uint16 (comm, AFP_FILE_BITMAP_EXT_DATA_FORK_LEN_BIT);
-    /* ForkLen */
-    g_vfs_afp_command_put_int64 (comm, afp_handle->size);
-
-    g_vfs_afp_connection_send_command (afp_backend->server->conn, comm, NULL,
-                                       close_replace_set_fork_parms_cb,
-                                       G_VFS_JOB (job)->cancellable, job);
-    g_object_unref (comm);
+    g_vfs_afp_volume_set_fork_size (afp_backend->volume, afp_handle->fork_refnum,
+                                    afp_handle->size, G_VFS_JOB (job)->cancellable,
+                                    close_replace_set_fork_size_cb, job);
   }
   else
   {
     /* Get ETAG */
-    get_fork_parms (afp_backend, afp_handle->fork_refnum, AFP_FILE_BITMAP_MOD_DATE_BIT,
-                    G_VFS_JOB (job)->cancellable, close_write_get_fork_parms_cb,
-                    job);
+    g_vfs_afp_volume_get_fork_parms (afp_backend->volume, afp_handle->fork_refnum,
+                                     AFP_FILE_BITMAP_MOD_DATE_BIT,
+                                     G_VFS_JOB (job)->cancellable,
+                                     close_write_get_fork_parms_cb, job);
   }
   
   return TRUE;
 }
 
-static void
-close_read_close_fork_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
-{
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
-  GVfsJobCloseRead *job = G_VFS_JOB_CLOSE_READ (user_data);
-
-  GError *err = NULL;
-  
-  if (!close_fork_finish (afp_backend, res, &err))
-  {
-    g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
-    g_error_free (err);
-    return;
-  }
-
-  g_vfs_job_succeeded (G_VFS_JOB (job));
-}
-
 static gboolean
 try_close_read (GVfsBackend *backend,
                 GVfsJobCloseRead *job,
@@ -2967,9 +1102,7 @@ try_close_read (GVfsBackend *backend,
   GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
   AfpHandle *afp_handle = (AfpHandle *)handle;
 
-  close_fork (afp_backend, afp_handle->fork_refnum, G_VFS_JOB (job)->cancellable,
-              close_read_close_fork_cb, job);
-  afp_handle_free ((AfpHandle *)job->handle);
+  close_fork (afp_backend->volume, G_VFS_JOB (job), afp_handle);
   
   return TRUE;
 }
@@ -2977,21 +1110,22 @@ try_close_read (GVfsBackend *backend,
 static void
 create_open_fork_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobOpenForWrite *job = G_VFS_JOB_OPEN_FOR_WRITE (user_data);
+  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (job->backend);
 
   gint16 fork_refnum;
   GError *err = NULL;
   AfpHandle *afp_handle;
   
-  if (!open_fork_finish (afp_backend, res, &fork_refnum, NULL, &err))
+  if (!g_vfs_afp_volume_open_fork_finish (volume, res, &fork_refnum, NULL, &err))
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
     g_error_free (err);
     return;
   }
 
-  afp_handle = afp_handle_new (fork_refnum);
+  afp_handle = afp_handle_new (afp_backend, fork_refnum);
   afp_handle->type = AFP_HANDLE_TYPE_CREATE_FILE;
   
   g_vfs_job_open_for_write_set_handle (job, (GVfsBackendHandle) afp_handle);
@@ -3004,20 +1138,20 @@ create_open_fork_cb (GObject *source_object, GAsyncResult *res, gpointer user_da
 static void
 create_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobOpenForWrite *job = G_VFS_JOB_OPEN_FOR_WRITE (user_data);
 
   GError *err = NULL;
 
-  if (!create_file_finish (afp_backend, res, &err))
+  if (!g_vfs_afp_volume_create_file_finish (volume, res, &err))
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
     g_error_free (err);
     return;
   }
 
-  open_fork (afp_backend, job->filename, AFP_ACCESS_MODE_WRITE_BIT, 0,
-             G_VFS_JOB (job)->cancellable, create_open_fork_cb, job);
+  g_vfs_afp_volume_open_fork (volume, job->filename, AFP_ACCESS_MODE_WRITE_BIT, 0,
+                              G_VFS_JOB (job)->cancellable, create_open_fork_cb, job);
 }
 
 static gboolean
@@ -3028,8 +1162,8 @@ try_create (GVfsBackend *backend,
 {
   GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
 
-  create_file (afp_backend, filename, FALSE, G_VFS_JOB (job)->cancellable,
-               create_cb, job);
+  g_vfs_afp_volume_create_file (afp_backend->volume, filename, FALSE,
+                                G_VFS_JOB (job)->cancellable, create_cb, job);
 
   return TRUE;
 }
@@ -3037,22 +1171,23 @@ try_create (GVfsBackend *backend,
 static void
 replace_open_fork_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobOpenForWrite *job = G_VFS_JOB_OPEN_FOR_WRITE (user_data);
+  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (job->backend);
 
   gint16 fork_refnum;
   GError *err = NULL;
   AfpHandle *afp_handle;
   char *tmp_filename;
   
-  if (!open_fork_finish (afp_backend, res, &fork_refnum, NULL, &err))
+  if (!g_vfs_afp_volume_open_fork_finish (volume, res, &fork_refnum, NULL, &err))
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
     g_error_free (err);
     return;
   }
 
-  afp_handle = afp_handle_new (fork_refnum);
+  afp_handle = afp_handle_new (afp_backend, fork_refnum);
   tmp_filename = g_object_get_data (G_OBJECT (job), "TempFilename");
   /* Replace using temporary file */
   if (tmp_filename)
@@ -3072,21 +1207,21 @@ replace_open_fork_cb (GObject *source_object, GAsyncResult *res, gpointer user_d
   g_vfs_job_succeeded (G_VFS_JOB (job));
 }
 
-static void replace_create_tmp_file (GVfsBackendAfp *afp_backend, GVfsJobOpenForWrite *job);
+static void replace_create_tmp_file (GVfsAfpVolume *volume, GVfsJobOpenForWrite *job);
 
 static void
 replace_create_tmp_file_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobOpenForWrite *job = G_VFS_JOB_OPEN_FOR_WRITE (user_data);
 
   GError *err = NULL;
   char *tmp_filename;
 
-  if (!create_file_finish (afp_backend, res, &err))
+  if (!g_vfs_afp_volume_create_file_finish (volume, res, &err))
   {
     if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_EXISTS))
-      replace_create_tmp_file (afp_backend, job);
+      replace_create_tmp_file (volume, job);
 
     /* We don't have the necessary permissions to create a temporary file
      * so we try to write directly to the file */
@@ -3102,8 +1237,9 @@ replace_create_tmp_file_cb (GObject *source_object, GAsyncResult *res, gpointer
       else
       {
         g_object_set_data (G_OBJECT (job), "TempFilename", NULL);
-        open_fork (afp_backend, job->filename, AFP_ACCESS_MODE_WRITE_BIT, 0,
-                   G_VFS_JOB (job)->cancellable, replace_open_fork_cb, job);
+        g_vfs_afp_volume_open_fork (volume, job->filename,
+                                    AFP_ACCESS_MODE_WRITE_BIT, 0,
+                                    G_VFS_JOB (job)->cancellable, replace_open_fork_cb, job);
       }
     }
                               
@@ -3117,8 +1253,9 @@ replace_create_tmp_file_cb (GObject *source_object, GAsyncResult *res, gpointer
   }
 
   tmp_filename = g_object_get_data (G_OBJECT (job), "TempFilename");
-  open_fork (afp_backend, tmp_filename, AFP_ACCESS_MODE_WRITE_BIT, 0,
-             G_VFS_JOB (job)->cancellable, replace_open_fork_cb, job);
+  g_vfs_afp_volume_open_fork (volume, tmp_filename,
+                              AFP_ACCESS_MODE_WRITE_BIT, 0,
+                              G_VFS_JOB (job)->cancellable, replace_open_fork_cb, job);
 }
 
 static void
@@ -3132,7 +1269,7 @@ random_chars (char *str, int len)
 }
 
 static void
-replace_create_tmp_file (GVfsBackendAfp *afp_backend, GVfsJobOpenForWrite *job)
+replace_create_tmp_file (GVfsAfpVolume *volume, GVfsJobOpenForWrite *job)
 {
   char basename[] = "~gvfXXXX.tmp";
   char *dir, *tmp_filename;
@@ -3144,20 +1281,22 @@ replace_create_tmp_file (GVfsBackendAfp *afp_backend, GVfsJobOpenForWrite *job)
   g_free (dir);
 
   g_object_set_data_full (G_OBJECT (job), "TempFilename", tmp_filename, g_free);
-  create_file (afp_backend, tmp_filename, FALSE, G_VFS_JOB (job)->cancellable,
-               replace_create_tmp_file_cb, job);
+  g_vfs_afp_volume_create_file (volume, tmp_filename, FALSE,
+                                G_VFS_JOB (job)->cancellable,
+                                replace_create_tmp_file_cb, job);
 }
 
 static void
 replace_get_filedir_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobOpenForWrite *job = G_VFS_JOB_OPEN_FOR_WRITE (user_data);
+  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (job->backend);
 
   GError *err = NULL;
   GFileInfo *info;
 
-  info = get_filedir_parms_finish (afp_backend, res, &err);
+  info = g_vfs_afp_volume_get_filedir_parms_finish (volume, res, &err);
   if (!info)
   {
     /* Create file if it doesn't exist */
@@ -3185,7 +1324,7 @@ replace_get_filedir_parms_cb (GObject *source_object, GAsyncResult *res, gpointe
   }
   else
   {
-    if (afp_backend->vol_attrs_bitmap & AFP_VOLUME_ATTRIBUTES_BITMAP_NO_EXCHANGE_FILES)
+    if (g_vfs_afp_volume_get_attributes (volume) & AFP_VOLUME_ATTRIBUTES_BITMAP_NO_EXCHANGE_FILES)
     {
       /* FIXME: We don't support making backups when we can't use FPExchangeFiles */
       if (job->make_backup)
@@ -3196,12 +1335,13 @@ replace_get_filedir_parms_cb (GObject *source_object, GAsyncResult *res, gpointe
       }
       else
       {
-        open_fork (afp_backend, job->filename, AFP_ACCESS_MODE_WRITE_BIT, 0,
-                   G_VFS_JOB (job)->cancellable, replace_open_fork_cb, job);
+        g_vfs_afp_volume_open_fork (volume, job->filename,
+                                    AFP_ACCESS_MODE_WRITE_BIT, 0,
+                                    G_VFS_JOB (job)->cancellable, replace_open_fork_cb, job);
       }
     }
     else
-      replace_create_tmp_file (afp_backend, job);
+      replace_create_tmp_file (volume, job);
   }
 
   g_object_unref (info);
@@ -3217,16 +1357,17 @@ try_replace (GVfsBackend *backend,
 {
   GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
 
-  get_filedir_parms (afp_backend, filename, AFP_FILE_BITMAP_MOD_DATE_BIT, 0,
-                     G_VFS_JOB (job)->cancellable, replace_get_filedir_parms_cb,
-                     job);
+  g_vfs_afp_volume_get_filedir_parms (afp_backend->volume, filename, 
+                                      AFP_FILE_BITMAP_MOD_DATE_BIT, 0,
+                                      G_VFS_JOB (job)->cancellable,
+                                      replace_get_filedir_parms_cb, job);
   return TRUE;
 }
 
 static void
 append_to_get_fork_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobOpenForWrite *job = G_VFS_JOB_OPEN_FOR_WRITE (user_data);
   AfpHandle *afp_handle = (AfpHandle *)job->backend_handle;
 
@@ -3234,7 +1375,7 @@ append_to_get_fork_parms_cb (GObject *source_object, GAsyncResult *res, gpointer
   GError *err = NULL;
   goffset size;
 
-  info = get_fork_parms_finish (afp_backend, res, &err);
+  info = g_vfs_afp_volume_get_fork_parms_finish (volume, res, &err);
   if (!info)
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err); 
@@ -3256,14 +1397,15 @@ append_to_get_fork_parms_cb (GObject *source_object, GAsyncResult *res, gpointer
 static void
 append_to_open_fork_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobOpenForWrite *job = G_VFS_JOB_OPEN_FOR_WRITE (user_data);
+  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (job->backend);
 
   gint16 fork_refnum;
   GError *err = NULL;
   AfpHandle *afp_handle;
 
-  if (!open_fork_finish (afp_backend, res, &fork_refnum, NULL, &err))
+  if (!g_vfs_afp_volume_open_fork_finish (volume, res, &fork_refnum, NULL, &err))
   {
     /* Create file if it doesn't exist */
     if (g_error_matches (err, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
@@ -3276,14 +1418,14 @@ append_to_open_fork_cb (GObject *source_object, GAsyncResult *res, gpointer user
     return;
   }
 
-  afp_handle = afp_handle_new (fork_refnum);
+  afp_handle = afp_handle_new (afp_backend, fork_refnum);
   afp_handle->type = AFP_HANDLE_TYPE_APPEND_TO_FILE;
   g_vfs_job_open_for_write_set_handle (job, (GVfsBackendHandle) afp_handle);
   
-  get_fork_parms (afp_backend, afp_handle->fork_refnum,
-                  AFP_FILE_BITMAP_EXT_DATA_FORK_LEN_BIT,
-                  G_VFS_JOB (job)->cancellable, append_to_get_fork_parms_cb,
-                  job);
+  g_vfs_afp_volume_get_fork_parms (afp_backend->volume, afp_handle->fork_refnum,
+                                   AFP_FILE_BITMAP_EXT_DATA_FORK_LEN_BIT,
+                                   G_VFS_JOB (job)->cancellable,
+                                   append_to_get_fork_parms_cb, job);
 }
 
 static gboolean
@@ -3294,29 +1436,31 @@ try_append_to (GVfsBackend *backend,
 {
   GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
 
-  open_fork (afp_backend, job->filename, AFP_ACCESS_MODE_WRITE_BIT, 0,
-             G_VFS_JOB (job)->cancellable, append_to_open_fork_cb, job);
+  g_vfs_afp_volume_open_fork (afp_backend->volume, job->filename,
+                              AFP_ACCESS_MODE_WRITE_BIT, 0,
+                              G_VFS_JOB (job)->cancellable, append_to_open_fork_cb, job);
   return TRUE;
 }
 
 static void
 read_open_fork_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobOpenForRead *job = G_VFS_JOB_OPEN_FOR_READ (user_data);
+  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (job->backend);
 
   GError *err = NULL;
   gint16 fork_refnum;
   AfpHandle *afp_handle;
   
-  if (!open_fork_finish (afp_backend, res, &fork_refnum, NULL, &err))
+  if (!g_vfs_afp_volume_open_fork_finish (volume, res, &fork_refnum, NULL, &err))
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
     g_error_free (err);
     return;
   }
 
-  afp_handle = afp_handle_new (fork_refnum);
+  afp_handle = afp_handle_new (afp_backend, fork_refnum);
   afp_handle->type = AFP_HANDLE_TYPE_READ_FILE;
   
   g_vfs_job_open_for_read_set_handle (job, (GVfsBackendHandle) afp_handle);
@@ -3332,8 +1476,9 @@ try_open_for_read (GVfsBackend *backend,
 {
   GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (backend);
 
-  open_fork (afp_backend, filename, AFP_ACCESS_MODE_READ_BIT, 0,
-             G_VFS_JOB (job)->cancellable, read_open_fork_cb, job);
+  g_vfs_afp_volume_open_fork (afp_backend->volume, filename,
+                              AFP_ACCESS_MODE_READ_BIT, 0,
+                              G_VFS_JOB (job)->cancellable, read_open_fork_cb, job);
   return TRUE;
 }
 
@@ -3362,7 +1507,7 @@ create_filedir_bitmap (GVfsBackendAfp *afp_backend, GFileAttributeMatcher *match
       g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_OWNER_GROUP))
       
   {
-    if (afp_backend->vol_attrs_bitmap & AFP_VOLUME_ATTRIBUTES_BITMAP_SUPPORTS_UNIX_PRIVS)
+    if (g_vfs_afp_volume_get_attributes (afp_backend->volume) & AFP_VOLUME_ATTRIBUTES_BITMAP_SUPPORTS_UNIX_PRIVS)
       bitmap |= AFP_FILEDIR_BITMAP_UNIX_PRIVS_BIT;
   }
       
@@ -3403,102 +1548,39 @@ enumerate (GVfsBackendAfp *afp_backend,
 static void
 enumerate_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsAfpConnection *afp_conn = G_VFS_AFP_CONNECTION (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobEnumerate *job = G_VFS_JOB_ENUMERATE (user_data);
   GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (job->backend);
 
-  GVfsAfpReply *reply;
+  GPtrArray *infos;
   GError *err = NULL;
-  AfpResultCode res_code;
-  
-  guint16 file_bitmap;
-  guint16  dir_bitmap;
-  gint16 count, i;
 
-  gint64 start_index, max;
+  guint i;
+  gint64 start_index;
 
-  reply = g_vfs_afp_connection_send_command_finish (afp_conn, res, &err);
-  if (!reply)
+  
+  if (!g_vfs_afp_volume_enumerate_finish (volume, res, &infos, &err))
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
     g_error_free (err);
     return;
   }
 
-  res_code = g_vfs_afp_reply_get_result_code (reply);
-  if (res_code != AFP_RESULT_NO_ERROR)
+  /* No more files */
+  if (!infos)
   {
-    g_object_unref (reply);
-    
-    switch (res_code)
-    {
-      case AFP_RESULT_ACCESS_DENIED:
-        g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
-                          _("Permission denied"));
-        break;
-      case AFP_RESULT_DIR_NOT_FOUND:
-        g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
-                          _("Directory doesn't exist"));
-        break;
-      case AFP_RESULT_OBJECT_TYPE_ERR:
-        g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY,
-                          _("Target object is not a directory"));
-        break;
-      case AFP_RESULT_OBJECT_NOT_FOUND:
-        g_vfs_job_succeeded (G_VFS_JOB (job));
-        g_vfs_job_enumerate_done (job);
-        break;
-      default:
-        job_failed_from_afp_result_code (G_VFS_JOB (job), res_code);
-        break;
-    }
+    g_vfs_job_succeeded (G_VFS_JOB (job));
+    g_vfs_job_enumerate_done (job);
     return;
   }
 
-  g_vfs_afp_reply_read_uint16 (reply, &file_bitmap);
-  g_vfs_afp_reply_read_uint16 (reply, &dir_bitmap);
-
-  g_vfs_afp_reply_read_int16 (reply, &count);
-  for (i = 0; i < count; i++)
-  {
-    goffset start_pos;
-    guint16 struct_length;
-    guint8 FileDir;
-    
-    gboolean directory;
-    guint16 bitmap;
-    GFileInfo *info;
-
-    start_pos = g_vfs_afp_reply_get_pos (reply);
-    
-    g_vfs_afp_reply_read_uint16 (reply, &struct_length);
-    g_vfs_afp_reply_read_byte (reply, &FileDir);
-    /* pad byte */
-    g_vfs_afp_reply_read_byte (reply, NULL);
-
-    directory = (FileDir & 0x80); 
-    bitmap =  directory ? dir_bitmap : file_bitmap;
-    
-    info = g_file_info_new ();
-    fill_info (afp_backend, info, reply, directory, bitmap);
-    g_vfs_job_enumerate_add_info (job, info);
-    g_object_unref (info);
-
-    g_vfs_afp_reply_seek (reply, start_pos + struct_length, G_SEEK_SET);
-  }
-  g_object_unref (reply);
+  for (i = 0; i < infos->len; i++)
+    g_vfs_job_enumerate_add_info (job, g_ptr_array_index (infos, i));
   
   start_index = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (job),
                                                     "start-index"));
-  start_index += count;
-
-  max = (afp_backend->server->version >= AFP_VERSION_3_1) ? G_MAXINT32 : G_MAXINT16;
-  /* Can't enumerate any more files */
-  if (start_index > max)
-  {
-    g_vfs_job_succeeded (G_VFS_JOB (job));
-    g_vfs_job_enumerate_done (job);
-  }
+  start_index += infos->len;
+  g_ptr_array_unref (infos);
 
   enumerate (afp_backend, job, start_index);
 }
@@ -3508,59 +1590,20 @@ enumerate (GVfsBackendAfp *afp_backend,
            GVfsJobEnumerate *job,
            gint32 start_index)
 {
-  GVfsAfpConnection *conn = afp_backend->server->conn;
   const char *filename = job->filename;
   GFileAttributeMatcher *matcher = job->attribute_matcher;
   
-  GVfsAfpCommand *comm;
   guint16 file_bitmap, dir_bitmap;
-
-  if (afp_backend->server->version >= AFP_VERSION_3_1)
-    comm = g_vfs_afp_command_new (AFP_COMMAND_ENUMERATE_EXT2);
-  else
-    comm = g_vfs_afp_command_new (AFP_COMMAND_ENUMERATE_EXT);
   
-  /* pad byte */
-  g_vfs_afp_command_put_byte (comm, 0);
-
-  /* Volume ID */
-  g_vfs_afp_command_put_uint16 (comm, afp_backend->volume_id);
-  /* Directory ID 2 == / */
-  g_vfs_afp_command_put_uint32 (comm, 2);
+  g_object_set_data (G_OBJECT (job), "start-index",
+                     GINT_TO_POINTER (start_index));
 
-  /* File Bitmap */
   file_bitmap = create_file_bitmap (afp_backend, matcher);
-  g_vfs_afp_command_put_uint16 (comm, file_bitmap);
-  
-  /* Dir Bitmap */
   dir_bitmap = create_dir_bitmap (afp_backend, matcher);
-  g_vfs_afp_command_put_uint16 (comm, dir_bitmap);
-
-  /* Req Count */
-  g_vfs_afp_command_put_int16 (comm, ENUMERATE_REQ_COUNT);
-
-  
-  /* StartIndex and MaxReplySize */
-  if (afp_backend->server->version >= AFP_VERSION_3_1)
-  {
-    g_vfs_afp_command_put_int32 (comm, start_index);
-    g_vfs_afp_command_put_int32 (comm, ENUMERATE_EXT2_MAX_REPLY_SIZE);
-  }
-  else
-  {
-    g_vfs_afp_command_put_int16 (comm, start_index);
-    g_vfs_afp_command_put_int16 (comm, ENUMERATE_EXT_MAX_REPLY_SIZE);
-  }
-  
-  /* Pathname */
-  put_pathname (comm, filename);
-
-  g_object_set_data (G_OBJECT (job), "start-index",
-                     GINT_TO_POINTER (start_index));
   
-  g_vfs_afp_connection_send_command (conn, comm, NULL, enumerate_cb,
-                                     G_VFS_JOB (job)->cancellable, job);
-  g_object_unref (comm);
+  g_vfs_afp_volume_enumerate (afp_backend->volume, filename, start_index,
+                              file_bitmap, dir_bitmap,
+                              G_VFS_JOB (job)->cancellable, enumerate_cb, job);
 }
 
 static gboolean
@@ -3587,7 +1630,7 @@ try_query_settable_attributes (GVfsBackend *backend,
 
   list = g_file_attribute_info_list_new ();
 
-  if (afp_backend->vol_attrs_bitmap & AFP_VOLUME_ATTRIBUTES_BITMAP_SUPPORTS_UNIX_PRIVS)
+  if (g_vfs_afp_volume_get_attributes (afp_backend->volume) & AFP_VOLUME_ATTRIBUTES_BITMAP_SUPPORTS_UNIX_PRIVS)
   {
     g_file_attribute_info_list_add (list,
                                     G_FILE_ATTRIBUTE_UNIX_MODE,
@@ -3616,12 +1659,12 @@ try_query_settable_attributes (GVfsBackend *backend,
 static void
 set_attribute_set_unix_privs_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobSetAttribute *job = G_VFS_JOB_SET_ATTRIBUTE (user_data);
 
   GError *err = NULL;
 
-  if (!set_unix_privs_finish (afp_backend, res, &err))
+  if (!g_vfs_afp_volume_set_unix_privs_finish (volume, res, &err))
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
     g_error_free (err);
@@ -3634,7 +1677,7 @@ set_attribute_set_unix_privs_cb (GObject *source_object, GAsyncResult *res, gpoi
 static void
 set_attribute_get_filedir_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobSetAttribute *job = G_VFS_JOB_SET_ATTRIBUTE (user_data);
 
   GFileInfo *info;
@@ -3642,7 +1685,7 @@ set_attribute_get_filedir_parms_cb (GObject *source_object, GAsyncResult *res, g
 
   guint32 uid, gid, permissions, ua_permissions;
 
-  info = get_filedir_parms_finish (afp_backend, res, &err);
+  info = g_vfs_afp_volume_get_filedir_parms_finish (volume, res, &err);
   if (!info)
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
@@ -3665,10 +1708,10 @@ set_attribute_get_filedir_parms_cb (GObject *source_object, GAsyncResult *res, g
   else if (strcmp (job->attribute, G_FILE_ATTRIBUTE_UNIX_MODE) == 0)
     permissions = job->value.uint32;
 
-  set_unix_privs (afp_backend, job->filename,
-                  uid, gid, permissions, ua_permissions,
-                  G_VFS_JOB (job)->cancellable,
-                  set_attribute_set_unix_privs_cb, job);
+  g_vfs_afp_volume_set_unix_privs (volume, job->filename,
+                                   uid, gid, permissions, ua_permissions,
+                                   G_VFS_JOB (job)->cancellable,
+                                   set_attribute_set_unix_privs_cb, job);
 }
 
 static gboolean
@@ -3685,7 +1728,7 @@ try_set_attribute (GVfsBackend *backend,
   if ((strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_MODE) == 0 ||
        strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_UID) == 0 ||
        strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_GID) == 0)
-      && afp_backend->vol_attrs_bitmap & AFP_VOLUME_ATTRIBUTES_BITMAP_SUPPORTS_UNIX_PRIVS)
+      && g_vfs_afp_volume_get_attributes (afp_backend->volume) & AFP_VOLUME_ATTRIBUTES_BITMAP_SUPPORTS_UNIX_PRIVS)
     {
       if (type != G_FILE_ATTRIBUTE_TYPE_UINT32) 
       {
@@ -3697,10 +1740,12 @@ try_set_attribute (GVfsBackend *backend,
         return TRUE;
       }
 
-      get_filedir_parms (afp_backend, filename, AFP_FILEDIR_BITMAP_UNIX_PRIVS_BIT,
-                         AFP_FILEDIR_BITMAP_UNIX_PRIVS_BIT,
-                         G_VFS_JOB (job)->cancellable, set_attribute_get_filedir_parms_cb,
-                         job);
+      g_vfs_afp_volume_get_filedir_parms (afp_backend->volume, filename,
+                                          AFP_FILEDIR_BITMAP_UNIX_PRIVS_BIT,
+                                          AFP_FILEDIR_BITMAP_UNIX_PRIVS_BIT,
+                                          G_VFS_JOB (job)->cancellable,
+                                          set_attribute_get_filedir_parms_cb,
+                                          job);
       return TRUE;
     }
 
@@ -3716,13 +1761,13 @@ try_set_attribute (GVfsBackend *backend,
 static void
 query_fs_info_get_vol_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsAfpServer *server = G_VFS_AFP_SERVER (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobQueryFsInfo *job = G_VFS_JOB_QUERY_FS_INFO (user_data);
 
   GError *err = NULL;
   GFileInfo *info;
 
-  info = g_vfs_afp_server_get_vol_parms_finish (server, res, &err);
+  info = g_vfs_afp_volume_get_parms_finish (volume, res, &err);
   if (!info)
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
@@ -3762,9 +1807,9 @@ try_query_fs_info (GVfsBackend *backend,
 
   if (vol_bitmap != 0)
   {
-    g_vfs_afp_server_get_vol_parms (afp_backend->server, afp_backend->volume_id,
-                                    vol_bitmap, G_VFS_JOB (job)->cancellable,
-                                    query_fs_info_get_vol_parms_cb, job);
+    g_vfs_afp_volume_get_parms (afp_backend->volume, vol_bitmap,
+                                G_VFS_JOB (job)->cancellable,
+                                query_fs_info_get_vol_parms_cb, job);
   }
   else
     g_vfs_job_succeeded (G_VFS_JOB (job));
@@ -3775,14 +1820,14 @@ try_query_fs_info (GVfsBackend *backend,
 static void
 get_name_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobQueryInfo *job = G_VFS_JOB_QUERY_INFO (user_data);
 
   char *name;
   AfpMapIDFunction map_function;
   guint outstanding_requests;
 
-  name = map_id_finish (afp_backend, res, &map_function, NULL);
+  name = g_vfs_afp_volume_map_id_finish (volume, res, &map_function, NULL);
   if (name)
   {
     switch (map_function)
@@ -3832,17 +1877,17 @@ set_root_info (GVfsBackendAfp *afp_backend, GFileInfo *info)
 static void
 query_info_get_filedir_parms_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {
-  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (source_object);
+  GVfsAfpVolume *volume = G_VFS_AFP_VOLUME (source_object);
   GVfsJobQueryInfo *job = G_VFS_JOB_QUERY_INFO (user_data);
+  GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (job->backend);
   
-
   GFileInfo *info;
   GError *err = NULL;
 
   GFileAttributeMatcher *matcher;
   guint outstanding_requests;
   
-  info = get_filedir_parms_finish (afp_backend, res, &err);
+  info = g_vfs_afp_volume_get_filedir_parms_finish (volume, res, &err);
   if (!info)
   {
     g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
@@ -3861,15 +1906,17 @@ query_info_get_filedir_parms_cb (GObject *source_object, GAsyncResult *res, gpoi
 
     if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_OWNER_USER))
     {
-      map_id (afp_backend, AFP_MAP_ID_FUNCTION_USER_ID_TO_NAME, uid,
-              G_VFS_JOB (job)->cancellable, get_name_cb, job);
+      g_vfs_afp_volume_map_id (volume,
+                               AFP_MAP_ID_FUNCTION_USER_ID_TO_NAME, uid,
+                               G_VFS_JOB (job)->cancellable, get_name_cb, job);
       outstanding_requests++;
     }
     
     if (g_file_attribute_matcher_matches (matcher, G_FILE_ATTRIBUTE_OWNER_USER_REAL))
     {
-      map_id (afp_backend, AFP_MAP_ID_FUNCTION_USER_ID_TO_UTF8_NAME, uid,
-              G_VFS_JOB (job)->cancellable, get_name_cb, job);
+      g_vfs_afp_volume_map_id (volume,
+                               AFP_MAP_ID_FUNCTION_USER_ID_TO_UTF8_NAME, uid,
+                               G_VFS_JOB (job)->cancellable, get_name_cb, job);
       outstanding_requests++;
     }
   }
@@ -3881,8 +1928,9 @@ query_info_get_filedir_parms_cb (GObject *source_object, GAsyncResult *res, gpoi
 
     gid = g_file_info_get_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_GID);
 
-    map_id (afp_backend, AFP_MAP_ID_FUNCTION_GROUP_ID_TO_NAME, gid,
-            G_VFS_JOB (job)->cancellable, get_name_cb, job);
+    g_vfs_afp_volume_map_id (volume,
+                             AFP_MAP_ID_FUNCTION_GROUP_ID_TO_NAME, gid,
+                             G_VFS_JOB (job)->cancellable, get_name_cb, job);
     outstanding_requests++;
   }
   
@@ -3919,9 +1967,10 @@ try_query_info (GVfsBackend *backend,
     
     if (dir_bitmap != 0)
     {
-      get_filedir_parms (afp_backend, filename, 0, dir_bitmap,
-                         G_VFS_JOB (job)->cancellable,
-                         query_info_get_filedir_parms_cb, job);
+      g_vfs_afp_volume_get_filedir_parms (afp_backend->volume, filename,
+                                          0, dir_bitmap,
+                                          G_VFS_JOB (job)->cancellable,
+                                          query_info_get_filedir_parms_cb, job);
     }
     else
     {
@@ -3936,87 +1985,12 @@ try_query_info (GVfsBackend *backend,
     file_bitmap = create_file_bitmap (afp_backend, matcher);
     dir_bitmap = create_dir_bitmap (afp_backend, matcher);
 
-    get_filedir_parms (afp_backend, filename, file_bitmap, dir_bitmap,
-                       G_VFS_JOB (job)->cancellable, query_info_get_filedir_parms_cb,
-                       job);
-  }
-
-  return TRUE;
-}
-
-static gboolean
-get_userinfo (GVfsBackendAfp *afp_backend,
-              GCancellable *cancellable,
-              GError **error)
-{
-  GVfsAfpCommand *comm;
-  guint16 bitmap;
-  gboolean res;
-
-  GVfsAfpReply *reply;
-  AfpResultCode res_code;
-
-  comm = g_vfs_afp_command_new (AFP_COMMAND_GET_USER_INFO);
-  /* Flags, ThisUser = 1 */
-  g_vfs_afp_command_put_byte (comm, 0x01);
-  /* UserId */
-  g_vfs_afp_command_put_int32 (comm, 0);
-  /* Bitmap */
-  bitmap = AFP_GET_USER_INFO_BITMAP_GET_UID_BIT | AFP_GET_USER_INFO_BITMAP_GET_GID_BIT;
-  g_vfs_afp_command_put_uint16 (comm, bitmap);
-
-  res = g_vfs_afp_connection_send_command_sync (afp_backend->server->conn,
-                                                comm, cancellable, error);
-  g_object_unref (comm);
-  if (!res)
-    return FALSE;
-
-  reply = g_vfs_afp_connection_read_reply_sync (afp_backend->server->conn,
-                                                cancellable, error);
-  if (!reply)
-    return FALSE;
-
-  res_code = g_vfs_afp_reply_get_result_code (reply);
-  if (res_code != AFP_RESULT_NO_ERROR)
-  {
-    g_object_unref (reply);
-
-    switch (res_code)
-    {
-      case AFP_RESULT_ACCESS_DENIED:
-        g_set_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
-                     _("Permission denied"));
-        break;
-        break;
-      case AFP_RESULT_CALL_NOT_SUPPORTED:
-        g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
-                     _("Command is not supported by server"));
-        break;
-      case AFP_RESULT_PWD_EXPIRED_ERR:
-        g_set_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
-                     _("User's password has expired"));
-        break;
-      case AFP_RESULT_PWD_NEEDS_CHANGE_ERR:
-        g_set_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED,
-                     _("User's password needs to be changed"));
-        break;
-
-      default:
-        g_propagate_error (error, afp_result_code_to_gerror (res_code));
-        break;
-    }
-    return FALSE;
+    g_vfs_afp_volume_get_filedir_parms (afp_backend->volume, filename,
+                                        file_bitmap, dir_bitmap,
+                                        G_VFS_JOB (job)->cancellable,
+                                        query_info_get_filedir_parms_cb, job);
   }
 
-  /* Bitmap */
-  g_vfs_afp_reply_read_uint16 (reply, NULL);
-  /* UID */
-  g_vfs_afp_reply_read_uint32 (reply, &afp_backend->user_id);
-  /* GID */
-  g_vfs_afp_reply_read_uint32 (reply, &afp_backend->group_id);
-
-  g_object_unref (reply);
-  
   return TRUE;
 }
 
@@ -4031,10 +2005,6 @@ do_mount (GVfsBackend *backend,
 
   gboolean res;
   GError *err = NULL;
-
-  GVfsAfpCommand *comm;
-  GVfsAfpReply *reply;
-  AfpResultCode res_code;
   
   GMountSpec *afp_mount_spec;
   char       *server_name;
@@ -4046,56 +2016,18 @@ do_mount (GVfsBackend *backend,
                                 NULL, G_VFS_JOB (job)->cancellable, &err);
   if (!res)
     goto error;
-  
-  /* Get User Info */
-  if (!get_userinfo (afp_backend, G_VFS_JOB (job)->cancellable, &err))
-    goto error;
-  
-  /* Open Volume */
-  comm = g_vfs_afp_command_new (AFP_COMMAND_OPEN_VOL);
-  /* pad byte */
-  g_vfs_afp_command_put_byte (comm, 0);
-  /* Volume Bitmap */
-  g_vfs_afp_command_put_uint16 (comm, AFP_VOLUME_BITMAP_VOL_ID_BIT | AFP_VOLUME_BITMAP_ATTRIBUTE_BIT);
-
-  /* VolumeName */
-  g_vfs_afp_command_put_pascal (comm, afp_backend->volume);
-
-  /* TODO: password? */
-
-  res = g_vfs_afp_connection_send_command_sync (afp_backend->server->conn,
-                                                comm, G_VFS_JOB (job)->cancellable,
-                                                &err);
-  g_object_unref (comm);
-  if (!res)
-    goto error;
 
-  reply = g_vfs_afp_connection_read_reply_sync (afp_backend->server->conn,
-                                                G_VFS_JOB (job)->cancellable, &err);
-  if (!reply)
+  afp_backend->volume =
+    g_vfs_afp_server_mount_volume_sync (afp_backend->server, afp_backend->volume_name,
+                                        G_VFS_JOB (job)->cancellable, &err);
+  if (!afp_backend->volume)
     goto error;
-
-  res_code = g_vfs_afp_reply_get_result_code (reply);
-  if (res_code != AFP_RESULT_NO_ERROR)
-  {
-    g_object_unref (reply);
-    goto generic_error;
-  }
-  
-  /* Volume Bitmap */
-  g_vfs_afp_reply_read_uint16 (reply, NULL);
-  /* Volume Attributes Bitmap */
-  g_vfs_afp_reply_read_uint16 (reply, &afp_backend->vol_attrs_bitmap);
-  /* Volume ID */
-  g_vfs_afp_reply_read_uint16 (reply, &afp_backend->volume_id);
-  
-  g_object_unref (reply);
   
   /* set mount info */
   afp_mount_spec = g_mount_spec_new ("afp-volume");
   g_mount_spec_set (afp_mount_spec, "host",
                     g_network_address_get_hostname (G_NETWORK_ADDRESS (afp_backend->addr)));
-  g_mount_spec_set (afp_mount_spec, "volume", afp_backend->volume);
+  g_mount_spec_set (afp_mount_spec, "volume", afp_backend->volume_name);
   if (afp_backend->user)
     g_mount_spec_set (afp_mount_spec, "user", afp_backend->user);
 
@@ -4110,12 +2042,12 @@ do_mount (GVfsBackend *backend,
   if (afp_backend->user)
     /* Translators: first %s is volumename, second username and third servername */ 
     display_name = g_strdup_printf (_("AFP volume %s for %s on %s"), 
-                                    afp_backend->volume, afp_backend->user,
+                                    afp_backend->volume_name, afp_backend->user,
                                     server_name);
   else
     /* Translators: first %s is volumename and second servername */
     display_name = g_strdup_printf (_("AFP volume %s on %s"),
-                                    afp_backend->volume, server_name);
+                                    afp_backend->volume_name, server_name);
   
   g_vfs_backend_set_display_name (backend, display_name);
   g_free (display_name);
@@ -4129,13 +2061,6 @@ do_mount (GVfsBackend *backend,
 error:
   g_vfs_job_failed_from_error (G_VFS_JOB (job), err);
   return;
-
-generic_error:
-  /* Translators: first %s is volumename and second servername */ 
-  g_vfs_job_failed (G_VFS_JOB (job), G_IO_ERROR, G_IO_ERROR_FAILED,
-                    _("Couldn't mount AFP volume %s on %s"), afp_backend->volume,
-                    afp_backend->server->server_name);
-  return;
 }
   
 static gboolean
@@ -4167,7 +2092,7 @@ try_mount (GVfsBackend *backend,
                       _("No volume specified"));
     return TRUE;
   }
-  afp_backend->volume = g_strdup (volume);
+  afp_backend->volume_name = g_strdup (volume);
 
   portstr = g_mount_spec_get (mount_spec, "port");
   if (portstr != NULL)
@@ -4188,7 +2113,7 @@ g_vfs_backend_afp_init (GVfsBackendAfp *object)
 {
   GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (object);
   
-  afp_backend->volume = NULL;
+  afp_backend->volume_name = NULL;
   afp_backend->user = NULL;
 
   afp_backend->addr = NULL;
@@ -4199,9 +2124,11 @@ g_vfs_backend_afp_finalize (GObject *object)
 {
   GVfsBackendAfp *afp_backend = G_VFS_BACKEND_AFP (object);
 
-  g_free (afp_backend->volume);
   g_free (afp_backend->user);
 
+  if (afp_backend->volume)
+    g_object_unref (afp_backend->volume);
+    
   if (afp_backend->addr)
     g_object_unref (afp_backend->addr);
 
diff --git a/daemon/gvfsbackendafpbrowse.c b/daemon/gvfsbackendafpbrowse.c
index 1b5f1fc..d3de896 100644
--- a/daemon/gvfsbackendafpbrowse.c
+++ b/daemon/gvfsbackendafpbrowse.c
@@ -65,18 +65,6 @@ struct _GVfsBackendAfpBrowse
 G_DEFINE_TYPE (GVfsBackendAfpBrowse, g_vfs_backend_afp_browse, G_VFS_TYPE_BACKEND);
 
 
-static gboolean
-is_root (const char *filename)
-{
-  const char *p;
-
-  p = filename;
-  while (*p == '/')
-    p++;
-
-  return *p == 0;
-}
-
 static void
 get_volumes_cb (GObject *source_object, GAsyncResult *res, gpointer user_data)
 {



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