[gvfs] [FTP] rework handling of uncached files



commit f9940f8318eb0835fb793557dbd914b856cbabf9
Author: Benjamin Otte <otte gnome org>
Date:   Tue Jun 9 15:13:19 2009 +0200

    [FTP] rework handling of uncached files
    
    The directory cache needs a callback to lookup uncachable files.
    Uncachaable files are files that can be accessed but where the parent
    directory cannot be listed.
    This has to go into the directory cache, as the directory cache performs
    symlink resolving and looking up symlinks may encounter such files.
    
    Fixes a testcase in Andreas' test collection.
---
 daemon/gvfsbackendftp.c  |  114 +++--------------------------
 daemon/gvfsftpdircache.c |  180 ++++++++++++++++++++++++++++++++++-----------
 daemon/gvfsftpdircache.h |    5 +-
 3 files changed, 150 insertions(+), 149 deletions(-)

diff --git a/daemon/gvfsbackendftp.c b/daemon/gvfsbackendftp.c
index 0c35fa3..67f9e4c 100644
--- a/daemon/gvfsbackendftp.c
+++ b/daemon/gvfsbackendftp.c
@@ -160,32 +160,6 @@ gvfs_backend_ftp_determine_system (GVfsFtpTask *task)
   g_strfreev (reply);
 }
 
-static GFileInfo *
-g_vfs_bacend_ftp_create_root_file_info (GVfsBackendFtp *ftp)
-{
-  GFileInfo *info;
-  GIcon *icon;
-  char *display_name;
- 
-  info = g_file_info_new ();
-  g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
-
-  g_file_info_set_name (info, "/");
-  display_name = g_strdup_printf (_("/ on %s"), ftp->host_display_name);
-  g_file_info_set_display_name (info, display_name);
-  g_free (display_name);
-  g_file_info_set_edit_name (info, "/");
-
-  g_file_info_set_content_type (info, "inode/directory");
-  g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE, "inode/directory");
-
-  icon = g_themed_icon_new ("folder-remote");
-  g_file_info_set_icon (info, icon);
-  g_object_unref (icon);
-
-  return info;
-}
-
 static void
 gvfs_backend_ftp_setup_directory_cache (GVfsBackendFtp *ftp)
 {
@@ -194,8 +168,7 @@ gvfs_backend_ftp_setup_directory_cache (GVfsBackendFtp *ftp)
   else
     ftp->dir_funcs = &g_vfs_ftp_dir_cache_funcs_default;
 
-  ftp->dir_cache = g_vfs_ftp_dir_cache_new (ftp->dir_funcs,
-                                            g_vfs_bacend_ftp_create_root_file_info (ftp));
+  ftp->dir_cache = g_vfs_ftp_dir_cache_new (ftp->dir_funcs);
 }
 
 /*** COMMON FUNCTIONS WITH SPECIAL HANDLING ***/
@@ -720,75 +693,6 @@ do_start_write (GVfsFtpTask *task,
     }
 }
 
