summaryrefslogtreecommitdiff
path: root/src/libotutil
diff options
context:
space:
mode:
authorColin Walters <walters@verbum.org>2017-10-04 15:06:31 -0400
committerAtomic Bot <atomic-devel@projectatomic.io>2017-10-04 20:42:39 +0000
commit5c7d2dd8be3606d9aec1e6835086f6b4dfcdb6a7 (patch)
treef796da733316a3dc1b36779d889140a624ed50df /src/libotutil
parentc511ca0fae0e352d52bb7d1a24b4c1d08c5a8ec9 (diff)
downloadostree-5c7d2dd8be3606d9aec1e6835086f6b4dfcdb6a7.tar.gz
Deduplicate and fix up our use of mmap()
Buried in this large patch is a logical fix: ``` - if (!map) - return glnx_throw_errno_prefix (error, "mmap"); + if (map == (void*)-1) + return glnx_null_throw_errno_prefix (error, "mmap"); ``` Which would have helped me debug another patch I was working on. But it turns out that actually correctly checking for errors from `mmap()` triggers lots of other bugs - basically because we sometimes handle zero-length variants (in detached metadata). When we start actually returning errors due to this, things break. (It wasn't a problem in practice before because most things looked at the zero size, not the data). Anyways there's a bigger picture issue here - a while ago we made a fix to only use `mmap()` for reading metadata from disk only if it was large enough (i.e. `>16k`). But that didn't help various other paths in the pull code and others that were directly doing the `mmap()`. Fix this by having a proper low level fs helper that does "read all data from fd+offset into GBytes", which handles the size check. Then the `GVariant` bits are just a clean layer on top of this. (At the small cost of an additional allocation) Side note: I had to remind myself, but the reason we can't just use `GMappedFile` here is it doesn't support passing an offset into `mmap()`. Closes: #1251 Approved by: jlebon
Diffstat (limited to 'src/libotutil')
-rw-r--r--src/libotutil/ot-fs-utils.c60
-rw-r--r--src/libotutil/ot-fs-utils.h5
-rw-r--r--src/libotutil/ot-variant-utils.c94
-rw-r--r--src/libotutil/ot-variant-utils.h24
4 files changed, 72 insertions, 111 deletions
diff --git a/src/libotutil/ot-fs-utils.c b/src/libotutil/ot-fs-utils.c
index 253de5b3..79ba3cf7 100644
--- a/src/libotutil/ot-fs-utils.c
+++ b/src/libotutil/ot-fs-utils.c
@@ -22,6 +22,7 @@
#include "ot-fs-utils.h"
#include "libglnx.h"
#include <sys/xattr.h>
+#include <sys/mman.h>
#include <gio/gunixinputstream.h>
#include <gio/gunixoutputstream.h>
@@ -144,22 +145,57 @@ ot_dfd_iter_init_allow_noent (int dfd,
return TRUE;
}
-GBytes *
-ot_file_mapat_bytes (int dfd,
- const char *path,
- GError **error)
-{
- glnx_fd_close int fd = openat (dfd, path, O_RDONLY | O_CLOEXEC);
- g_autoptr(GMappedFile) mfile = NULL;
+typedef struct {
+ gpointer addr;
+ gsize len;
+} MapData;
- if (fd < 0)
- return glnx_null_throw_errno_prefix (error, "openat(%s)", path);
+static void
+map_data_destroy (gpointer data)
+{
+ MapData *mdata = data;
+ (void) munmap (mdata->addr, mdata->len);
+ g_free (mdata);
+}
- mfile = g_mapped_file_new_from_fd (fd, FALSE, error);
- if (!mfile)
+/* Return a newly-allocated GBytes that refers to the contents of the file
+ * starting at offset @start. If the file is large enough, mmap() may be used.
+ */
+GBytes *
+ot_fd_readall_or_mmap (int fd,
+ goffset start,
+ GError **error)
+{
+ struct stat stbuf;
+ if (!glnx_fstat (fd, &stbuf, error))
return FALSE;
- return g_mapped_file_get_bytes (mfile);
+ /* http://stackoverflow.com/questions/258091/when-should-i-use-mmap-for-file-access */
+ if (start > stbuf.st_size)
+ return g_bytes_new_static (NULL, 0);
+ const gsize len = stbuf.st_size - start;
+ if (len > 16*1024)
+ {
+ /* The reason we don't use g_mapped_file_new_from_fd() here
+ * is it doesn't support passing an offset, which is actually
+ * used by the static delta code.
+ */
+ gpointer map = mmap (NULL, len, PROT_READ, MAP_PRIVATE, fd, start);
+ if (map == (void*)-1)
+ return glnx_null_throw_errno_prefix (error, "mmap");
+
+ MapData *mdata = g_new (MapData, 1);
+ mdata->addr = map;
+ mdata->len = len;
+
+ return g_bytes_new_with_free_func (map, len, map_data_destroy, mdata);
+ }
+
+ /* Fall through to plain read into a malloc buffer */
+ if (lseek (fd, start, SEEK_SET) < 0)
+ return glnx_null_throw_errno_prefix (error, "lseek");
+ /* Not cancellable since this should be small */
+ return glnx_fd_readall_bytes (fd, NULL, error);
}
/* Given an input stream, splice it to an anonymous file (O_TMPFILE).
diff --git a/src/libotutil/ot-fs-utils.h b/src/libotutil/ot-fs-utils.h
index 98fcd6a2..7eb811cd 100644
--- a/src/libotutil/ot-fs-utils.h
+++ b/src/libotutil/ot-fs-utils.h
@@ -85,8 +85,7 @@ ot_map_anonymous_tmpfile_from_content (GInputStream *instream,
GCancellable *cancellable,
GError **error);
-GBytes *ot_file_mapat_bytes (int dfd,
- const char *path,
- GError **error);
+GBytes *ot_fd_readall_or_mmap (int fd, goffset offset,
+ GError **error);
G_END_DECLS
diff --git a/src/libotutil/ot-variant-utils.c b/src/libotutil/ot-variant-utils.c
index d1981c8b..d4ae7d9d 100644
--- a/src/libotutil/ot-variant-utils.c
+++ b/src/libotutil/ot-variant-utils.c
@@ -25,7 +25,6 @@
#include <gio/gfiledescriptorbased.h>
#include <string.h>
-#include <sys/mman.h>
#include "otutil.h"
@@ -85,86 +84,25 @@ ot_util_variant_take_ref (GVariant *variant)
return g_variant_take_ref (variant);
}
+/* Create a GVariant in @out_variant that is backed by
+ * the data from @fd, starting at @start. If the data is
+ * large enough, mmap() may be used. @trusted is used
+ * by the GVariant core; see g_variant_new_from_data().
+ */
gboolean
-ot_util_variant_map_at (int dfd,
- const char *path,
- const GVariantType *type,
- OtVariantMapFlags flags,
- GVariant **out_variant,
- GError **error)
-{
- glnx_fd_close int fd = -1;
- const gboolean trusted = (flags & OT_VARIANT_MAP_TRUSTED) > 0;
-
- fd = openat (dfd, path, O_RDONLY | O_CLOEXEC);
- if (fd < 0)
- {
- if (errno == ENOENT && (flags & OT_VARIANT_MAP_ALLOW_NOENT) > 0)
- {
- *out_variant = NULL;
- return TRUE;
- }
- else
- {
- glnx_set_error_from_errno (error);
- g_prefix_error (error, "Opening %s: ", path);
- return FALSE;
- }
- }
-
- return ot_util_variant_map_fd (fd, 0, type, trusted, out_variant, error);
-}
-
-typedef struct {
- gpointer addr;
- gsize len;
-} VariantMapData;
-
-static void
-variant_map_data_destroy (gpointer data)
-{
- VariantMapData *mdata = data;
- (void) munmap (mdata->addr, mdata->len);
- g_free (mdata);
-}
-
-gboolean
-ot_util_variant_map_fd (int fd,
- goffset start,
- const GVariantType *type,
- gboolean trusted,
- GVariant **out_variant,
- GError **error)
+ot_variant_read_fd (int fd,
+ goffset start,
+ const GVariantType *type,
+ gboolean trusted,
+ GVariant **out_variant,
+ GError **error)
{
- gboolean ret = FALSE;
- gpointer map;
- struct stat stbuf;
- VariantMapData *mdata = NULL;
- gsize len;
-
- if (fstat (fd, &stbuf) != 0)
- {
- glnx_set_error_from_errno (error);
- goto out;
- }
-
- len = stbuf.st_size - start;
- map = mmap (NULL, len, PROT_READ, MAP_PRIVATE, fd, start);
- if (!map)
- {
- glnx_set_error_from_errno (error);
- goto out;
- }
-
- mdata = g_new (VariantMapData, 1);
- mdata->addr = map;
- mdata->len = len;
+ g_autoptr(GBytes) bytes = ot_fd_readall_or_mmap (fd, start, error);
+ if (!bytes)
+ return FALSE;
- ret = TRUE;
- *out_variant = g_variant_ref_sink (g_variant_new_from_data (type, map, len, trusted,
- variant_map_data_destroy, mdata));
- out:
- return ret;
+ *out_variant = g_variant_ref_sink (g_variant_new_from_bytes (type, bytes, trusted));
+ return TRUE;
}
GInputStream *
diff --git a/src/libotutil/ot-variant-utils.h b/src/libotutil/ot-variant-utils.h
index 135ae5d0..379e19ac 100644
--- a/src/libotutil/ot-variant-utils.h
+++ b/src/libotutil/ot-variant-utils.h
@@ -36,24 +36,12 @@ GHashTable *ot_util_variant_asv_to_hash_table (GVariant *variant);
GVariant * ot_util_variant_take_ref (GVariant *variant);
-typedef enum {
- OT_VARIANT_MAP_TRUSTED = (1 << 0),
- OT_VARIANT_MAP_ALLOW_NOENT = (1 << 1)
-} OtVariantMapFlags;
-
-gboolean ot_util_variant_map_at (int dfd,
- const char *path,
- const GVariantType *type,
- OtVariantMapFlags flags,
- GVariant **out_variant,
- GError **error);
-
-gboolean ot_util_variant_map_fd (int fd,
- goffset offset,
- const GVariantType *type,
- gboolean trusted,
- GVariant **out_variant,
- GError **error);
+gboolean ot_variant_read_fd (int fd,
+ goffset offset,
+ const GVariantType *type,
+ gboolean trusted,
+ GVariant **out_variant,
+ GError **error);
GInputStream *ot_variant_read (GVariant *variant);