[gvfs] [FTP] rework handling of uncached files
- From: Benjamin Otte <otte src gnome org>
- To: svn-commits-list gnome org
- Subject: [gvfs] [FTP] rework handling of uncached files
- Date: Thu, 11 Jun 2009 05:21:13 -0400 (EDT)
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]