[glib] GFile: Add g_file_peek_path()
- From: Philip Withnall <pwithnall src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib] GFile: Add g_file_peek_path()
- Date: Mon, 15 Jan 2018 18:27:21 +0000 (UTC)
commit 4808a957b5482fdbe10926c3a84895b409de117e
Author: Colin Walters <walters verbum org>
Date: Thu Jun 23 08:42:53 2016 -0400
GFile: Add g_file_peek_path()
This is a variant of g_file_get_path() which returns a const string to
the caller, rather than transferring ownership.
I've been carrying `gs_file_get_path_cached()` in libgsystem and it
has seen a lot of use in the ostree and flatpak codebases. There are
probably others too.
I think language bindings like Python/Gjs could also use this to avoid
an extra malloc (i.e. we could transparently replace
`g_file_get_path()` with `g_file_peek_path()`.
(Originally by Colin Walters. Tweaked by Philip Withnall to update to
2.56, change the function name and drop the locking.)
https://bugzilla.gnome.org/show_bug.cgi?id=767976
docs/reference/gio/gio-sections.txt | 1 +
gio/gfile.c | 101 +++++++++++++++++++++++++++++++++++
gio/gfile.h | 2 +
gio/tests/file.c | 8 ++-
4 files changed, 109 insertions(+), 3 deletions(-)
---
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index 277ca61..2eb7efc 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -91,6 +91,7 @@ g_file_hash
g_file_equal
g_file_get_basename
g_file_get_path
+g_file_peek_path
g_file_get_uri
g_file_get_parse_name
g_file_get_parent
diff --git a/gio/gfile.c b/gio/gfile.c
index 4405582..bf08d20 100644
--- a/gio/gfile.c
+++ b/gio/gfile.c
@@ -534,6 +534,107 @@ g_file_get_path (GFile *file)
return (* iface->get_path) (file);
}
+/* Original commit introducing this in libgsystem:
+ *
+ * fileutil: Handle recent: and trash: URIs
+ *
+ * The gs_file_get_path_cached() was rather brittle in its handling
+ * of URIs. It would assert() when a GFile didn't have a backing path
+ * (such as when handling trash: or recent: URIs), and didn't know
+ * how to get the target URI for those items either.
+ *
+ * Make sure that we do not assert() when a backing path cannot be
+ * found, and handle recent: and trash: URIs.
+ *
+ * https://bugzilla.gnome.org/show_bug.cgi?id=708435
+ */
+static char *
+file_get_target_path (GFile *file)
+{
+ GFileInfo *info;
+ const char *target;
+ char *path;
+
+ info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI, G_FILE_QUERY_INFO_NONE, NULL, NULL);
+ if (info == NULL)
+ return NULL;
+ target = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_STANDARD_TARGET_URI);
+ path = g_filename_from_uri (target, NULL, NULL);
+ g_object_unref (info);
+
+ return path;
+}
+
+static const char *
+file_peek_path_generic (GFile *file)
+{
+ const char *path;
+ static GQuark _file_path_quark = 0;
+
+ if (G_UNLIKELY (_file_path_quark) == 0)
+ _file_path_quark = g_quark_from_static_string ("gio-file-path");
+
+ /* We need to be careful about threading, as two threads calling
+ * g_file_peek_path() on the same file could race: both would see
+ * (g_object_get_qdata(…) == NULL) to begin with, both would generate and add
+ * the path, but the second thread to add it would end up freeing the path
+ * set by the first thread. The first thread would still return the pointer
+ * to that freed path, though, resulting an a read-after-free. Handle that
+ * with a compare-and-swap loop. The g_object_*_qdata() functions are atomic. */
+
+ while (TRUE)
+ {
+ gchar *new_path = NULL;
+
+ path = g_object_get_qdata ((GObject*)file, _file_path_quark);
+
+ if (path != NULL)
+ break;
+
+ if (g_file_has_uri_scheme (file, "trash") ||
+ g_file_has_uri_scheme (file, "recent"))
+ new_path = file_get_target_path (file);
+ else
+ new_path = g_file_get_path (file);
+ if (new_path == NULL)
+ return NULL;
+
+ /* By passing NULL here, we ensure we never replace existing data: */
+ if (g_object_replace_qdata ((GObject *) file, _file_path_quark,
+ NULL, (gpointer) new_path,
+ (GDestroyNotify) g_free, NULL))
+ break;
+ else
+ g_free (new_path);
+ }
+
+ return path;
+}
+
+/**
+ * g_file_peek_path:
+ * @file: input #GFile
+ *
+ * Exactly like g_file_get_path(), but caches the result via
+ * g_object_set_qdata_full(). This is useful for example in C
+ * applications which mix `g_file_*` APIs with native ones. It
+ * also avoids an extra duplicated string when possible, so will be
+ * generally more efficient.
+ *
+ * This call does no blocking I/O.
+ *
+ * Returns: (type filename) (nullable): string containing the #GFile's path,
+ * or %NULL if no such path exists. The returned string is owned by @file.
+ * Since: 2.56
+ */
+const char *
+g_file_peek_path (GFile *file)
+{
+ if (G_IS_LOCAL_FILE (file))
+ return _g_local_file_get_filename ((GLocalFile *) file);
+ return file_peek_path_generic (file);
+}
+
/**
* g_file_get_uri:
* @file: input #GFile
diff --git a/gio/gfile.h b/gio/gfile.h
index 1717665..4aff644 100644
--- a/gio/gfile.h
+++ b/gio/gfile.h
@@ -620,6 +620,8 @@ GLIB_AVAILABLE_IN_ALL
char * g_file_get_basename (GFile *file);
GLIB_AVAILABLE_IN_ALL
char * g_file_get_path (GFile *file);
+GLIB_AVAILABLE_IN_2_56
+const char * g_file_peek_path (GFile *file);
GLIB_AVAILABLE_IN_ALL
char * g_file_get_uri (GFile *file);
GLIB_AVAILABLE_IN_ALL
diff --git a/gio/tests/file.c b/gio/tests/file.c
index cf2aae2..98eeb85 100644
--- a/gio/tests/file.c
+++ b/gio/tests/file.c
@@ -169,9 +169,12 @@ monitor_changed (GFileMonitor *monitor,
{
CreateDeleteData *data = user_data;
gchar *path;
+ const gchar *peeked_path;
path = g_file_get_path (file);
+ peeked_path = g_file_peek_path (file);
g_assert_cmpstr (data->monitor_path, ==, path);
+ g_assert_cmpstr (path, ==, peeked_path);
g_free (path);
if (event_type == G_FILE_MONITOR_EVENT_CREATED)
@@ -619,7 +622,7 @@ static void
test_replace_load (void)
{
ReplaceLoadData *data;
- gchar *path;
+ const gchar *path;
GFileIOStream *iostream;
data = g_new0 (ReplaceLoadData, 1);
@@ -631,7 +634,7 @@ test_replace_load (void)
g_assert (data->file != NULL);
g_object_unref (iostream);
- path = g_file_get_path (data->file);
+ path = g_file_peek_path (data->file);
remove (path);
g_assert (!g_file_query_exists (data->file, NULL));
@@ -653,7 +656,6 @@ test_replace_load (void)
g_main_loop_unref (data->loop);
g_object_unref (data->file);
g_free (data);
- free (path);
}
static void
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]