From 81521e8c1fef4911901278bff90746d2b3cc6ebf Mon Sep 17 00:00:00 2001 From: Ryan Lortie Date: Tue, 23 Oct 2012 10:14:43 +0200 Subject: wip: NFS on the library side --- engine/Makefile.am | 1 + engine/dconf-engine-source-private.h | 1 + engine/dconf-engine-source-user-nfs.c | 93 +++++++++++++++++ engine/dconf-engine-source.c | 17 +++- service/dconf-nfs-writer.c | 182 ++++++++++++++++++++++++++++++++++ shm/Makefile.am | 3 +- shm/dconf-shm-nfs-check.c | 63 ++++++++++++ shm/dconf-shm.c | 2 +- shm/dconf-shm.h | 6 ++ tests/dconf-mock-shm.c | 12 +++ 10 files changed, 375 insertions(+), 5 deletions(-) create mode 100644 engine/dconf-engine-source-user-nfs.c create mode 100644 service/dconf-nfs-writer.c create mode 100644 shm/dconf-shm-nfs-check.c diff --git a/engine/Makefile.am b/engine/Makefile.am index b69c70c..8eda5f1 100644 --- a/engine/Makefile.am +++ b/engine/Makefile.am @@ -9,6 +9,7 @@ libdconf_engine_a_SOURCES = \ dconf-engine-source-private.h \ dconf-engine-source.h \ dconf-engine-source-user.c \ + dconf-engine-source-user-nfs.c \ dconf-engine-source-system.c \ dconf-engine-source.c \ dconf-engine.h \ diff --git a/engine/dconf-engine-source-private.h b/engine/dconf-engine-source-private.h index 822354a..57840dc 100644 --- a/engine/dconf-engine-source-private.h +++ b/engine/dconf-engine-source-private.h @@ -26,6 +26,7 @@ #include "dconf-engine-source.h" G_GNUC_INTERNAL extern const DConfEngineSourceVTable dconf_engine_source_user_vtable; +G_GNUC_INTERNAL extern const DConfEngineSourceVTable dconf_engine_source_user_nfs_vtable; G_GNUC_INTERNAL extern const DConfEngineSourceVTable dconf_engine_source_system_vtable; #endif /* __dconf_engine_source_private_h__ */ diff --git a/engine/dconf-engine-source-user-nfs.c b/engine/dconf-engine-source-user-nfs.c new file mode 100644 index 0000000..336fa04 --- /dev/null +++ b/engine/dconf-engine-source-user-nfs.c @@ -0,0 +1,93 @@ +/* + * Copyright © 2010 Codethink Limited + * Copyright © 2012 Canonical Limited + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Ryan Lortie + */ + +#include "dconf-engine-source-private.h" + +#include "../shm/dconf-shm.h" +#include "dconf-engine.h" +#include +#include +#include + +static void +dconf_engine_source_user_nfs_init (DConfEngineSource *source) +{ + GError *error = NULL; + GVariant *reply; + + source->bus_type = G_BUS_TYPE_SESSION; + source->bus_name = g_strdup ("ca.desrt.dconf"); + source->object_path = g_strdup_printf ("/ca/desrt/dconf/Writer/%s", source->name); + source->writable = TRUE; + + /* We need to get the dconf-service to come online and notice that + * we're on an NFS home directory. In that case it will copy the + * given database into the XDG_RUNTIME_DIR which is where we will + * access it. + * + * This prevents us from doing mmap() on a file on NFS (which often + * results in us seeing SIGBUS). + */ + reply = dconf_engine_dbus_call_sync_func (G_BUS_TYPE_SESSION, source->bus_name, source->object_path, + "ca.desrt.dconf.Writer", "Init", NULL, G_VARIANT_TYPE_UNIT, &error); + + if (reply) + g_variant_unref (reply); + else + { + g_warning ("Trying to start the dconf service failed: %s. Expect problems.", error->message); + g_error_free (error); + } +} + +static gboolean +dconf_engine_source_user_nfs_needs_reopen (DConfEngineSource *source) +{ + return !source->values || !gvdb_table_is_valid (source->values); +} + +static GvdbTable * +dconf_engine_source_user_nfs_reopen (DConfEngineSource *source) +{ + GvdbTable *table; + gchar *filename; + + filename = g_build_filename (dconf_shm_get_shmdir (), source->name, NULL); + table = gvdb_table_new (filename, FALSE, NULL); + g_free (filename); + + return table; +} + +static void +dconf_engine_source_user_nfs_finalize (DConfEngineSource *source) +{ +} + +G_GNUC_INTERNAL +const DConfEngineSourceVTable dconf_engine_source_user_nfs_vtable = { + .instance_size = sizeof (DConfEngineSource), + .init = dconf_engine_source_user_nfs_init, + .finalize = dconf_engine_source_user_nfs_finalize, + .needs_reopen = dconf_engine_source_user_nfs_needs_reopen, + .reopen = dconf_engine_source_user_nfs_reopen +}; diff --git a/engine/dconf-engine-source.c b/engine/dconf-engine-source.c index 4eb7faa..68b10de 100644 --- a/engine/dconf-engine-source.c +++ b/engine/dconf-engine-source.c @@ -71,6 +71,15 @@ dconf_engine_source_refresh (DConfEngineSource *source) return FALSE; } +static const DConfEngineSourceVTable * +dconf_engine_source_get_user_vtable (void) +{ + if (dconf_shm_homedir_is_native ()) + return &dconf_engine_source_user_vtable; + else + return &dconf_engine_source_user_nfs_vtable; +} + DConfEngineSource * dconf_engine_source_new (const gchar *description) { @@ -94,7 +103,7 @@ dconf_engine_source_new (const gchar *description) /* Check if the part before the colon is "user-db"... */ if ((colon == description + 7) && memcmp (description, "user-db", 7) == 0) - vtable = &dconf_engine_source_user_vtable; + vtable = dconf_engine_source_get_user_vtable (); /* ...or "system-db" */ else if ((colon == description + 9) && memcmp (description, "system-db", 9) == 0) @@ -122,10 +131,12 @@ dconf_engine_source_new (const gchar *description) DConfEngineSource * dconf_engine_source_new_default (void) { + const DConfEngineSourceVTable *vtable; DConfEngineSource *source; - source = g_malloc0 (dconf_engine_source_user_vtable.instance_size); - source->vtable = &dconf_engine_source_user_vtable; + vtable = dconf_engine_source_get_user_vtable (); + source = g_malloc0 (vtable->instance_size); + source->vtable = vtable; source->name = g_strdup ("user"); source->vtable->init (source); diff --git a/service/dconf-nfs-writer.c b/service/dconf-nfs-writer.c new file mode 100644 index 0000000..69675ef --- /dev/null +++ b/service/dconf-nfs-writer.c @@ -0,0 +1,182 @@ +/* + * Copyright © 2010 Codethink Limited + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Ryan Lortie + */ + +#include "dconf-writer.h" + +#include "../gvdb/gvdb-reader.h" + +#include +#include + +typedef DConfWriterClass DConfNfsWriterClass; + +typedef struct +{ + DConfWriter parent_instance; + + gchar *lockfile; + gchar *filename; + gint lock_fd; +} DConfNfsWriter; + +G_DEFINE_TYPE (DConfNfsWriter, dconf_nfs_writer, DCONF_TYPE_WRITER) + +static void +dconf_nfs_writer_constructed (GObject *object) +{ + DConfNfsWriter *nfs = (DConfNfsWriter *) object; + DConfWriter *writer = DCONF_WRITER (object); + + G_OBJECT_CLASS (dconf_nfs_writer_parent_class)->constructed (object); + + nfs->filename = g_build_filename (g_get_user_config_dir (), "dconf", writer->name, NULL); +} + +static DConfChangeset * +dconf_nfs_writer_diff (DConfNfsWriter *nfs, + GHashTable *old, + GError **error) +{ + DConfChangeset *changeset; + GvdbTable *new; + GBytes *bytes; + + { + gchar *contents; + gsize size; + + if (!g_file_get_contents (nfs->filename, &contents, &size, error)) + return NULL; + + bytes = g_bytes_new_take (contents, size); + } + + new = gvdb_table_new (bytes, FALSE, error); + + g_bytes_unref (bytes); + + if (new == NULL) + return NULL; +} + +static gboolean +dconf_nfs_writer_begin (DConfWriter *writer, + GError **error) +{ + DConfNfsWriter *nfs = (DConfNfsWriter *) writer; + DConfChangeset *changeset; + struct flock lock; + gint fd; + + nfs->lock_fd = open (nfs->lockfile, O_CREAT | O_WRONLY, 0600); + + if (nfs->lock_fd == -1) + { + gint saved_errno = errno; + + g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (saved_errno), + "Cannot open dconf lockfile %s: %s", nfs->lockfile, g_strerror (saved_errno)); + goto out; + } + + lock.l_whence = SEEK_SET; + lock.l_type = F_WRLCK; + lock.l_start = 0; + lock.l_len = 0; + + if (fcntl (nfs->lock_fd, F_SETLKW, &lock) != 0) + { + gint saved_errno = errno; + + g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (saved_errno), + "Unable to lock dconf lockfile %s: %s", nfs->lockfile, g_strerror (saved_errno)); + goto out; + } + + if (!DCONF_WRITER_CLASS (dconf_nfs_writer_parent_class)->begin (writer, error)) + goto out; + + changeset = dconf_nfs_writer_diff (nfs, writer->uncommited_values, error); + if (changeset == NULL) + goto out; + + if (!dconf_changeset_is_empty (changeset)) + dconf_writer_change (writer, changeset, "(updated from nfs home directory)"); + + dconf_changeset_unref (changeset); + + return TRUE; + +out: + if (writer->uncommited_values) + DCONF_WRITER_CLASS (dconf_nfs_writer_parent_class)->end (writer); + + if (nfs->lock_fd != -1) + { + close (nfs->lock_fd); + nfs->lock_fd = -1; + } + + return FALSE; +} + +static gboolean +dconf_nfs_writer_commit (DConfWriter *writer, + GError **error) +{ + return DCONF_WRITER_CLASS (dconf_nfs_writer_parent_class)->commit (writer, error); +} + +static void +dconf_nfs_writer_end (DConfWriter *writer) +{ + DConfNfsWriter *nfs = (DConfNfsWriter *) writer; + + DCONF_WRITER_CLASS (dconf_nfs_writer_parent_class)->end (writer); + + if (nfs->lock_fd != -1) + { + close (nfs->lock_fd); + nfs->lock_fd = -1; + } +} + +static void +dconf_nfs_writer_init (DConfNfsWriter *nfs) +{ + dconf_writer_set_native (DCONF_WRITER (nfs), FALSE); + + nfs->lock_fd = -1; +} + +static void +dconf_nfs_writer_class_init (DConfNfsWriterClass *class) +{ + class->begin = dconf_nfs_writer_begin; + class->commit = dconf_nfs_writer_commit; + class->end = dconf_nfs_writer_end; +} + +DConfWriter * +dconf_nfs_writer_new (const gchar *name) +{ + return g_object_new (dconf_nfs_writer_get_type (), NULL); +} diff --git a/shm/Makefile.am b/shm/Makefile.am index ffa2fd3..2417500 100644 --- a/shm/Makefile.am +++ b/shm/Makefile.am @@ -5,7 +5,8 @@ noinst_LIBRARIES = libdconf-shm.a libdconf-shm-shared.a libdconf_shm_a_CFLAGS = $(glib_CFLAGS) libdconf_shm_a_SOURCES = \ dconf-shm.h \ - dconf-shm.c + dconf-shm.c \ + dconf-shm-nfs-check.c libdconf_shm_shared_a_CFLAGS = $(libdconf_shm_a_CFLAGS) -fPIC -DPIC libdconf_shm_shared_a_SOURCES = $(libdconf_shm_a_SOURCES) diff --git a/shm/dconf-shm-nfs-check.c b/shm/dconf-shm-nfs-check.c new file mode 100644 index 0000000..7f1b199 --- /dev/null +++ b/shm/dconf-shm-nfs-check.c @@ -0,0 +1,63 @@ +/* + * Copyright © 2012 Canonical Limited + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Ryan Lortie + */ + +#include "dconf-shm.h" + +#include + +#include + +#ifndef ECRYPTFS_SUPER_MAGIC +#define ECRYPTFS_SUPER_MAGIC 0xf15f +#endif + +#ifndef NFS_SUPER_MAGIC +#define NFS_SUPER_MAGIC 0x6969 +#endif + +/* returns TRUE if the filesystem is capable */ +static gboolean +dconf_shm_check (const gchar *filename) +{ + struct statfs buf; + + if (statfs (filename, &buf) != 0) + return FALSE; + + return buf.f_type != NFS_SUPER_MAGIC && buf.f_type != ECRYPTFS_SUPER_MAGIC; +} + +gboolean +dconf_shm_homedir_is_native (void) +{ + static gsize homedir_is_native; + + if (g_once_init_enter (&homedir_is_native)) + { + gboolean is_native; + + is_native = dconf_shm_check (g_get_home_dir ()); + + g_once_init_leave (&homedir_is_native, is_native + 1); + } + + return homedir_is_native - 1; +} diff --git a/shm/dconf-shm.c b/shm/dconf-shm.c index d291305..84bcc33 100644 --- a/shm/dconf-shm.c +++ b/shm/dconf-shm.c @@ -27,7 +27,7 @@ #include #include -static gchar * +const gchar * dconf_shm_get_shmdir (void) { static gchar *shmdir; diff --git a/shm/dconf-shm.h b/shm/dconf-shm.h index c1f136c..3d77f16 100644 --- a/shm/dconf-shm.h +++ b/shm/dconf-shm.h @@ -24,6 +24,9 @@ #include +G_GNUC_INTERNAL +const gchar * dconf_shm_get_shmdir (void); + G_GNUC_INTERNAL guint8 * dconf_shm_open (const gchar *name); G_GNUC_INTERNAL @@ -37,4 +40,7 @@ dconf_shm_is_flagged (const guint8 *shm) return shm == NULL || *shm != 0; } +G_GNUC_INTERNAL +gboolean dconf_shm_homedir_is_native (void); + #endif /* __dconf_shm_h__ */ diff --git a/tests/dconf-mock-shm.c b/tests/dconf-mock-shm.c index 588667e..33384ac 100644 --- a/tests/dconf-mock-shm.c +++ b/tests/dconf-mock-shm.c @@ -122,3 +122,15 @@ dconf_mock_shm_assert_log (const gchar *expected_log) g_string_truncate (dconf_mock_shm_log, 0); g_mutex_unlock (&dconf_mock_shm_lock); } + +const gchar * +dconf_shm_get_shmdir (void) +{ + g_assert_not_reached (); +} + +gboolean +dconf_shm_homedir_is_native (void) +{ + return TRUE; +} -- cgit v1.2.1