summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Hughes <richard@hughsie.com>2013-12-03 08:54:09 +0000
committerRichard Hughes <richard@hughsie.com>2013-12-03 08:54:09 +0000
commit7a0c53d9de1056d591c536c0848d75059d5a67e6 (patch)
treefbc2ec03dac8d16976ab956accf4363e9235033e
parentd0db566168ce0044ff2ba4bc97bafdd175954c14 (diff)
downloadcolord-7a0c53d9de1056d591c536c0848d75059d5a67e6.tar.gz
Detect at runtime if the lcms2 function MemoryWrite is faulty
This allows us to switch to a slower, but non-buggy version of the profile serialization code that uses a temporary file.
-rw-r--r--lib/colord/cd-icc.c214
1 files changed, 175 insertions, 39 deletions
diff --git a/lib/colord/cd-icc.c b/lib/colord/cd-icc.c
index ac021f7..cba0e13 100644
--- a/lib/colord/cd-icc.c
+++ b/lib/colord/cd-icc.c
@@ -27,6 +27,7 @@
#include "config.h"
#include <glib.h>
+#include <glib/gstdio.h>
#include <lcms2.h>
#include <locale.h>
#include <string.h>
@@ -1239,6 +1240,170 @@ out:
}
/**
+ * cd_icc_check_error_cb:
+ **/
+static void
+cd_icc_check_error_cb (cmsContext context_id,
+ cmsUInt32Number code,
+ const char *message)
+{
+ gboolean *ret = (gboolean *) context_id;
+ *ret = FALSE;
+}
+
+/**
+ * cd_icc_check_lcms2_MemoryWrite:
+ *
+ * - Create a sRGB profile with some metadata
+ * - Serialize it to a memory blob
+ * - Load the resulting memory blob
+ *
+ * If there are any errors thrown, we're being compiled against a LCMS2 with
+ * the bad MemoryWrite() implementation.
+ **/
+static gboolean
+cd_icc_check_lcms2_MemoryWrite (void)
+{
+ cmsHANDLE dict;
+ cmsHPROFILE p;
+ cmsUInt32Number size;
+ gboolean ret = TRUE;
+ gchar *data;
+
+ /* setup temporary log handler */
+ cmsSetLogErrorHandler (cd_icc_check_error_cb);
+
+ /* create test data */
+ p = cmsCreate_sRGBProfileTHR (&ret);
+ dict = cmsDictAlloc (NULL);
+ cmsDictAddEntry (dict, L"1", L"2", NULL, NULL);
+ cmsWriteTag (p, cmsSigMetaTag, dict);
+ cmsSaveProfileToMem (p, NULL, &size);
+ data = g_malloc (size);
+ cmsSaveProfileToMem (p, data, &size);
+ cmsCloseProfile (p);
+ cmsDictFree (dict);
+
+ /* open file */
+ p = cmsOpenProfileFromMemTHR (&ret, data, size);
+ dict = cmsReadTag (p, cmsSigMetaTag);
+ g_assert (dict != (gpointer) 0x01); /* appease GCC */
+ cmsCloseProfile (p);
+ g_free (data);
+ cmsSetLogErrorHandler (NULL);
+ return ret;
+}
+
+/**
+ * cd_icc_serialize_profile_fallback:
+ **/
+static GBytes *
+cd_icc_serialize_profile_fallback (CdIcc *icc, GError **error)
+{
+ CdIccPrivate *priv = icc->priv;
+ gboolean ret;
+ GBytes *data = NULL;
+ gchar *data_tmp = NULL;
+ gchar *temp_file = NULL;
+ GError *error_local = NULL;
+ gint fd;
+ gsize length = 0;
+
+ /* get unique temp file */
+ fd = g_file_open_tmp ("colord-XXXXXX.icc", &temp_file, &error_local);
+ if (fd < 0) {
+ g_set_error (error,
+ CD_ICC_ERROR,
+ CD_ICC_ERROR_FAILED_TO_SAVE,
+ "failed to open temp file: %s",
+ error_local->message);
+ g_error_free (error_local);
+ goto out;
+ }
+
+ /* dump to a file, avoiding the problematic call to MemoryWrite() */
+ ret = cmsSaveProfileToFile (priv->lcms_profile, temp_file);
+ if (!ret) {
+ g_set_error_literal (error,
+ CD_ICC_ERROR,
+ CD_ICC_ERROR_FAILED_TO_SAVE,
+ "failed to dump ICC file to temp file");
+ goto out;
+ }
+ ret = g_file_get_contents (temp_file, &data_tmp, &length, NULL);
+ if (!ret) {
+ g_set_error_literal (error,
+ CD_ICC_ERROR,
+ CD_ICC_ERROR_FAILED_TO_SAVE,
+ "failed to load temp file");
+ goto out;
+ }
+ /* success */
+ data = g_bytes_new (data_tmp, length);
+out:
+ if (fd >= 0)
+ close (fd);
+ if (temp_file != NULL)
+ g_unlink (temp_file);
+ g_free (temp_file);
+ g_free (data_tmp);
+ return data;
+}
+
+/**
+ * cd_icc_serialize_profile:
+ **/
+static GBytes *
+cd_icc_serialize_profile (CdIcc *icc, GError **error)
+{
+ CdIccPrivate *priv = icc->priv;
+ cmsUInt32Number length = 0;
+ gboolean ret;
+ GBytes *data = NULL;
+ gchar *data_tmp = NULL;
+
+ /* get size of profile */
+ ret = cmsSaveProfileToMem (priv->lcms_profile,
+ NULL, &length);
+ if (!ret) {
+ g_set_error_literal (error,
+ CD_ICC_ERROR,
+ CD_ICC_ERROR_FAILED_TO_SAVE,
+ "failed to dump ICC file");
+ goto out;
+ }
+
+ /* sanity check to 16Mb */
+ if (length == 0 || length > 16 * 1024 * 1024) {
+ g_set_error (error,
+ CD_ICC_ERROR,
+ CD_ICC_ERROR_FAILED_TO_SAVE,
+ "failed to save ICC file, requested %u "
+ "bytes and limit is 16Mb",
+ length);
+ goto out;
+ }
+
+ /* allocate and get profile data */
+ data_tmp = g_new0 (gchar, length);
+ ret = cmsSaveProfileToMem (priv->lcms_profile,
+ data_tmp, &length);
+ if (!ret) {
+ g_set_error_literal (error,
+ CD_ICC_ERROR,
+ CD_ICC_ERROR_FAILED_TO_SAVE,
+ "failed to dump ICC file to memory");
+ goto out;
+ }
+
+ /* success */
+ data = g_bytes_new (data_tmp, length);
+out:
+ g_free (data_tmp);
+ return data;
+}
+
+/**
* cd_icc_save_data:
* @icc: a #CdIcc instance.
* @flags: a set of #CdIccSaveFlags
@@ -1257,12 +1422,10 @@ cd_icc_save_data (CdIcc *icc,
{
CdIccPrivate *priv = icc->priv;
cmsHANDLE dict = NULL;
- cmsUInt32Number length = 0;
const gchar *key;
const gchar *value;
gboolean ret = FALSE;
GBytes *data = NULL;
- gchar *data_tmp = NULL;
GList *l;
GList *md_keys = NULL;
guint i;
@@ -1395,48 +1558,21 @@ cd_icc_save_data (CdIcc *icc,
goto out;
}
- /* get size of profile */
- ret = cmsSaveProfileToMem (priv->lcms_profile,
- NULL, &length);
- if (!ret) {
- g_set_error_literal (error,
- CD_ICC_ERROR,
- CD_ICC_ERROR_FAILED_TO_SAVE,
- "failed to dump ICC file");
- goto out;
- }
-
- /* sanity check to 16Mb */
- if (length == 0 || length > 16 * 1024 * 1024) {
- ret = FALSE;
- g_set_error (error,
- CD_ICC_ERROR,
- CD_ICC_ERROR_FAILED_TO_SAVE,
- "failed to save ICC file, requested %u "
- "bytes and limit is 16Mb",
- length);
- goto out;
- }
-
- /* allocate and get profile data */
- data_tmp = g_new0 (gchar, length);
- ret = cmsSaveProfileToMem (priv->lcms_profile,
- data_tmp, &length);
- if (!ret) {
- g_set_error_literal (error,
- CD_ICC_ERROR,
- CD_ICC_ERROR_FAILED_TO_SAVE,
- "failed to dump ICC file to memory");
- goto out;
+ /* LCMS2 doesn't serialize some tags properly when using the function
+ * cmsSaveProfileToMem() twice, so until lcms 2.6 is released we have to
+ * use a fallback version which uses a temporary file.
+ * See https://github.com/mm2/Little-CMS/commit/1d2643cb8153c48dcfdee3d5cda43a38f7e719e2
+ * for the fix. */
+ if (cd_icc_check_lcms2_MemoryWrite ()) {
+ data = cd_icc_serialize_profile (icc, error);
+ } else {
+ g_debug ("Using file serialization due to bad MemoryWrite.");
+ data = cd_icc_serialize_profile_fallback (icc, error);
}
-
- /* success */
- data = g_bytes_new (data_tmp, length);
out:
g_list_free (md_keys);
if (dict != NULL)
cmsDictFree (dict);
- g_free (data_tmp);
return data;
}