Add an icon cache validator.

2007-05-01  Matthias Clasen  <mclasen@redhat.com>

        * gtk/gtkiconcachvalidator.[hc]: Add an icon cache validator.

        * gtk/updateiconcache.c: Validate the generated cache before
        moving it in place. Also add a --validate option to validate
        an existing icon cache.

        * gtk/gtkiconcache.c: Validate icon caches before using them.

        * gtk/Makefile.am: Integrate it.


svn path=/trunk/; revision=17753
This commit is contained in:
Matthias Clasen 2007-05-01 20:00:17 +00:00 committed by Matthias Clasen
parent 6bade15e60
commit 2a80113304
6 changed files with 513 additions and 12 deletions

View File

@ -1,3 +1,15 @@
2007-05-01 Matthias Clasen <mclasen@redhat.com>
* gtk/gtkiconcachvalidator.[hc]: Add an icon cache validator.
* gtk/updateiconcache.c: Validate the generated cache before
moving it in place. Also add a --validate option to validate
an existing icon cache.
* gtk/gtkiconcache.c: Validate icon caches before using them.
* gtk/Makefile.am: Integrate it.
2007-05-01 Michael Emmel <mike.emmel@gmail.com>
* gdk/directfb/gdkdisplay-directfb.c:

View File

@ -454,6 +454,7 @@ gtk_base_c_sources = \
gtkhseparator.c \
gtkhsv.c \
gtkiconcache.c \
gtkiconcachevalidator.c \
gtkiconfactory.c \
gtkicontheme.c \
gtkiconview.c \
@ -610,6 +611,7 @@ gtk_all_c_sources += $(gtk_os_unix_c_sources)
if OS_UNIX
gtk_private_h_sources += \
gtkfilesystemunix.h \
gtkiconcachevalidator.h \
gtkprintbackend.h \
gtkprinter-private.h \
gtkprinteroption.h \
@ -865,7 +867,8 @@ gtk_query_immodules_2_0_SOURCES = queryimmodules.c
gtk_update_icon_cache_LDADD = $(top_builddir)/gdk-pixbuf/libgdk_pixbuf-$(GTK_API_VERSION).la
gtk_update_icon_cache_SOURCES = updateiconcache.c
gtk_update_icon_cache_SOURCES = \
updateiconcache.c
.PHONY: files test test-debug

View File

