diff options
author | David King <amigadave@amigadave.com> | 2014-11-26 20:02:30 +0000 |
---|---|---|
committer | David King <amigadave@amigadave.com> | 2014-11-26 22:30:12 +0000 |
commit | 90253305a2cf14c761d479b9072782a1c8d5e33f (patch) | |
tree | 6d117f27d8e079a631574528a33f39dc0b405422 | |
parent | 71944b1bfd2cff57e889b806d001458dce6fa2b5 (diff) | |
download | glib-wip/gbytes-memfd.tar.gz |
WIP Add memfd support to GByteswip/gbytes-memfd
This code is untested.
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | docs/reference/glib/glib-sections.txt | 1 | ||||
-rw-r--r-- | glib/gbytes.c | 188 | ||||
-rw-r--r-- | glib/gbytes.h | 3 |
4 files changed, 182 insertions, 12 deletions
diff --git a/configure.ac b/configure.ac index ced92b22f..2703a32df 100644 --- a/configure.ac +++ b/configure.ac @@ -781,7 +781,7 @@ AC_CHECK_HEADERS([sys/param.h sys/resource.h mach/mach_time.h]) AC_CHECK_HEADERS([sys/select.h stdint.h inttypes.h sched.h malloc.h]) AC_CHECK_HEADERS([sys/vfs.h sys/vmount.h sys/statfs.h sys/statvfs.h sys/filio.h]) AC_CHECK_HEADERS([mntent.h sys/mnttab.h sys/vfstab.h sys/mntctl.h fstab.h]) -AC_CHECK_HEADERS([linux/magic.h sys/prctl.h]) +AC_CHECK_HEADERS([linux/magic.h linux/memfd.h sys/prctl.h]) # Some versions of MSC lack these AC_CHECK_HEADERS([dirent.h sys/time.h]) diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index 20b71013c..b07ea7180 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -2470,6 +2470,7 @@ g_byte_array_free_to_bytes GBytes g_bytes_new g_bytes_new_take +g_bytes_new_take_fd g_bytes_new_static g_bytes_new_with_free_func g_bytes_new_from_bytes diff --git a/glib/gbytes.c b/glib/gbytes.c index 1520d7609..5ef8bf1dd 100644 --- a/glib/gbytes.c +++ b/glib/gbytes.c @@ -26,12 +26,53 @@ #include <glib/garray.h> #include <glib/gstrfuncs.h> #include <glib/gatomic.h> +#include <glib/gmappedfile.h> +#include <glib/gstdio.h> #include <glib/gslice.h> #include <glib/gtestutils.h> #include <glib/gmem.h> #include <glib/gmessages.h> #include <string.h> +#include <errno.h> +#include <sys/stat.h> +#include <sys/types.h> +#ifdef HAVE_LINUX_MEMFD_H +#include <fcntl.h> +#include <linux/memfd.h> +#include <unistd.h> +/* FIXME: Use a similar approach to systemd/src/shared/missing.h */ +#ifndef F_LINUX_SPECIFIC_BASE +#define F_LINUX_SPECIFIC_BASE 1024 +#endif + +#ifndef F_SETPIPE_SZ +#define F_SETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 7) +#endif + +#ifndef F_GETPIPE_SZ +#define F_GETPIPE_SZ (F_LINUX_SPECIFIC_BASE + 8) +#endif + +#ifndef F_ADD_SEALS +#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9) +#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10) + +#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */ +#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */ +#define F_SEAL_GROW 0x0004 /* prevent file from growing */ +#define F_SEAL_WRITE 0x0008 /* prevent writes */ +#endif + +#ifndef MFD_ALLOW_SEALING +#define MFD_ALLOW_SEALING 0x0002U +#endif + +#ifndef MFD_CLOEXEC +#define MFD_CLOEXEC 0x0001U +#endif +/* end FIXME. */ +#endif /** * GBytes: @@ -69,8 +110,10 @@ struct _GBytes gconstpointer data; /* may be NULL iff (size == 0) */ gsize size; /* may be 0 */ gint ref_count; + gint fd; /* -1 when data is not backed by an fd. */ GDestroyNotify free_func; - gpointer user_data; + gpointer user_data; /* used to store a GMappedFile when backed by an fd (and + * only after g_bytes_get_data() has been called. */ }; /** @@ -96,6 +139,104 @@ g_bytes_new (gconstpointer data, return g_bytes_new_take (g_memdup (data, size), size); } +/* + * _g_bytes_new_internal: + * Same as g_bytes_new_with_free_func() except for the fd parameter, used by + * g_bytes_new_take_fd(). + */ +static GBytes * +_g_bytes_new_internal (gconstpointer data, + gsize size, + gint fd, + GDestroyNotify free_func, + gpointer user_data) +{ + GBytes *bytes; + + g_return_val_if_fail (data != NULL || fd != -1, NULL); + + bytes = g_slice_new (GBytes); + bytes->data = data; + bytes->size = size; + bytes->fd = fd; + bytes->free_func = free_func; + bytes->user_data = user_data; + bytes->ref_count = 1; + + return (GBytes *)bytes; +} + +static void +unref_mapped_file (gpointer data) +{ + if (data != NULL) + { + g_mapped_file_unref ((GMappedFile *)data); + } +} + +/** + * g_bytes_new_take_fd: + * @fd: the file descriptor containg data from which to create a #GBytes + * + * Creates a new #GBytes from @fd. + * + * This call "consumes" the file descriptor, transferring ownership to the + * returned #GBytes. + * + * Returns: (transfer full): a new #GBytes + * + * Since: 2.44 + */ +GBytes * +g_bytes_new_take_fd (gint fd) +{ +#ifdef HAVE_LINUX_MEMFD_H + gint seals; + const gint IMMUTABLE_SEALS = F_SEAL_WRITE | + F_SEAL_SHRINK | + F_SEAL_GROW | + F_SEAL_SEAL; +#endif /* HAVE_LINUX_MEMFD_H */ + gint error; + struct stat sb; + +#ifdef HAVE_LINUX_MEMFD_H + /* FIXME: seal the fd if possible (only on Linux 3.17+, and if the fd was + * created with memfd_create()). */ + /* FIXME: Check if the provided fd is one of the standard ones, and warn? */ + seals = fcntl (fd, F_GET_SEALS); + if (seals == -1) + { + g_debug ("Retrieving fd seals failed: %s", g_strerror (errno)); + } + + /* Seal the fd, if it is not already. */ + if ((seals & (IMMUTABLE_SEALS)) >= IMMUTABLE_SEALS) + { + g_debug ("%s", "fd already sealed"); + } + else + { + error = fcntl (fd, F_ADD_SEALS, IMMUTABLE_SEALS); + if (error == -1) + { + g_debug ("fd sealing failed: %s", g_strerror (errno)); + /* FIXME: use shm_open() on non-Linux, or when memfd_create() is + * otherwise unavailable? */ + } + } +#endif /* HAVE_LINUX_MEMFD_H */ + + error = fstat (fd, &sb); + if (error == -1) + { + g_debug ("fstat() on fd failed: %s", g_strerror (errno)); + } + + return _g_bytes_new_internal (NULL, sb.st_size, fd, NULL, unref_mapped_file); +} + /** * g_bytes_new_take: * @data: (transfer full) (array length=size) (element-type guint8) (allow-none): @@ -176,18 +317,9 @@ g_bytes_new_with_free_func (gconstpointer data, GDestroyNotify free_func, gpointer user_data) { - GBytes *bytes; - g_return_val_if_fail (data != NULL || size == 0, NULL); - bytes = g_slice_new (GBytes); - bytes->data = data; - bytes->size = size; - bytes->free_func = free_func; - bytes->user_data = user_data; - bytes->ref_count = 1; - - return (GBytes *)bytes; + return _g_bytes_new_internal (data, size, -1, free_func, user_data); } /** @@ -242,9 +374,38 @@ gconstpointer g_bytes_get_data (GBytes *bytes, gsize *size) { + GError *error = NULL; + g_return_val_if_fail (bytes != NULL, NULL); + if (size) *size = bytes->size; + + if (bytes->fd > -1) + { + if (bytes->user_data == NULL) + { + bytes->user_data = g_mapped_file_new_from_fd (bytes->fd, FALSE, + &error); + if (bytes->user_data == NULL) + { + g_debug ("Failed to map fd: %s", error->message); + *size = 0; + return NULL; + } + + *size = g_mapped_file_get_length (bytes->user_data); + if (*size != bytes->size) + { + g_debug ("Mapped size differs from that returned by fstat(): %" + G_GSIZE_FORMAT " versus %" G_GOFFSET_FORMAT, *size, + bytes->size); + } + + bytes->data = g_mapped_file_get_contents (bytes->user_data); + } + } + return bytes->data; } @@ -461,6 +622,11 @@ g_bytes_unref_to_data (GBytes *bytes, * Copy: Non g_malloc (or compatible) allocator, or static memory, * so we have to copy, and then unref. */ + if (!bytes->data && bytes->fd > -1) + { + /* Map the fd if the data is to be copied. */ + g_bytes_get_data (bytes, NULL); + } result = g_memdup (bytes->data, bytes->size); *size = bytes->size; g_bytes_unref (bytes); diff --git a/glib/gbytes.h b/glib/gbytes.h index 24f1856fb..77bfd30ef 100644 --- a/glib/gbytes.h +++ b/glib/gbytes.h @@ -39,6 +39,9 @@ GLIB_AVAILABLE_IN_ALL GBytes * g_bytes_new_take (gpointer data, gsize size); +GLIB_AVAILABLE_IN_2_44 +GBytes * g_bytes_new_take_fd (gint fd); + GLIB_AVAILABLE_IN_ALL GBytes * g_bytes_new_static (gconstpointer data, gsize size); |