[gvfs/f18] Fix metadata issues on NFS homedirs (#561904)

Tomas Bzatek tbzatek at fedoraproject.org
Tue May 14 15:54:26 UTC 2013


commit b6cfb15fd4de70a94fade719620674d3179c3a0d
Author: Tomas Bzatek <tbzatek at redhat.com>
Date:   Tue May 14 17:54:22 2013 +0200

    Fix metadata issues on NFS homedirs (#561904)

 ...Be-more-resistive-to-broken-journal-files.patch |   58 +++
 ...ta-Create-new-journal-if-it-doesn-t-exist.patch |  101 ++++++
 ...ush-all-scheduled-writeouts-on-daemon-exi.patch |   98 +++++
 ...Force-tree-re-read-after-successful-flush.patch |   89 +++++
 ...t-journal-in-XDG_RUNTIME_DIR-for-shared-N.patch |  370 ++++++++++++++++++++
 ...adata-Use-shorter-writeout-timeout-on-NFS.patch |   78 ++++
 gvfs.spec                                          |   21 +-
 7 files changed, 814 insertions(+), 1 deletions(-)
---
diff --git a/gvfs-1.17.1-metadata-Be-more-resistive-to-broken-journal-files.patch b/gvfs-1.17.1-metadata-Be-more-resistive-to-broken-journal-files.patch
new file mode 100644
index 0000000..1c3e5b3
--- /dev/null
+++ b/gvfs-1.17.1-metadata-Be-more-resistive-to-broken-journal-files.patch
@@ -0,0 +1,58 @@
+From 21811b3ae17ec705327484c1ce0be75fec95bb0e Mon Sep 17 00:00:00 2001
+From: Tomas Bzatek <tbzatek at redhat.com>
+Date: Mon, 13 May 2013 17:40:01 +0200
+Subject: [PATCH 2/6] metadata: Be more resistive to broken journal files
+
+In shared NFS homedir case with multiple clients writing to the same
+mmaped journal file data can get easily corrupted. The daemon iterates
+over a journal file on flush taking in account variable entry size and
+advances according to the data read.
+
+However in certain case invalid data are read making us to jump out of
+bounds. In case of zero entry size we would stand at the same place
+leading to infinite loop.
+
+This patch checks if the indicated entry size is at least the size of
+the structure we're getting the size from (it's a first element) and breaks
+the iteration cycle if it's not. This may lead to partial data loss on flush
+as we don't process the rest of the journal file. Old data from existing
+tree file will be preserved of course, only few recent changes would get lost.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=637095
+---
+ metadata/metatree.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/metadata/metatree.c b/metadata/metatree.c
+index 015300e..20b7862 100644
+--- a/metadata/metatree.c
++++ b/metadata/metatree.c
+@@ -1304,6 +1304,11 @@ meta_journal_iterate (MetaJournal *journal,
+     {
+       sizep = (guint32 *)entry;
+       entry = (MetaJournalEntry *)((char *)entry - GUINT32_FROM_BE (*(sizep-1)));
++      if (GUINT32_FROM_BE (*(sizep)) < sizeof (MetaJournalEntry) && entry > journal->first_entry)
++        {
++          g_warning ("meta_journal_iterate: found short sized entry, possible journal corruption\n");
++          break;
++        }
+ 
+       mtime = GUINT64_FROM_BE (entry->mtime);
+       journal_path = &entry->path[0];
+@@ -2343,6 +2348,13 @@ apply_journal_to_builder (MetaTree *tree,
+ 
+       sizep = (guint32 *)entry;
+       entry = (MetaJournalEntry *)((char *)entry + GUINT32_FROM_BE (*(sizep)));
++      if (GUINT32_FROM_BE (*(sizep)) < sizeof (MetaJournalEntry) && entry < journal->last_entry)
++        {
++          /* This shouldn't happen, we found an entry that is shorter than its data */
++          /* See https://bugzilla.gnome.org/show_bug.cgi?id=637095 for discussion */
++          g_warning ("apply_journal_to_builder: found short sized entry, possible journal corruption\n");
++          break;
++        }
+     }
+ }
+ 
+-- 
+1.8.1.4
+
diff --git a/gvfs-1.17.1-metadata-Create-new-journal-if-it-doesn-t-exist.patch b/gvfs-1.17.1-metadata-Create-new-journal-if-it-doesn-t-exist.patch
new file mode 100644
index 0000000..4b1ed7f
--- /dev/null
+++ b/gvfs-1.17.1-metadata-Create-new-journal-if-it-doesn-t-exist.patch
@@ -0,0 +1,101 @@
+From 3381859ba7a92f4824ff09a7aa951407bf4e1c72 Mon Sep 17 00:00:00 2001
+From: Tomas Bzatek <tbzatek at redhat.com>
+Date: Mon, 13 May 2013 17:40:40 +0200
+Subject: [PATCH 3/6] metadata: Create new journal if it doesn't exist
+
+With concurrent access of multiple daemons there may be a moment when
+tree file exists but not the journal file. The daemon can't write
+anything without journal and is doomed until next rotation. Missing
+journal file can also happen on system crash etc.
+
+This patch tries to create new journal file only when it doesn't exist,
+other errors still lead to inability to store anything.
+
+This will also allow us to have journal file somewhere else, e.g. on
+a non-persistent storage.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=637095
+---
+ metadata/metabuilder.c |  6 +++---
+ metadata/metabuilder.h |  2 ++
+ metadata/metatree.c    | 16 +++++++++++++---
+ 3 files changed, 18 insertions(+), 6 deletions(-)
+
+diff --git a/metadata/metabuilder.c b/metadata/metabuilder.c
+index 58bb57b..c7e69fa 100644
+--- a/metadata/metabuilder.c
++++ b/metadata/metabuilder.c
+@@ -843,8 +843,8 @@ get_journal_filename (const char *filename, guint32 random_tag)
+   return g_strconcat (filename, "-", tag, ".log", NULL);
+ }
+ 
+-static gboolean
+-create_new_journal (const char *filename, guint32 random_tag)
++gboolean
++meta_builder_create_new_journal (const char *filename, guint32 random_tag)
+ {
+   char *journal_name;
+   guint32 size_offset;
+@@ -1016,7 +1016,7 @@ meta_builder_write (MetaBuilder *builder,
+   if (!write_all_data_and_close (fd, out->str, out->len))
+     goto out;
+ 
+-  if (!create_new_journal (filename, random_tag))
++  if (!meta_builder_create_new_journal (filename, random_tag))
+     goto out;
+ 
+   /* Open old file so we can set it rotated */
+diff --git a/metadata/metabuilder.h b/metadata/metabuilder.h
+index 364c0d8..ad3876f 100644
+--- a/metadata/metabuilder.h
++++ b/metadata/metabuilder.h
+@@ -68,6 +68,8 @@ void         meta_builder_copy      (MetaBuilder *builder,
+ 				     guint64      mtime);
+ gboolean     meta_builder_write     (MetaBuilder *builder,
+ 				     const char  *filename);
++gboolean     meta_builder_create_new_journal (const char *filename,
++				     guint32      random_tag);
+ MetaFile *   metafile_new           (const char  *name,
+ 				     MetaFile    *parent);
+ void         metafile_free          (MetaFile    *file);
+diff --git a/metadata/metatree.c b/metadata/metatree.c
+index 20b7862..8e2ab46 100644
+--- a/metadata/metatree.c
++++ b/metadata/metatree.c
+@@ -1146,20 +1146,30 @@ meta_journal_open (MetaTree *tree, const char *filename, gboolean for_write, gui
+   char *data;
+   char *journal_filename;
+   int open_flags, mmap_prot;
++  gboolean retried;
+ 
+   g_assert (sizeof (MetaJournalHeader) == 20);
+-
+-  journal_filename = get_journal_filename (filename, tag);
++  retried = FALSE;
+ 
+   if (for_write)
+     open_flags = O_RDWR;
+   else
+     open_flags = O_RDONLY;
+ 
++ retry:
++  journal_filename = get_journal_filename (filename, tag);
+   fd = safe_open (tree, journal_filename, open_flags);
+   g_free (journal_filename);
+   if (fd == -1)
+-    return NULL;
++    {
++      if (errno == ENOENT && tree->for_write && !retried)
++        {
++          retried = TRUE;
++          if (meta_builder_create_new_journal (filename, tag))
++            goto retry;
++        }
++      return NULL;
++    }
+ 
+   if (fstat (fd, &statbuf) != 0 ||
+       statbuf.st_size < sizeof (MetaJournalHeader))
+-- 
+1.8.1.4
+
diff --git a/gvfs-1.17.1-metadata-Flush-all-scheduled-writeouts-on-daemon-exi.patch b/gvfs-1.17.1-metadata-Flush-all-scheduled-writeouts-on-daemon-exi.patch
new file mode 100644
index 0000000..d06d2e5
--- /dev/null
+++ b/gvfs-1.17.1-metadata-Flush-all-scheduled-writeouts-on-daemon-exi.patch
@@ -0,0 +1,98 @@
+From e793a0476feeaa0a3cc4f1fd043c15c297707455 Mon Sep 17 00:00:00 2001
+From: Tomas Bzatek <tbzatek at redhat.com>
+Date: Mon, 13 May 2013 17:43:40 +0200
+Subject: [PATCH 6/6] metadata: Flush all scheduled writeouts on daemon exit
+
+This patch ensures that we safely write all data from journals to
+metatrees on exit. E.g. if anything happens to session bus or we get
+replaced by some other instance.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=637095
+---
+ metadata/meta-daemon.c | 46 ++++++++++++++++++++++++++++++++++++++++------
+ 1 file changed, 40 insertions(+), 6 deletions(-)
+
+diff --git a/metadata/meta-daemon.c b/metadata/meta-daemon.c
+index f29e7f0..05e1630 100644
+--- a/metadata/meta-daemon.c
++++ b/metadata/meta-daemon.c
+@@ -79,6 +79,24 @@ tree_info_schedule_writeout (TreeInfo *info)
+     }
+ }
+ 
++static void
++flush_single (const gchar *filename,
++              TreeInfo *info,
++              gpointer user_data)
++{
++  if (info->writeout_timeout != 0)
++    {
++      g_source_remove (info->writeout_timeout);
++      writeout_timeout (info);
++    }
++}
++
++static void
++flush_all ()
++{
++  g_hash_table_foreach (tree_infos, (GHFunc) flush_single, NULL);
++}
++
+ static TreeInfo *
+ tree_info_new (const char *filename)
+ {
+@@ -433,9 +451,22 @@ on_name_lost (GDBusConnection *connection,
+   GMainLoop *loop = user_data;
+ 
+   /* means that someone has claimed our name (we allow replacement) */
++  flush_all ();
+   g_main_loop_quit (loop);
+ }
+ 
++static void
++on_connection_closed (GDBusConnection *connection,
++                      gboolean         remote_peer_vanished,
++                      GError          *error,
++                      gpointer         user_data)
++{
++  GMainLoop *loop = user_data;
++
++  /* session bus died */
++  flush_all ();
++  g_main_loop_quit (loop);
++}
+ 
+ int
+ main (int argc, char *argv[])
+@@ -496,7 +527,15 @@ main (int argc, char *argv[])
+       g_error_free (error);
+       return 1;
+     }
+-  
++
++  tree_infos = g_hash_table_new_full (g_str_hash,
++				      g_str_equal,
++				      NULL,
++				      (GDestroyNotify)tree_info_free);
++
++  g_dbus_connection_set_exit_on_close (conn, FALSE);
++  g_signal_connect (conn, "closed", G_CALLBACK (on_connection_closed), loop);
++
+   flags = G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT;
+   if (replace)
+     flags |= G_BUS_NAME_OWNER_FLAGS_REPLACE;
+@@ -509,11 +548,6 @@ main (int argc, char *argv[])
+                                                 loop,
+                                                 NULL);
+   
+-  tree_infos = g_hash_table_new_full (g_str_hash,
+-				      g_str_equal,
+-				      NULL,
+-				      (GDestroyNotify)tree_info_free);
+-
+   g_main_loop_run (loop);
+   
+   if (skeleton)
+-- 
+1.8.1.4
+
diff --git a/gvfs-1.17.1-metadata-Force-tree-re-read-after-successful-flush.patch b/gvfs-1.17.1-metadata-Force-tree-re-read-after-successful-flush.patch
new file mode 100644
index 0000000..c71f184
--- /dev/null
+++ b/gvfs-1.17.1-metadata-Force-tree-re-read-after-successful-flush.patch
@@ -0,0 +1,89 @@
+From 6b12c3d7b33c87a7fea228106f39ecf2a3e0f310 Mon Sep 17 00:00:00 2001
+From: Tomas Bzatek <tbzatek at redhat.com>
+Date: Mon, 13 May 2013 17:39:26 +0200
+Subject: [PATCH 1/6] metadata: Force tree re-read after successful flush
+
+Once we flush the journal and write new tree file we need to re-read
+it to refresh internal data structures (and mmap data from the right
+file). We originally left this work on meta_tree_refresh_locked() and
+meta_tree_needs_rereading() respectively where we checked the rotated
+bit.
+
+In detail, metabuilder wrote a new temp tree file, then explicitly opened
+the current (old) one, wrote the rotated bit and atomically replaced the
+temp file. Then the metadata daemon having mmapped the old file detected
+the rotated bit and scheduled journal and tree file reopen+reread.
+
+However in concurrent environment like NFS homedir where multiple metadata
+daemons are handling the same database we may run in a race and not getting
+the rotated bit detected properly.
+
+This led to an infinite loop between meta_journal_add_entry() -
+meta_tree_flush_locked() - meta_tree_refresh_locked() - meta_journal_add_entry()
+since we had full journal, didn't detect the rotation and since the files
+were already unlinked, there was no force to break that loop. This patch
+forces tree file re-read after successful flush to prevent this issue.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=637095
+---
+ metadata/metatree.c | 14 ++++++++------
+ 1 file changed, 8 insertions(+), 6 deletions(-)
+
+diff --git a/metadata/metatree.c b/metadata/metatree.c
+index 3bcf9a6..015300e 100644
+--- a/metadata/metatree.c
++++ b/metadata/metatree.c
+@@ -169,7 +169,8 @@ struct _MetaTree {
+   MetaJournal *journal;
+ };
+ 
+-static void         meta_tree_refresh_locked   (MetaTree    *tree);
++static void         meta_tree_refresh_locked   (MetaTree    *tree,
++						gboolean     force_reread);
+ static MetaJournal *meta_journal_open          (MetaTree    *tree,
+ 						const char  *filename,
+ 						gboolean     for_write,
+@@ -510,7 +511,7 @@ meta_tree_init (MetaTree *tree)
+      journal. However we can detect this case by looking at the tree and see
+      if its been rotated, we do this to ensure we have an uptodate tree+journal
+      combo. */
+-  meta_tree_refresh_locked (tree);
++  meta_tree_refresh_locked (tree, FALSE);
+ 
+   return TRUE;
+ 
+@@ -658,10 +659,10 @@ meta_tree_has_new_journal_entries (MetaTree *tree)
+ 
+ /* Must be called with a write lock held */
+ static void
+-meta_tree_refresh_locked (MetaTree *tree)
++meta_tree_refresh_locked (MetaTree *tree, gboolean force_reread)
+ {
+   /* Needs to recheck since we dropped read lock */
+-  if (meta_tree_needs_rereading (tree))
++  if (force_reread || meta_tree_needs_rereading (tree))
+     {
+       if (tree->header)
+ 	meta_tree_clear (tree);
+@@ -685,7 +686,7 @@ meta_tree_refresh (MetaTree *tree)
+   if (needs_refresh)
+     {
+       g_rw_lock_writer_lock (&metatree_lock);
+-      meta_tree_refresh_locked (tree);
++      meta_tree_refresh_locked (tree, FALSE);
+       g_rw_lock_writer_unlock (&metatree_lock);
+     }
+ }
+@@ -2363,7 +2364,8 @@ meta_tree_flush_locked (MetaTree *tree)
+   res = meta_builder_write (builder,
+ 			    meta_tree_get_filename (tree));
+   if (res)
+-    meta_tree_refresh_locked (tree);
++    /* Force re-read since we wrote a new file */
++    meta_tree_refresh_locked (tree, TRUE);
+ 
+   meta_builder_free (builder);
+ 
+-- 
+1.8.1.4
+
diff --git a/gvfs-1.17.1-metadata-Put-journal-in-XDG_RUNTIME_DIR-for-shared-N.patch b/gvfs-1.17.1-metadata-Put-journal-in-XDG_RUNTIME_DIR-for-shared-N.patch
new file mode 100644
index 0000000..c7b0f68
--- /dev/null
+++ b/gvfs-1.17.1-metadata-Put-journal-in-XDG_RUNTIME_DIR-for-shared-N.patch
@@ -0,0 +1,370 @@
+From 749c872b9192a84e65d83bc0f0eb697ab247fdd8 Mon Sep 17 00:00:00 2001
+From: Tomas Bzatek <tbzatek at redhat.com>
+Date: Mon, 13 May 2013 17:43:02 +0200
+Subject: [PATCH 4/6] metadata: Put journal in $XDG_RUNTIME_DIR for shared NFS
+ homedir case
+
+This essentially moves is_on_nfs() from metatree.c in metabuilder.c
+as the more appropriate place for shared functions. It's used in
+meta_builder_get_journal_filename() to determine whether to use original
+metadata directory or temporary $XDG_RUNTIME_DIR location to work around
+certain NFS issues.
+
+The idea behind this change is to have separate journals for every client
+that is accessing shared homedir. Then the only possible point of conflict
+is on rotation which is backed up by atomic file rename. Without this,
+there were multiple metadata daemons writing to the same journal file,
+overwriting changes to each other and being racy in flush and rotation.
+
+There will always be a conflict between clients, overwriting tree file
+data by flushing their journals.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=637095
+---
+ metadata/metabuilder.c | 135 +++++++++++++++++++++++++++++++++++++++++++++++--
+ metadata/metabuilder.h |   3 ++
+ metadata/metatree.c    | 103 +------------------------------------
+ 3 files changed, 136 insertions(+), 105 deletions(-)
+
+diff --git a/metadata/metabuilder.c b/metadata/metabuilder.c
+index c7e69fa..af9e99d 100644
+--- a/metadata/metabuilder.c
++++ b/metadata/metabuilder.c
+@@ -1,3 +1,4 @@
++#include "config.h"
+ #include "metabuilder.h"
+ #include <sys/types.h>
+ #include <sys/stat.h>
+@@ -8,6 +9,44 @@
+ #include <sys/mman.h>
+ #include <glib/gstdio.h>
+ 
++#if HAVE_SYS_STATFS_H
++#include <sys/statfs.h>
++#endif
++#if HAVE_SYS_STATVFS_H
++#include <sys/statvfs.h>
++#endif
++#if HAVE_SYS_VFS_H
++#include <sys/vfs.h>
++#elif HAVE_SYS_MOUNT_H
++#if HAVE_SYS_PARAM_H
++#include <sys/param.h>
++#endif
++#include <sys/mount.h>
++#endif
++
++#if defined(HAVE_STATFS) && defined(HAVE_STATVFS)
++/* Some systems have both statfs and statvfs, pick the
++   most "native" for these */
++# if !defined(HAVE_STRUCT_STATFS_F_BAVAIL)
++   /* on solaris and irix, statfs doesn't even have the
++      f_bavail field */
++#  define USE_STATVFS
++# else
++  /* at least on linux, statfs is the actual syscall */
++#  define USE_STATFS
++# endif
++
++#elif defined(HAVE_STATFS)
++
++# define USE_STATFS
++
++#elif defined(HAVE_STATVFS)
++
++# define USE_STATVFS
++
++#endif
++
++
+ #define MAJOR_VERSION 1
+ #define MINOR_VERSION 0
+ #define MAJOR_JOURNAL_VERSION 1
+@@ -825,12 +864,89 @@ write_all_data_and_close (int fd, char *data, gsize len)
+   return res;
+ }
+ 
++gboolean
++meta_builder_is_on_nfs (const char *filename)
++{
++#ifdef USE_STATFS
++  struct statfs statfs_buffer;
++  int statfs_result;
++#elif defined(USE_STATVFS) && defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
++  struct statvfs statfs_buffer;
++  int statfs_result;
++#endif
++  char *dirname;
++  gboolean res;
++
++  dirname = g_path_get_dirname (filename);
++
++  res = FALSE;
++
++#ifdef USE_STATFS
++
++# if STATFS_ARGS == 2
++  statfs_result = statfs (dirname, &statfs_buffer);
++# elif STATFS_ARGS == 4
++  statfs_result = statfs (dirname, &statfs_buffer,
++                          sizeof (statfs_buffer), 0);
++# endif
++  if (statfs_result == 0)
++#ifdef __OpenBSD__
++    res = strcmp(statfs_buffer.f_fstypename, MOUNT_NFS) == 0;
++#else
++    res = statfs_buffer.f_type == 0x6969;
++#endif
++
++#elif defined(USE_STATVFS) && defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
++  statfs_result = statvfs (dirname, &statfs_buffer);
++
++  if (statfs_result == 0)
++    res = strcmp (statfs_buffer.f_basetype, "nfs") == 0;
++#endif
++
++  g_free (dirname);
++
++  return res;
++}
++
+ static char *
+-get_journal_filename (const char *filename, guint32 random_tag)
++get_runtime_journal_dir (const char *tree_filename)
++{
++  const char *rd;
++  char *dbname;
++  char *real_path;
++  char *ret;
++
++  rd = g_get_user_runtime_dir ();
++  if (! rd || *rd == '\0')
++    return NULL;
++
++  real_path = g_build_filename (rd, "gvfs-metadata", NULL);
++  if (! g_file_test (real_path, G_FILE_TEST_EXISTS))
++    {
++      if (g_mkdir_with_parents (real_path, 0700) != 0)
++        {
++          g_free (real_path);
++          return NULL;
++        }
++    }
++
++  dbname = g_path_get_basename (tree_filename);
++  ret = g_build_filename (real_path, dbname, NULL);
++
++  g_free (dbname);
++  g_free (real_path);
++
++  return ret;
++}
++
++char *
++meta_builder_get_journal_filename (const char *tree_filename, guint32 random_tag)
+ {
+   const char *hexdigits = "0123456789abcdef";
+   char tag[9];
+   int i;
++  char *ret;
++  char *real_filename = NULL;
+ 
+   for (i = 7; i >= 0; i--)
+     {
+@@ -840,7 +956,18 @@ get_journal_filename (const char *filename, guint32 random_tag)
+ 
+   tag[8] = 0;
+ 
+-  return g_strconcat (filename, "-", tag, ".log", NULL);
++  if (meta_builder_is_on_nfs (tree_filename))
++    {
++      /* Put the journal in $XDG_RUNTIME_DIR to avoid file usage from concurrent clients */
++      real_filename = get_runtime_journal_dir (tree_filename);
++    }
++
++  if (! real_filename)
++    return g_strconcat (tree_filename, "-", tag, ".log", NULL);
++
++  ret = g_strconcat (real_filename, "-", tag, ".log", NULL);
++  g_free (real_filename);
++  return ret;
+ }
+ 
+ gboolean
+@@ -852,7 +979,7 @@ meta_builder_create_new_journal (const char *filename, guint32 random_tag)
+   gsize pos;
+   gboolean res;
+ 
+-  journal_name = get_journal_filename (filename, random_tag);
++  journal_name = meta_builder_get_journal_filename (filename, random_tag);
+ 
+   out = g_string_new (NULL);
+ 
+@@ -1055,7 +1182,7 @@ meta_builder_write (MetaBuilder *builder,
+ 	  munmap (data, RANDOM_TAG_OFFSET + 4);
+ 	  close (fd2);
+ 
+-	  old_log = get_journal_filename (filename, old_tag);
++	  old_log = meta_builder_get_journal_filename (filename, old_tag);
+ 	  g_unlink (old_log);
+ 	  g_free (old_log);
+ 	}
+diff --git a/metadata/metabuilder.h b/metadata/metabuilder.h
+index ad3876f..aa173c5 100644
+--- a/metadata/metabuilder.h
++++ b/metadata/metabuilder.h
+@@ -70,6 +70,9 @@ gboolean     meta_builder_write     (MetaBuilder *builder,
+ 				     const char  *filename);
+ gboolean     meta_builder_create_new_journal (const char *filename,
+ 				     guint32      random_tag);
++char *       meta_builder_get_journal_filename (const char *tree_filename,
++				     guint32      random_tag);
++gboolean     meta_builder_is_on_nfs (const char  *filename);
+ MetaFile *   metafile_new           (const char  *name,
+ 				     MetaFile    *parent);
+ void         metafile_free          (MetaFile    *file);
+diff --git a/metadata/metatree.c b/metadata/metatree.c
+index 8e2ab46..31ef8a6 100644
+--- a/metadata/metatree.c
++++ b/metadata/metatree.c
+@@ -9,43 +9,6 @@
+ #include <stdlib.h>
+ #include <time.h>
+ 
+-#if HAVE_SYS_STATFS_H
+-#include <sys/statfs.h>
+-#endif
+-#if HAVE_SYS_STATVFS_H
+-#include <sys/statvfs.h>
+-#endif
+-#if HAVE_SYS_VFS_H
+-#include <sys/vfs.h>
+-#elif HAVE_SYS_MOUNT_H
+-#if HAVE_SYS_PARAM_H
+-#include <sys/param.h>
+-#endif
+-#include <sys/mount.h>
+-#endif
+-
+-#if defined(HAVE_STATFS) && defined(HAVE_STATVFS)
+-/* Some systems have both statfs and statvfs, pick the
+-   most "native" for these */
+-# if !defined(HAVE_STRUCT_STATFS_F_BAVAIL)
+-   /* on solaris and irix, statfs doesn't even have the
+-      f_bavail field */
+-#  define USE_STATVFS
+-# else
+-  /* at least on linux, statfs is the actual syscall */
+-#  define USE_STATFS
+-# endif
+-
+-#elif defined(HAVE_STATFS)
+-
+-# define USE_STATFS
+-
+-#elif defined(HAVE_STATVFS)
+-
+-# define USE_STATVFS
+-
+-#endif
+-
+ #include "metatree.h"
+ #include "metabuilder.h"
+ #include <glib.h>
+@@ -278,50 +241,6 @@ meta_tree_clear (MetaTree *tree)
+ }
+ 
+ static gboolean
+-is_on_nfs (char *filename)
+-{
+-#ifdef USE_STATFS
+-  struct statfs statfs_buffer;
+-  int statfs_result;
+-#elif defined(USE_STATVFS) && defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
+-  struct statvfs statfs_buffer;
+-  int statfs_result;
+-#endif
+-  char *dirname;
+-  gboolean res;
+-
+-  dirname = g_path_get_dirname (filename);
+-
+-  res = FALSE;
+-
+-#ifdef USE_STATFS
+-
+-# if STATFS_ARGS == 2
+-  statfs_result = statfs (dirname, &statfs_buffer);
+-# elif STATFS_ARGS == 4
+-  statfs_result = statfs (dirname, &statfs_buffer,
+-			  sizeof (statfs_buffer), 0);
+-# endif
+-  if (statfs_result == 0)
+-#ifdef __OpenBSD__
+-    res = strcmp(statfs_buffer.f_fstypename, MOUNT_NFS) == 0;
+-#else
+-    res = statfs_buffer.f_type == 0x6969;
+-#endif
+-
+-#elif defined(USE_STATVFS) && defined(HAVE_STRUCT_STATVFS_F_BASETYPE)
+-  statfs_result = statvfs (dirname, &statfs_buffer);
+-
+-  if (statfs_result == 0)
+-    res = strcmp (statfs_buffer.f_basetype, "nfs") == 0;
+-#endif
+-
+-  g_free (dirname);
+-
+-  return res;
+-}
+-
+-static gboolean
+ link_to_tmp (const char *source, char *tmpl)
+ {
+   char *XXXXXX;
+@@ -431,7 +350,7 @@ meta_tree_init (MetaTree *tree)
+ 
+   retried = FALSE;
+  retry:
+-  tree->on_nfs = is_on_nfs (tree->filename);
++  tree->on_nfs = meta_builder_is_on_nfs (tree->filename);
+   fd = safe_open (tree, tree->filename, O_RDONLY);
+   if (fd == -1)
+     {
+@@ -845,24 +764,6 @@ meta_data_get_key (MetaTree *tree,
+   return dataent;
+ }
+ 
+-static char *
+-get_journal_filename (const char *filename, guint32 random_tag)
+-{
+-  const char *hexdigits = "0123456789abcdef";
+-  char tag[9];
+-  int i;
+-
+-  for (i = 7; i >= 0; i--)
+-    {
+-      tag[i] = hexdigits[random_tag % 0x10];
+-      random_tag >>= 4;
+-    }
+-
+-  tag[8] = 0;
+-
+-  return g_strconcat (filename, "-", tag, ".log", NULL);
+-}
+-
+ static void
+ meta_journal_free (MetaJournal *journal)
+ {
+@@ -1157,7 +1058,7 @@ meta_journal_open (MetaTree *tree, const char *filename, gboolean for_write, gui
+     open_flags = O_RDONLY;
+ 
+  retry:
+-  journal_filename = get_journal_filename (filename, tag);
++  journal_filename = meta_builder_get_journal_filename (filename, tag);
+   fd = safe_open (tree, journal_filename, open_flags);
+   g_free (journal_filename);
+   if (fd == -1)
+-- 
+1.8.1.4
+
diff --git a/gvfs-1.17.1-metadata-Use-shorter-writeout-timeout-on-NFS.patch b/gvfs-1.17.1-metadata-Use-shorter-writeout-timeout-on-NFS.patch
new file mode 100644
index 0000000..dd3eedf
--- /dev/null
+++ b/gvfs-1.17.1-metadata-Use-shorter-writeout-timeout-on-NFS.patch
@@ -0,0 +1,78 @@
+From c8e7d1375715545f28a0943ce7657330e3b70acd Mon Sep 17 00:00:00 2001
+From: Tomas Bzatek <tbzatek at redhat.com>
+Date: Mon, 13 May 2013 17:43:19 +0200
+Subject: [PATCH 5/6] metadata: Use shorter writeout timeout on NFS
+
+Since we've moved journal to a non-volatile storage, let's flush
+more often to minimize a chance of data loss.
+
+https://bugzilla.gnome.org/show_bug.cgi?id=637095
+---
+ metadata/meta-daemon.c | 12 +++++++++---
+ metadata/metatree.c    |  6 ++++++
+ metadata/metatree.h    |  1 +
+ 3 files changed, 16 insertions(+), 3 deletions(-)
+
+diff --git a/metadata/meta-daemon.c b/metadata/meta-daemon.c
+index ce34b73..f29e7f0 100644
+--- a/metadata/meta-daemon.c
++++ b/metadata/meta-daemon.c
+@@ -32,6 +32,7 @@
+ #include "metadata-dbus.h"
+ 
+ #define WRITEOUT_TIMEOUT_SECS 60
++#define WRITEOUT_TIMEOUT_SECS_NFS 15
+ 
+ typedef struct {
+   char *filename;
+@@ -67,10 +68,15 @@ writeout_timeout (gpointer data)
+ static void
+ tree_info_schedule_writeout (TreeInfo *info)
+ {
++  gboolean on_nfs;
++
+   if (info->writeout_timeout == 0)
+-    info->writeout_timeout =
+-      g_timeout_add_seconds (WRITEOUT_TIMEOUT_SECS,
+-			     writeout_timeout, info);
++    {
++      on_nfs = meta_tree_is_on_nfs (info->tree);
++      info->writeout_timeout =
++        g_timeout_add_seconds (on_nfs ? WRITEOUT_TIMEOUT_SECS_NFS : WRITEOUT_TIMEOUT_SECS,
++			       writeout_timeout, info);
++    }
+ }
+ 
+ static TreeInfo *
+diff --git a/metadata/metatree.c b/metadata/metatree.c
+index 31ef8a6..6aef33c 100644
+--- a/metadata/metatree.c
++++ b/metadata/metatree.c
+@@ -472,6 +472,12 @@ meta_tree_exists (MetaTree *tree)
+   return tree->fd != -1;
+ }
+ 
++gboolean
++meta_tree_is_on_nfs (MetaTree *tree)
++{
++  return tree->on_nfs;
++}
++
+ static GHashTable *cached_trees = NULL;
+ G_LOCK_DEFINE_STATIC (cached_trees);
+ 
+diff --git a/metadata/metatree.h b/metadata/metatree.h
+index 4bbb192..f24b4e9 100644
+--- a/metadata/metatree.h
++++ b/metadata/metatree.h
+@@ -66,6 +66,7 @@ void        meta_tree_unref          (MetaTree   *tree);
+ void        meta_tree_refresh        (MetaTree   *tree);
+ const char *meta_tree_get_filename   (MetaTree   *tree);
+ gboolean    meta_tree_exists         (MetaTree   *tree);
++gboolean    meta_tree_is_on_nfs      (MetaTree   *tree);
+ 
+ MetaKeyType meta_tree_lookup_key_type  (MetaTree                         *tree,
+ 					const char                       *path,
+-- 
+1.8.1.4
+
diff --git a/gvfs.spec b/gvfs.spec
index c8bb44d..f633816 100644
--- a/gvfs.spec
+++ b/gvfs.spec
@@ -1,7 +1,7 @@
 Summary: Backends for the gio framework in GLib
 Name: gvfs
 Version: 1.14.2
-Release: 3%{?dist}
+Release: 4%{?dist}
 License: GPLv3 and LGPLv2+
 Group: System Environment/Libraries
 URL: http://www.gtk.org
@@ -41,6 +41,16 @@ Patch0: gvfs-archive-integration.patch
 # from upstream
 Patch1: gvfs-1.15.4-tmpfilesd.patch
 
+# https://bugzilla.redhat.com/show_bug.cgi?id=561904
+# gvfsd-metadata producing large amounts of traffic to nfs server
+Patch10: gvfs-1.17.1-metadata-Be-more-resistive-to-broken-journal-files.patch
+Patch11: gvfs-1.17.1-metadata-Force-tree-re-read-after-successful-flush.patch
+Patch12: gvfs-1.17.1-metadata-Create-new-journal-if-it-doesn-t-exist.patch
+Patch13: gvfs-1.17.1-metadata-Put-journal-in-XDG_RUNTIME_DIR-for-shared-N.patch
+Patch14: gvfs-1.17.1-metadata-Use-shorter-writeout-timeout-on-NFS.patch
+Patch15: gvfs-1.17.1-metadata-Flush-all-scheduled-writeouts-on-daemon-exi.patch
+
+
 Obsoletes: gnome-mount <= 0.8
 Obsoletes: gnome-mount-nautilus-properties <= 0.8
 
@@ -155,6 +165,12 @@ to applications using gvfs.
 %setup -q
 %patch0 -p1 -b .archive-integration
 %patch1 -p1 -b .tmpfilesd
+%patch10 -p1 -b .metadata-Be-more-resistive-to-broken-journal-files
+%patch11 -p1 -b .metadata-Force-tree-re-read-after-successful-flush
+%patch12 -p1 -b .metadata-Create-new-journal-if-it-doesn-t-exist
+%patch13 -p1 -b .metadata-Put-journal-in-XDG_RUNTIME_DIR-for-shared-NFS
+%patch14 -p1 -b .metadata-Use-shorter-writeout-timeout-on-NFS
+%patch15 -p1 -b .metadata-Flush-all-scheduled-writeouts-on-daemon-exit
 
 %build
 # Needed for gvfs-0.2.1-archive-integration.patch
@@ -335,6 +351,9 @@ killall -USR1 gvfsd >&/dev/null || :
 %{_datadir}/gvfs/mounts/afp-browse.mount
 
 %changelog
+* Tue May 14 2013 Tomas Bzatek <tbzatek at redhat.com> - 1.14.2-4
+- Fix metadata issues on NFS homedirs (#561904)
+
 * Wed Feb  6 2013 Tomas Bzatek <tbzatek at redhat.com> - 1.14.2-3
 - Install systemd tmpfiles.d exclusion file for gvfs-fuse (#902743)
 


More information about the scm-commits mailing list