@ -21,6 +21,7 @@
#include "gtkdebug.h"
#include "gtkiconcache.h"
#include "gtkiconcachevalidator.h"
#include "gtkalias.h"
#include <glib/gstdio.h>
@ -58,7 +59,7 @@ struct _GtkIconCache {
GtkIconCache *
_gtk_icon_cache_ref (GtkIconCache *cache)
{
cache->ref_count ++;
cache->ref_count++;
return cache;
}
@ -89,6 +90,7 @@ _gtk_icon_cache_new_for_path (const gchar *path)
struct stat st;
struct stat path_st;
gchar *buffer = NULL;
CacheInfo info;
/* Check if we have a cache file */
cache_filename = g_build_filename (path, "icon-theme.cache", NULL);
@ -121,25 +123,26 @@ _gtk_icon_cache_new_for_path (const gchar *path)
if (!map)
goto done;
/* Verify version */
buffer = g_mapped_file_get_contents (map);
if (GET_UINT16 (buffer, 0) != MAJOR_VERSION ||
GET_UINT16 (buffer, 2) != MINOR_VERSION)
info.cache = g_mapped_file_get_contents (map);
info.cache_size = g_mapped_file_get_length (map);
info.n_directories = 0;
info.flags = CHECK_OFFSETS|CHECK_STRINGS;
g_print ("validating %s\n", cache_filename);
if (!_gtk_icon_cache_validate (&info))
{
g_mapped_file_free (map);
GTK_NOTE (ICONTHEME,
g_print ("wrong cache version\n"));
GTK_NOTE (ICONTHEME, g_print ("invalid icon cache\n"));
goto done;
}
GTK_NOTE (ICONTHEME,
g_print ("found cache for %s\n", path));
GTK_NOTE (ICONTHEME, g_print ("found cache for %s\n", path));
cache = g_new0 (GtkIconCache, 1);
cache->ref_count = 1;
cache->map = map;
cache->buffer = buffer;
cache->buffer = g_mapped_file_get_contents (map);
done:
g_free (cache_filename);
@ -440,7 +443,7 @@ _gtk_icon_cache_get_icon (GtkIconCache *cache,
length = GET_UINT32 (cache->buffer, pixel_data_offset + 4);
if (!gdk_pixdata_deserialize (&pixdata, length,
cache->buffer + pixel_data_offset + 8,
(guchar *)(cache->buffer + pixel_data_offset + 8),
&error))
{
GTK_NOTE (ICONTHEME,

376
gtk/gtkiconcachevalidator.c Normal file
View File

@ -0,0 +1,376 @@
/* gtkiconcachevalidator.c
* Copyright (C) 2007 Red Hat, Inc
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library 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.
*/
#include "config.h"
#include "gtkiconcachevalidator.h"
#include <glib.h>
#include <gdk-pixbuf/gdk-pixdata.h>
#define VERBOSE(x)
#define check(name,condition) \
if (!(condition)) \
{ \
VERBOSE(g_print ("bad %s\n", (name))); \
return FALSE; \
}
static inline gboolean
get_uint16 (CacheInfo *info,
guint32 offset,
guint16 *value)
{
if (offset < info->cache_size)
{
*value = GUINT16_FROM_BE(*(guint16*)(info->cache + offset));
return TRUE;
}
else
{
*value = 0;
return FALSE;
}
}
static inline gboolean
get_uint32 (CacheInfo *info,
guint32 offset,
guint32 *value)
{
if (offset < info->cache_size)
{
*value = GUINT32_FROM_BE(*(guint32*)(info->cache + offset));
return TRUE;
}
else
{
*value = 0;
return FALSE;
}
}
static gboolean
check_version (CacheInfo *info)
{
guint16 major, minor;
check ("major version", get_uint16 (info, 0, &major) && major == 1);
check ("minor version", get_uint16 (info, 2, &minor) && minor == 0);
return TRUE;
}
static gboolean
check_string (CacheInfo *info,
guint32 offset)
{
check ("string offset", offset < info->cache_size);
if (info->flags & CHECK_STRINGS)
{
gint i;
gchar c;
/* assume no string is longer than 1k */
for (i = 0; i < 1024; i++)
{
check ("string offset", offset + i < info->cache_size)
c = *(info->cache + offset + i);
if (c == '\0')
break;
check ("string content", g_ascii_isgraph (c));
}
check ("string length", i < 1024);
}
return TRUE;
}
static gboolean
check_directory_list (CacheInfo *info,
guint32 offset)
{
guint32 directory_offset;
gint i;
check ("offset, directory list", get_uint32 (info, offset, &info->n_directories));
for (i = 0; i < info->n_directories; i++)
{
check ("offset, directory", get_uint32 (info, offset + 4 + 4 * i, &directory_offset));
if (!check_string (info, directory_offset))
return FALSE;
}
return TRUE;
}
static gboolean
check_pixel_data (CacheInfo *info,
guint32 offset)
{
guint32 type;
guint32 length;
check ("offset, pixel data type", get_uint32 (info, offset, &type));
check ("offset, pixel data length", get_uint32 (info, offset + 4, &length));
check ("pixel data type", type == 0);
check ("pixel data length", offset + 8 + length < info->cache_size);
if (info->flags & CHECK_PIXBUFS)
{
GdkPixdata data;
check ("pixel data", gdk_pixdata_deserialize (&data, length,
info->cache + offset + 8,
NULL));
}
return TRUE;
}
static gboolean
check_embedded_rect (CacheInfo *info,
guint32 offset)
{
check ("embedded rect", offset + 4 < info->cache_size);
return TRUE;
}
static gboolean
check_attach_point_list (CacheInfo *info,
guint32 offset)
{
guint32 n_attach_points;
check ("offset, attach point list", get_uint32 (info, offset, &n_attach_points));
check ("attach points", offset + 4 + 4 * n_attach_points < info->cache_size);
return TRUE;
}
static gboolean
check_display_name_list (CacheInfo *info,
guint32 offset)
{
guint32 n_display_names;
gint i;
check ("offset, display name list",
get_uint32 (info, offset, &n_display_names));
for (i = 0; i < n_display_names; i++)
{
if (!check_string (info, offset + 4 + 8 * i))
return FALSE;
if (!check_string (info, offset + 4 + 8 * i + 4))
return FALSE;
}
return TRUE;
}
static gboolean
check_meta_data (CacheInfo *info,
guint32 offset)
{
guint32 embedded_rect_offset;
guint32 attach_point_list_offset;
guint32 display_name_list_offset;
check ("offset, embedded rect",
get_uint32 (info, offset, &embedded_rect_offset));
check ("offset, attach point list",
get_uint32 (info, offset + 4, &attach_point_list_offset));
check ("offset, display name list",
get_uint32 (info, offset + 8, &display_name_list_offset));
if (embedded_rect_offset != 0)
{
if (!check_embedded_rect (info, embedded_rect_offset))
return FALSE;
}
if (attach_point_list_offset != 0)
{
if (!check_attach_point_list (info, attach_point_list_offset))
return FALSE;
}
if (display_name_list_offset != 0)
{
if (!check_display_name_list (info, display_name_list_offset))
return FALSE;
}
return TRUE;
}
static gboolean
check_image_data (CacheInfo *info,
guint32 offset)
{
guint32 pixel_data_offset;
guint32 meta_data_offset;
check ("offset, pixel data", get_uint32 (info, offset, &pixel_data_offset));
check ("offset, meta data", get_uint32 (info, offset + 4, &meta_data_offset));
if (pixel_data_offset != 0)
{
if (!check_pixel_data (info, pixel_data_offset))
return FALSE;
}
if (meta_data_offset != 0)
{
if (!check_meta_data (info, meta_data_offset))
return FALSE;
}
return TRUE;
}
static gboolean
check_image (CacheInfo *info,
guint32 offset)
{
guint16 index;
guint16 flags;
guint32 image_data_offset;
check ("offset, image index", get_uint16 (info, offset, &index));
check ("offset, image flags", get_uint16 (info, offset + 2, &flags));
check ("offset, image data offset",
get_uint32 (info, offset + 4, &image_data_offset));
check ("image index", index < info->n_directories);
check ("image flags", flags == 1 || flags == 2 || flags == 4 ||
flags == 9 || flags == 10 || flags == 12);
if (image_data_offset != 0)
{
if (!check_image_data (info, image_data_offset))
return FALSE;
}
return TRUE;
}
static gboolean
check_image_list (CacheInfo *info,
guint32 offset)
{
guint32 n_images;
gint i;
check ("offset, image list", get_uint32 (info, offset, &n_images));
for (i = 0; i < n_images; i++)
{
if (!check_image (info, offset + 4 + 8 * i))
return FALSE;
}
return TRUE;
}
static gboolean
check_icon (CacheInfo *info,
guint32 offset)
{
guint32 chain_offset;
guint32 name_offset;
guint32 image_list_offset;
check ("offset, icon chain", get_uint32 (info, offset, &chain_offset));
check ("offset, icon name", get_uint32 (info, offset + 4, &name_offset));
check ("offset, icon image list", get_uint32 (info, offset + 8,
&image_list_offset));
if (!check_string (info, name_offset))
return FALSE;
if (!check_image_list (info, image_list_offset))
return FALSE;
if (chain_offset != 0xffffffff)
{
if (!check_icon (info, chain_offset))
return FALSE;
}
return TRUE;
}
static gboolean
check_hash (CacheInfo *info,
guint32 offset)
{
guint32 n_buckets, icon_offset;
gint i;
check ("offset, hash size", get_uint32 (info, offset, &n_buckets));
for (i = 0; i < n_buckets; i++)
{
check ("offset, hash chain",
get_uint32 (info, offset + 4 + 4 * i, &icon_offset));
if (icon_offset != 0xffffffff)
{
if (!check_icon (info, icon_offset))
return FALSE;
}
}
return TRUE;
}
/**
* _gtk_icon_cache_validate:
* @info: a CacheInfo structure
*
* Validates the icon cache passed in the @cache and
* @cache_size fields of the @info structure. The
* validator checks that offsets specified in the
* cache do not point outside the mapped area, that
* strings look reasonable, and that pixbufs can
* be deserialized. The amount of validation can
* be controlled with the @flags field.
*
* Return value: %TRUE if the cache is valid
*/
gboolean
_gtk_icon_cache_validate (CacheInfo *info)
{
guint32 hash_offset;
guint32 directory_list_offset;
if (!check_version (info))
return FALSE;
check ("header, hash offset", get_uint32 (info, 4, &hash_offset));
check ("header, directory list offset", get_uint32 (info, 8, &directory_list_offset));
if (!check_directory_list (info, directory_list_offset))
return FALSE;
if (!check_hash (info, hash_offset))
return FALSE;
return TRUE;
}

View File

@ -0,0 +1,44 @@
/* gtkiconcachevalidator.4
* Copyright (C) 2007 Red Hat, Inc
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library 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.
*/
#ifndef __GTK_ICON_CACHE_VALIDATOR_H__
#define __GTK_ICON_CACHE_VALIDATOR_H__
#include <glib.h>
G_BEGIN_DECLS
enum {
CHECK_OFFSETS = 1,
CHECK_STRINGS = 2,
CHECK_PIXBUFS = 4
};
typedef struct {
const gchar *cache;
gsize cache_size;
guint32 n_directories;
gint flags;
} CacheInfo;
gboolean _gtk_icon_cache_validate (CacheInfo *info);
G_END_DECLS
#endif /* __GTK_ICON_CACHE_VALIDATOR_H__ */

View File

@ -39,13 +39,20 @@
#include <glib/gstdio.h>
#include <gdk-pixbuf/gdk-pixdata.h>
#include <glib/gi18n.h>
#include "gtkiconcachevalidator.h"
static gboolean force_update = FALSE;
static gboolean ignore_theme_index = FALSE;
static gboolean quiet = FALSE;
static gboolean index_only = FALSE;
static gboolean validate = FALSE;
static gchar *var_name = "-";
/* Quite ugly - if we just add the c file to the
* list of sources in Makefile.am, libtool complains.
*/
#include "gtkiconcachevalidator.c"
#define CACHE_NAME "icon-theme.cache"
#define HAS_SUFFIX_XPM (1 << 0)
@ -1396,6 +1403,32 @@ write_file (FILE *cache, GHashTable *files, GList *directories)
return TRUE;
}
static gboolean
validate_file (const gchar *file)
{
GMappedFile *map;
CacheInfo info;
map = g_mapped_file_new (file, FALSE, NULL);
if (!map)
return FALSE;
info.cache = g_mapped_file_get_contents (map);
info.cache_size = g_mapped_file_get_length (map);
info.n_directories = 0;
info.flags = CHECK_OFFSETS|CHECK_STRINGS|CHECK_PIXBUFS;
if (!_gtk_icon_cache_validate (&info))
{
g_mapped_file_free (map);
return FALSE;
}
g_mapped_file_free (map);
return TRUE;
}
static void
build_cache (const gchar *path)
{
@ -1448,6 +1481,13 @@ build_cache (const gchar *path)
exit (1);
}
if (!validate_file (tmp_cache_path))
{
g_printerr (_("The generated cache was invalid.\n"));
g_unlink (tmp_cache_path);
exit (1);
}
cache_path = g_build_filename (path, CACHE_NAME, NULL);
#ifdef G_OS_WIN32
@ -1542,6 +1582,7 @@ static GOptionEntry args[] = {
{ "index-only", 'i', 0, G_OPTION_ARG_NONE, &index_only, N_("Don't include image data in the cache"), NULL },
{ "source", 'c', 0, G_OPTION_ARG_STRING, &var_name, N_("Output a C header file"), "NAME" },
{ "quiet", 'q', 0, G_OPTION_ARG_NONE, &quiet, N_("Turn off verbose output"), NULL },
{ "validate", 'v', 0, G_OPTION_ARG_NONE, &validate, N_("Validate existing icon cache"), NULL },
{ NULL }
};
@ -1569,6 +1610,28 @@ main (int argc, char **argv)
path = g_locale_to_utf8 (path, -1, NULL, NULL, NULL);
#endif
if (validate)
{
gchar *file = g_build_filename (path, CACHE_NAME, NULL);
if (!g_file_test (file, G_FILE_TEST_IS_REGULAR))
{
if (!quiet)
g_printerr (_("File not found: %s\n"), file);
exit (1);
}
if (!validate_file (file))
{
if (!quiet)
g_printerr (_("Not a valid icon cache: %s\n"), file);
exit (1);
}
else
{
exit (0);
}
}
if (!ignore_theme_index && !has_theme_index (path))
{
g_printerr (_("No theme index file in '%s'.\n"