summaryrefslogtreecommitdiff
path: root/metadata/metatree.c
diff options
context:
space:
mode:
authorAlexander Larsson <alexl@redhat.com>2009-06-18 10:55:15 +0200
committerAlexander Larsson <alexl@redhat.com>2009-06-23 15:10:27 +0200
commit74ea19992c60e0bd2e9f1a9e925b349909f1bdd8 (patch)
tree1c0e3c73e6de333f1ba24d251c94c4cf029b6b27 /metadata/metatree.c
parent7e9a03103145fbfdbab2d29767321f5362d7ac31 (diff)
downloadgvfs-74ea19992c60e0bd2e9f1a9e925b349909f1bdd8.tar.gz
Cache mountinfo lookups
Diffstat (limited to 'metadata/metatree.c')
-rw-r--r--metadata/metatree.c270
1 files changed, 167 insertions, 103 deletions
diff --git a/metadata/metatree.c b/metadata/metatree.c
index dad41d85..744f54e0 100644
--- a/metadata/metatree.c
+++ b/metadata/metatree.c
@@ -11,6 +11,7 @@
#include <glib.h>
#include <glib/gstdio.h>
#include <errno.h>
+#include <poll.h>
#include "crc32.h"
#define MAGIC "\xda\x1ameta"
@@ -1873,81 +1874,25 @@ struct _MetaLookupCache {
};
#ifdef __linux__
-static gboolean
-mountinfo_strequal_escaped (const char *s,
- const char *escaped)
-{
- char c;
- while (*s != 0 && *escaped != ' ' && *escaped != 0)
- {
- if (*escaped == '\\')
- {
- escaped++;
- c = *escaped++ - '0';
- c <<= 3;
- c |= *escaped++ - '0';
- c <<= 3;
- c |= *escaped++ - '0';
- }
- else
- c = *escaped++;
-
- if (c != *s++)
- return FALSE;
- }
- if (*s == 0 && (*escaped == 0 || *escaped == ' '))
- return TRUE;
- return FALSE;
-}
-static char *
-mountinfo_unescape (const char *escaped)
-{
- char *res, *s;
- char c;
- gsize len;
-
- s = strchr (escaped, ' ');
- if (s)
- len = s - escaped;
- else
- len = strlen (escaped);
- res = malloc (len + 1);
- s = res;
+typedef struct {
+ char *mountpoint;
+ char *root;
+} MountinfoEntry;
- while (*escaped != 0 && *escaped != ' ')
- {
- if (*escaped == '\\')
- {
- escaped++;
- c = *escaped++ - '0';
- c <<= 3;
- c |= *escaped++ - '0';
- c <<= 3;
- c |= *escaped++ - '0';
- }
- else
- c = *escaped++;
- *s++ = c;
- }
- *s = 0;
- return res;
-}
+static gboolean mountinfo_initialized = FALSE;
+static int mountinfo_fd = -1;
+static MountinfoEntry *mountinfo_roots = NULL;
+G_LOCK_DEFINE_STATIC (mountinfo);
/* We want to avoid mmap and stat as these are not ideal
operations for a proc file */
static char *
-read_contents (char *filename)
+read_contents (int fd)
{
char *data;
gsize len;
gsize bytes_read;
- int fd;
-
- fd = open (filename, O_RDONLY);
-
- if (fd == -1)
- return NULL;
len = 4096;
data = g_malloc (len);
@@ -1970,7 +1915,6 @@ read_contents (char *filename)
if (errno != EINTR)
{
g_free (data);
- close (fd);
return NULL;
}
}
@@ -1979,7 +1923,6 @@ read_contents (char *filename)
else
bytes_read += rc;
}
- close (fd);
/* zero terminate */
if (len - bytes_read < 1)
@@ -1988,62 +1931,183 @@ read_contents (char *filename)
return (char *)data;
}
-#endif
-
static char *
-get_extra_prefix_for_mount (const char *mountpoint)
+mountinfo_unescape (const char *escaped)
{
-#ifdef __linux__
- gchar *contents;
- char *res;
- char *line;
- char *line_root;
- char *line_mountpoint;
+ char *res, *s;
+ char c;
+ gsize len;
- if (mountpoint &&
- (contents = read_contents ("/proc/self/mountinfo")) != NULL)
+ s = strchr (escaped, ' ');
+ if (s)
+ len = s - escaped;
+ else
+ len = strlen (escaped);
+ res = malloc (len + 1);
+ s = res;
+
+ while (*escaped != 0 && *escaped != ' ')
{
- line = contents;
- while (line != NULL && *line != 0)
+ if (*escaped == '\\')
{
- /* parent id */
- line = strchr (line, ' ');
- line_mountpoint = NULL;
+ escaped++;
+ c = *escaped++ - '0';
+ c <<= 3;
+ c |= *escaped++ - '0';
+ c <<= 3;
+ c |= *escaped++ - '0';
+ }
+ else
+ c = *escaped++;
+ *s++ = c;
+ }
+ *s = 0;
+ return res;
+}
+
+static MountinfoEntry *
+parse_mountinfo (const char *contents)
+{
+ GArray *a;
+ const char *line;
+ const char *line_root;
+ const char *line_mountpoint;
+
+ a = g_array_new (TRUE, TRUE, sizeof (MountinfoEntry));
+
+ line = contents;
+ while (line != NULL && *line != 0)
+ {
+ /* parent id */
+ line = strchr (line, ' ');
+ line_mountpoint = NULL;
+ if (line)
+ {
+ /* major:minor */
+ line = strchr (line+1, ' ');
if (line)
{
- /* major:minor */
+ /* root */
line = strchr (line+1, ' ');
+ line_root = line + 1;
if (line)
{
- /* root */
+ /* mountpoint */
line = strchr (line+1, ' ');
- line_root = line + 1;
- if (line)
- {
- /* mountpoint */
- line = strchr (line+1, ' ');
- line_mountpoint = line + 1;
- }
+ line_mountpoint = line + 1;
}
}
+ }
- if (line_mountpoint &&
- mountinfo_strequal_escaped (mountpoint, line_mountpoint))
- {
- res = mountinfo_unescape (line_root);
- g_free (contents);
- return res;
- }
+ if (line_mountpoint && !(line_root[0] == '/' && line_root[1] == ' '))
+ {
+ MountinfoEntry new_entry;
- line = strchr (line, '\n');
- if (line)
- line++;
+ new_entry.mountpoint = mountinfo_unescape (line_mountpoint);
+ new_entry.root = mountinfo_unescape (line_root);
+
+ g_array_append_val (a, new_entry);
}
- g_free (contents);
+ line = strchr (line, '\n');
+ if (line)
+ line++;
}
+ return (MountinfoEntry *)g_array_free (a, FALSE);
+}
+
+static void
+free_mountinfo (void)
+{
+ int i;
+
+ if (mountinfo_roots)
+ {
+ for (i = 0; mountinfo_roots[i].mountpoint != NULL; i++)
+ {
+ g_free (mountinfo_roots[i].mountpoint);
+ g_free (mountinfo_roots[i].root);
+ }
+ g_free (mountinfo_roots);
+ mountinfo_roots = NULL;
+ }
+}
+
+static void
+update_mountinfo (void)
+{
+ char *contents;
+ int res;
+ gboolean first;
+ struct pollfd pfd;
+
+ first = FALSE;
+ if (!mountinfo_initialized)
+ {
+ mountinfo_initialized = TRUE;
+ mountinfo_fd = open ("/proc/self/mountinfo", O_RDONLY);
+ first = TRUE;
+ }
+
+ if (mountinfo_fd == -1)
+ return;
+
+ if (!first)
+ {
+ pfd.fd = mountinfo_fd;
+ pfd.events = POLLIN | POLLOUT | POLLPRI;
+ pfd.revents = 0;
+ res = poll (&pfd, 1, 0);
+ if (res == 0)
+ return;
+ }
+
+ free_mountinfo ();
+ contents = read_contents (mountinfo_fd);
+ lseek (mountinfo_fd, SEEK_SET, 0);
+ if (contents)
+ mountinfo_roots = parse_mountinfo (contents);
+}
+
+static const char *
+find_mountinfo_root_for_mountpoint (const char *mountpoint)
+{
+ char *res;
+ int i;
+
+ res = NULL;
+
+ G_LOCK (mountinfo);
+
+ update_mountinfo ();
+
+ if (mountinfo_roots)
+ {
+ for (i = 0; mountinfo_roots[i].mountpoint != NULL; i++)
+ {
+ if (strcmp (mountinfo_roots[i].mountpoint, mountpoint) == 0)
+ {
+ res = g_strdup (mountinfo_roots[i].root);
+ break;
+ }
+ }
+ }
+
+ G_UNLOCK (mountinfo);
+
+ return res;
+}
+
+#endif
+
+
+static char *
+get_extra_prefix_for_mount (const char *mountpoint)
+{
+#ifdef __linux__
+ return find_mountinfo_root_for_mountpoint (mountpoint);
#endif
return NULL;
}