-/* NB: This gets a file info for the given object, no matter if it's a dir
- * or a file */
-static GFileInfo *
-create_file_info (GVfsFtpTask *task, GVfsFtpFile *file, gboolean resolve_symlinks)
-{
-  GFileInfo *info;
-  char **reply;
-
-  if (g_vfs_ftp_task_is_in_error (task))
-    return NULL;
-
-  info = g_vfs_ftp_dir_cache_lookup_file (task->backend->dir_cache, task, file, resolve_symlinks);
-  if (info)
-    return info;
-
-  g_vfs_ftp_task_clear_error (task);
-
-  /* the directory cache fails when the parent directory of the file is not readable.
-   * This cannot happen on Unix, but it can happen on FTP.
-   * In this case we try to figure out as much as possible about the file (does it even exist?)
-   * using standard ftp commands.
-   */
-  if (g_vfs_ftp_task_try_cd (task, file))
-    {
-      char *tmp;
-
-      info = g_file_info_new ();
-
-      tmp = g_path_get_basename (g_vfs_ftp_file_get_gvfs_path (file));
-      g_file_info_set_name (info, tmp);
-      g_free (tmp);
-
-      gvfs_file_info_populate_default (info, g_vfs_ftp_file_get_gvfs_path (file), G_FILE_TYPE_DIRECTORY);
-
-      g_file_info_set_is_hidden (info, TRUE);
-    }
-  else if (g_vfs_ftp_task_send_and_check (task, 0, NULL, NULL, &reply, "SIZE %s", g_vfs_ftp_file_get_ftp_path (file)))
-    {
-      char *tmp;
-
-      info = g_file_info_new ();
-
-      tmp = g_path_get_basename (g_vfs_ftp_file_get_gvfs_path (file));
-      g_file_info_set_name (info, tmp);
-      g_free (tmp);
-
-      gvfs_file_info_populate_default (info, g_vfs_ftp_file_get_gvfs_path (file), G_FILE_TYPE_REGULAR);
-
-      g_file_info_set_size (info, g_ascii_strtoull (reply[0] + 4, NULL, 0));
-      g_strfreev (reply);
-
-      g_file_info_set_is_hidden (info, TRUE);
-    }
-  else
-    {
-      info = NULL;
-      /* clear error from ftp_connection_send() in else if line above */
-      g_vfs_ftp_task_clear_error (task);
-
-      /* note that there might still be a file/directory, we just have
-       * no way to figure this out (in particular on ftp servers that
-       * don't support SIZE.
-       * If you have ways to improve file detection, patches are welcome. */
-    }
-
-  return info;
-}
-
-
 static void
 do_create (GVfsBackend *backend,
            GVfsJobOpenForWrite *job,
@@ -801,7 +705,7 @@ do_create (GVfsBackend *backend,
   GVfsFtpFile *file;
 
   file = g_vfs_ftp_file_new_from_gvfs (ftp, filename);
-  info = create_file_info (&task, file, FALSE);
+  info = g_vfs_ftp_dir_cache_lookup_file (ftp->dir_cache, &task, file, FALSE);
   if (info)
     {
       g_object_unref (info);
@@ -924,7 +828,10 @@ do_query_info (GVfsBackend *backend,
   GFileInfo *real;
  
   file = g_vfs_ftp_file_new_from_gvfs (ftp, filename);
-  real = create_file_info (&task, file, query_flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS ? FALSE : TRUE);
+  real = g_vfs_ftp_dir_cache_lookup_file (ftp->dir_cache,
+                                          &task,
+                                          file,
+                                          query_flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS ? FALSE : TRUE);
 
   if (real)
     {
@@ -1131,9 +1038,10 @@ do_move (GVfsBackend *backend,
 
   if (!(flags & G_FILE_COPY_OVERWRITE))
     {
-      GFileInfo *info = create_file_info (&task,
-                                          destfile,
-                                          FALSE);
+      GFileInfo *info = g_vfs_ftp_dir_cache_lookup_file (ftp->dir_cache,
+                                                         &task,
+                                                         destfile,
+                                                         FALSE);
 
       if (info)
         {
@@ -1311,7 +1219,7 @@ do_pull (GVfsBackend *         backend,
 
   src = g_vfs_ftp_file_new_from_gvfs (ftp, source);
   if (progress_callback)
-    info = create_file_info (&task, src, TRUE);
+    info = g_vfs_ftp_dir_cache_lookup_file (ftp->dir_cache, &task, src, TRUE);
   else
     info = NULL;
   if (info)
diff --git a/daemon/gvfsftpdircache.c b/daemon/gvfsftpdircache.c
index 536f3e6..b94a3b3 100644
--- a/daemon/gvfsftpdircache.c
+++ b/daemon/gvfsftpdircache.c
@@ -96,17 +96,15 @@ struct _GVfsFtpDirCache
   GHashTable *          directories;    /* GVfsFtpFile of directory => GVfsFtpDirCacheEntry mapping */
   guint                 stamp;          /* used to identify validity of cache when flushing */
   GMutex *              lock;           /* mutex for thread safety of stamp and hash table */
-  GFileInfo *           root;           /* file info for '/' */
   const GVfsFtpDirFuncs *funcs;         /* functions to call */
 };
 
 GVfsFtpDirCache *
-g_vfs_ftp_dir_cache_new (const GVfsFtpDirFuncs *funcs, GFileInfo *root)
+g_vfs_ftp_dir_cache_new (const GVfsFtpDirFuncs *funcs)
 {
   GVfsFtpDirCache *cache;
 
   g_return_val_if_fail (funcs != NULL, NULL);
-  g_return_val_if_fail (G_IS_FILE_INFO (root), NULL);
 
   cache = g_slice_new0 (GVfsFtpDirCache);
   cache->directories = g_hash_table_new_full (g_vfs_ftp_file_hash,
@@ -115,7 +113,6 @@ g_vfs_ftp_dir_cache_new (const GVfsFtpDirFuncs *funcs, GFileInfo *root)
                                               (GDestroyNotify) g_vfs_ftp_dir_cache_entry_unref);
   cache->lock = g_mutex_new();
   cache->funcs = funcs;
-  cache->root = root;
 
   return cache;
 }
@@ -127,7 +124,6 @@ g_vfs_ftp_dir_cache_free (GVfsFtpDirCache *cache)
 
   g_hash_table_destroy (cache->directories);
   g_mutex_free (cache->lock);
-  g_object_unref (cache->root);
   g_slice_free (GVfsFtpDirCache, cache);
 }
 
@@ -191,13 +187,51 @@ g_vfs_ftp_dir_cache_lookup_entry (GVfsFtpDirCache *  cache,
 }
 
 static GFileInfo *
+g_vfs_ftp_dir_cache_lookup_file_internal (GVfsFtpDirCache *  cache,
+                                          GVfsFtpTask *      task,
+                                          const GVfsFtpFile *file,
+                                          guint              stamp)
+{
+  GVfsFtpDirCacheEntry *entry;
+  GVfsFtpFile *dir;
+  GFileInfo *info;
+
+  if (g_vfs_ftp_task_is_in_error (task))
+    return NULL;
+
+  if (!g_vfs_ftp_file_is_root (file))
+    {
+      dir = g_vfs_ftp_file_new_parent (file);
+      entry = g_vfs_ftp_dir_cache_lookup_entry (cache, task, dir, stamp);
+      g_vfs_ftp_file_free (dir);
+      if (entry == NULL)
+        return NULL;
+
+      info = g_hash_table_lookup (entry->files, file);
+      if (info != NULL)
+        {
+          /* NB: the order of ref/unref is important here */
+          g_object_ref (info);
+          g_vfs_ftp_dir_cache_entry_unref (entry);
+          return info;
+        }
+
+      g_vfs_ftp_dir_cache_entry_unref (entry);
+    }
+
+  if (!g_vfs_ftp_task_is_in_error (task))
+    info = cache->funcs->lookup_uncached (task, file);
+
+  return info;
+}
+
+static GFileInfo *
 g_vfs_ftp_dir_cache_resolve_symlink (GVfsFtpDirCache *  cache,
                                      GVfsFtpTask *      task,
                                      const GVfsFtpFile *file,
                                      GFileInfo *        original,
                                      guint              stamp)
 {
-  GVfsFtpDirCacheEntry *entry;
   static const char *copy_attributes[] = {
     G_FILE_ATTRIBUTE_STANDARD_IS_SYMLINK,
     G_FILE_ATTRIBUTE_STANDARD_IS_HIDDEN,
@@ -230,28 +264,13 @@ g_vfs_ftp_dir_cache_resolve_symlink (GVfsFtpDirCache *  cache,
           g_vfs_ftp_task_clear_error (task);
           return original;
         }
-      tmp = g_vfs_ftp_file_new_parent (link);
-      entry = g_vfs_ftp_dir_cache_lookup_entry (cache, task, tmp, stamp);
-      g_vfs_ftp_file_free (tmp);
-      if (entry == NULL)
-        {
-          g_vfs_ftp_file_free (link);
-          /* clear the (potential) error here, dangling symlinks etc should not cause errors */
-          g_vfs_ftp_task_clear_error (task);
-          return original;
-        }
-      info = g_hash_table_lookup (entry->files, link);
+      info = g_vfs_ftp_dir_cache_lookup_file_internal (cache, task, link, stamp);
       if (info == NULL)
         {
-          g_vfs_ftp_dir_cache_entry_unref (entry);
           g_vfs_ftp_file_free (link);
+          g_vfs_ftp_task_clear_error (task);
           return original;
         }
-      else
-        {
-          g_object_ref (info);
-          g_vfs_ftp_dir_cache_entry_unref (entry);
-        }
     }
   while (g_file_info_get_is_symlink (info) && lookups++ < 8);
 
@@ -293,35 +312,17 @@ g_vfs_ftp_dir_cache_lookup_file (GVfsFtpDirCache *  cache,
                                  const GVfsFtpFile *file,
                                  gboolean           resolve_symlinks)
 {
-  GVfsFtpDirCacheEntry *entry;
-  GVfsFtpFile *dir;
   GFileInfo *info;
 
   g_return_val_if_fail (cache != NULL, NULL);
   g_return_val_if_fail (task != NULL, NULL);
   g_return_val_if_fail (file != NULL, NULL);
 
-  if (g_vfs_ftp_task_is_in_error (task))
-    return NULL;
-
-  if (g_vfs_ftp_file_is_root (file))
-    return g_object_ref (cache->root);
-
-  dir = g_vfs_ftp_file_new_parent (file);
-  entry = g_vfs_ftp_dir_cache_lookup_entry (cache, task, dir, 0);
-  g_vfs_ftp_file_free (dir);
-  if (entry == NULL)
-    return NULL;
+  info = g_vfs_ftp_dir_cache_lookup_file_internal (cache, task, file, 0);
 
-  info = g_hash_table_lookup (entry->files, file);
-  if (info != NULL)
-    {
-      g_object_ref (info);
-      if (resolve_symlinks)
-        info = g_vfs_ftp_dir_cache_resolve_symlink (cache, task, file, info, 0);
-    }
+  if (info != NULL && resolve_symlinks)
+    info = g_vfs_ftp_dir_cache_resolve_symlink (cache, task, file, info, 0);
 
-  g_vfs_ftp_dir_cache_entry_unref (entry);
   return info;
 }
 
@@ -365,6 +366,7 @@ g_vfs_ftp_dir_cache_lookup_dir (GVfsFtpDirCache *  cache,
       g_object_ref (info);
       if (resolve_symlinks)
         info = g_vfs_ftp_dir_cache_resolve_symlink (cache, task, file, info, stamp);
+      g_assert (!g_vfs_ftp_task_is_in_error (task));
       result = g_list_prepend (result, info);
     }
   g_vfs_ftp_dir_cache_entry_unref (entry);
@@ -406,6 +408,94 @@ g_vfs_ftp_dir_cache_purge_file (GVfsFtpDirCache *  cache,
 #include "ParseFTPList.h"
 #include "gvfsdaemonutils.h"
 
+static GFileInfo *
+create_root_file_info (GVfsBackendFtp *ftp)
+{
+  GFileInfo *info;
+  GIcon *icon;
+  char *display_name;
+ 
+  info = g_file_info_new ();
+  g_file_info_set_file_type (info, G_FILE_TYPE_DIRECTORY);
+
+  g_file_info_set_name (info, "/");
+  display_name = g_strdup_printf (_("/ on %s"), ftp->host_display_name);
+  g_file_info_set_display_name (info, display_name);
+  g_free (display_name);
+  g_file_info_set_edit_name (info, "/");
+
+  g_file_info_set_content_type (info, "inode/directory");
+  g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE, "inode/directory");
+
+  icon = g_themed_icon_new ("folder-remote");
+  g_file_info_set_icon (info, icon);
+  g_object_unref (icon);
+
+  return info;
+}
+
+static GFileInfo *
+g_vfs_ftp_dir_cache_funcs_lookup_uncached (GVfsFtpTask *      task,
+                                           const GVfsFtpFile *file)
+{
+  GFileInfo *info;
+  char **reply;
+
+  if (g_vfs_ftp_file_is_root (file))
+    return create_root_file_info (task->backend);
+
+  /* the directory cache fails when the parent directory of the file is not readable.
+   * This cannot happen on Unix, but it can happen on FTP.
+   * In this case we try to figure out as much as possible about the file (does it even exist?)
+   * using standard ftp commands.
+   */
+  if (g_vfs_ftp_task_send (task, 0, "CWD %s", g_vfs_ftp_file_get_ftp_path (file)))
+    {
+      char *tmp;
+
+      info = g_file_info_new ();
+
+      tmp = g_path_get_basename (g_vfs_ftp_file_get_gvfs_path (file));
+      g_file_info_set_name (info, tmp);
+      g_free (tmp);
+
+      gvfs_file_info_populate_default (info, g_vfs_ftp_file_get_gvfs_path (file), G_FILE_TYPE_DIRECTORY);
+
+      g_file_info_set_is_hidden (info, TRUE);
+      
+      return info;
+    }
+  
+  g_vfs_ftp_task_clear_error (task);
+  if (g_vfs_ftp_task_send_and_check (task, 0, NULL, NULL, &reply, "SIZE %s", g_vfs_ftp_file_get_ftp_path (file)))
+    {
+      char *tmp;
+
+      info = g_file_info_new ();
+
+      tmp = g_path_get_basename (g_vfs_ftp_file_get_gvfs_path (file));
+      g_file_info_set_name (info, tmp);
+      g_free (tmp);
+
+      gvfs_file_info_populate_default (info, g_vfs_ftp_file_get_gvfs_path (file), G_FILE_TYPE_REGULAR);
+
+      g_file_info_set_size (info, g_ascii_strtoull (reply[0] + 4, NULL, 0));
+      g_strfreev (reply);
+
+      g_file_info_set_is_hidden (info, TRUE);
+      return info;
+    }
+      
+  g_vfs_ftp_task_clear_error (task);
+
+  /* note that there might still be a file/directory, we just have
+   * no way to figure this out (in particular on ftp servers that
+   * don't support SIZE.
+   * If you have ways to improve file detection, patches are welcome. */
+
+  return NULL;
+}
+
 static gboolean
 g_vfs_ftp_dir_cache_funcs_process (GInputStream *        stream,
                                    int                   debug_id,
@@ -591,11 +681,13 @@ g_vfs_ftp_dir_cache_funcs_process_default (GInputStream *        stream,
 const GVfsFtpDirFuncs g_vfs_ftp_dir_cache_funcs_unix = {
   "LIST -a",
   g_vfs_ftp_dir_cache_funcs_process_unix,
+  g_vfs_ftp_dir_cache_funcs_lookup_uncached,
   g_vfs_ftp_dir_cache_funcs_resolve_default
 };
 
 const GVfsFtpDirFuncs g_vfs_ftp_dir_cache_funcs_default = {
   "LIST",
   g_vfs_ftp_dir_cache_funcs_process_default,
+  g_vfs_ftp_dir_cache_funcs_lookup_uncached,
   g_vfs_ftp_dir_cache_funcs_resolve_default
 };
diff --git a/daemon/gvfsftpdircache.h b/daemon/gvfsftpdircache.h
index c007d07..52e49ba 100644
--- a/daemon/gvfsftpdircache.h
+++ b/daemon/gvfsftpdircache.h
@@ -41,6 +41,8 @@ struct _GVfsFtpDirFuncs {
                                                                  GVfsFtpDirCacheEntry * entry,
                                                                  GCancellable *         cancellable,
                                                                  GError **              error);
+  GFileInfo *           (* lookup_uncached)                     (GVfsFtpTask *          task,
+                                                                 const GVfsFtpFile *    file);
   GVfsFtpFile *         (* resolve_symlink)                     (GVfsFtpTask *          task,
                                                                  const GVfsFtpFile *    file,
                                                                  const char *           target);
@@ -49,8 +51,7 @@ struct _GVfsFtpDirFuncs {
 extern const GVfsFtpDirFuncs g_vfs_ftp_dir_cache_funcs_unix;
 extern const GVfsFtpDirFuncs g_vfs_ftp_dir_cache_funcs_default;
 
-GVfsFtpDirCache *       g_vfs_ftp_dir_cache_new                 (const GVfsFtpDirFuncs *funcs,
-                                                                 GFileInfo *            root);
+GVfsFtpDirCache *       g_vfs_ftp_dir_cache_new                 (const GVfsFtpDirFuncs *funcs);
 void                    g_vfs_ftp_dir_cache_free                (GVfsFtpDirCache *      cache);
 
 GFileInfo *             g_vfs_ftp_dir_cache_lookup_file         (GVfsFtpDirCache *      cache,



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