diff options
author | Ryan Lortie <desrt@desrt.ca> | 2012-07-08 19:02:35 -0400 |
---|---|---|
committer | Ryan Lortie <desrt@desrt.ca> | 2012-07-08 19:02:35 -0400 |
commit | f0637da31d404b0710b6e6911b9ee6d7a3593ca2 (patch) | |
tree | 4b9c9d3cea24a0f858919a45bc8e7f26ff851029 | |
parent | dd9c1a0cad35082724bba59235af031246c06ce5 (diff) | |
download | dconf-f0637da31d404b0710b6e6911b9ee6d7a3593ca2.tar.gz |
shm/: make some adjustments after research
After doing some research it has been discovered that ftruncate() isn't
sufficient for allocating space for a file. It can create a sparse file
and you don't find out that the space doesn't exist until you get a
SIGBUS later upon trying to write to the mmap region.
posix_fallocate() isn't doing quite what we want either (due to a flaky
glibc emulation of it when the native syscall is unavailable -- which is
the case for tmpfs).
Use a workaround based on pwrite() instead.
Also: mmap() really can't fail here except in the cases where we would
also abort due to g_malloc() failing, so we should just abort too.
-rw-r--r-- | shm/dconf-shm.c | 59 |
1 files changed, 36 insertions, 23 deletions
diff --git a/shm/dconf-shm.c b/shm/dconf-shm.c index 62439a0..10bfec9 100644 --- a/shm/dconf-shm.c +++ b/shm/dconf-shm.c @@ -71,22 +71,27 @@ dconf_shm_open (const gchar *name) goto out; } - if (ftruncate (fd, 1) != 0) + /* fruncate(fd, 1) is not sufficient because it does not actually + * ensure that the space is available (which could give a SIGBUS + * later). + * + * posix_fallocate() is also problematic because it is implemented in + * a racy way in the libc if unavailable for a particular filesystem + * (as is the case for tmpfs, which is where we probably are). + * + * By writing to the second byte in the file we ensure we don't + * overwrite the first byte (which is the one we care about). + */ + if (pwrite (fd, "", 1, 1) != 1) { g_critical ("failed to allocate file '%s': %s. dconf will not work properly.", filename, g_strerror (errno)); goto out; } memory = mmap (NULL, 1, PROT_READ, MAP_SHARED, fd, 0); + g_assert (memory != MAP_FAILED); g_assert (memory != NULL); - if (memory == MAP_FAILED) - { - g_critical ("failed to mmap file '%s': %s. dconf will not work properly.", filename, g_strerror (errno)); - memory = NULL; - goto out; - } - out: g_free (filename); close (fd); @@ -104,35 +109,43 @@ dconf_shm_flag (const gchar *name) shmdir = dconf_shm_get_shmdir (); filename = g_build_filename (shmdir, name, NULL); - fd = open (filename, O_WRONLY); + /* We need O_RDWR for PROT_WRITE. + * + * This is probably due to the fact that some architectures can't make + * write-only mappings (so they end up being readable as well). + */ + fd = open (filename, O_RDWR); if (fd >= 0) { - guint8 *shm; - - /* Easiest thing to do here would be write(fd, "\1", 1); but this - * causes problems on kernels (ie: OpenBSD) that don't sync up - * their filesystem cache with mmap()ed regions. + /* In theory we could have opened the file after a client created + * it but before they called pwrite(). Do the pwrite() ourselves + * to make sure (so we don't get SIGBUS in a moment). * - * Using mmap() works everywhere. + * If this fails then it will probably fail for the client too. + * If it doesn't then there's not really much we can do... */ - shm = mmap (NULL, 1, PROT_WRITE, MAP_SHARED, fd, 0); - - if (shm != MAP_FAILED) + if (pwrite (fd, "", 1, 1) == 1) { + guint8 *shm; + + /* It would ahve been easier for us to do write(fd, "\1", 1); + * but this causes problems on kernels (ie: OpenBSD) that + * don't sync up their filesystem cache with mmap()ed regions. + * + * Using mmap() works everywhere. + */ + shm = mmap (NULL, 1, PROT_WRITE, MAP_SHARED, fd, 0); + g_assert (shm != MAP_FAILED); + *shm = 1; munmap (shm, 1); } - else - g_warning ("failed to invalidate mmap file '%s': %s.", filename, g_strerror (errno)); close (fd); unlink (filename); } - else if (errno != ENOENT) - unlink (filename); - g_free (filename); } |