Bug 731390 - XCF files have a max size of 4G

Step one, without changing anything in the saved XCFs yet:

Abstract reading and writing of file offsets away into their own
xcf_read_offset() and xcf_write_offset() functions, which take
"goffset" instead of "guint32". Also change xcf_seek_pos() to take a
goffset argument.

Change all file offset variables in xcf-load.c, xcf-write.c and struct
XcfInfo to goffset, and add new member "bytes_per_offset" to XcfInfo,
which is currently always 4.
This commit is contained in:
Michael Natterer 2017-03-23 11:44:41 +01:00
parent f861585051
commit a0a5f714bb
10 changed files with 196 additions and 118 deletions

View file

@ -150,8 +150,8 @@ xcf_load_image (Gimp *gimp,
GimpImage *image = NULL;
const GimpParasite *parasite;
gboolean has_metadata = FALSE;
guint32 saved_pos;
guint32 offset;
goffset saved_pos;
goffset offset;
gint width;
gint height;
gint image_type;
@ -393,7 +393,7 @@ xcf_load_image (Gimp *gimp,
GList *item_path = NULL;
/* read in the offset of the next layer */
info->cp += xcf_read_int32 (info->input, &offset, 1);
info->cp += xcf_read_offset (info->input, &offset, 1);
/* if the offset is 0 then we are at the end
* of the layer list.
@ -477,7 +477,7 @@ xcf_load_image (Gimp *gimp,
GimpChannel *channel;
/* read in the offset of the next channel */
info->cp += xcf_read_int32 (info->input, &offset, 1);
info->cp += xcf_read_offset (info->input, &offset, 1);
/* if the offset is 0 then we are at the end
* of the channel list.
@ -801,7 +801,7 @@ xcf_load_image_props (XcfInfo *info,
case PROP_PARASITES:
{
glong base = info->cp;
goffset base = info->cp;
while (info->cp - base < prop_size)
{
@ -912,14 +912,14 @@ xcf_load_image_props (XcfInfo *info,
case PROP_VECTORS:
{
guint32 base = info->cp;
goffset base = info->cp;
if (xcf_load_vectors (info, image))
{
if (base + prop_size != info->cp)
{
g_printerr ("Mismatch in PROP_VECTORS size: "
"skipping %d bytes.\n",
"skipping " G_GOFFSET_FORMAT " bytes.\n",
base + prop_size - info->cp);
xcf_seek_pos (info, base + prop_size, NULL);
}
@ -978,9 +978,8 @@ xcf_load_layer_props (XcfInfo *info,
case PROP_FLOATING_SELECTION:
info->floating_sel = *layer;
info->cp +=
xcf_read_int32 (info->input,
(guint32 *) &info->floating_sel_offset, 1);
info->cp += xcf_read_offset (info->input,
&info->floating_sel_offset, 1);
break;
case PROP_OPACITY:
@ -1147,7 +1146,7 @@ xcf_load_layer_props (XcfInfo *info,
case PROP_PARASITES:
{
glong base = info->cp;
goffset base = info->cp;
while (info->cp - base < prop_size)
{
@ -1220,8 +1219,8 @@ xcf_load_layer_props (XcfInfo *info,
case PROP_ITEM_PATH:
{
glong base = info->cp;
GList *path = NULL;
goffset base = info->cp;
GList *path = NULL;
while (info->cp - base < prop_size)
{
@ -1408,7 +1407,7 @@ xcf_load_channel_props (XcfInfo *info,
case PROP_PARASITES:
{
glong base = info->cp;
goffset base = info->cp;
while ((info->cp - base) < prop_size)
{
@ -1481,8 +1480,8 @@ xcf_load_layer (XcfInfo *info,
{
GimpLayer *layer;
GimpLayerMask *layer_mask;
guint32 hierarchy_offset;
guint32 layer_mask_offset;
goffset hierarchy_offset;
goffset layer_mask_offset;
gboolean apply_mask = TRUE;
gboolean edit_mask = FALSE;
gboolean show_mask = FALSE;
@ -1592,8 +1591,8 @@ xcf_load_layer (XcfInfo *info,
}
/* read the hierarchy and layer mask offsets */
info->cp += xcf_read_int32 (info->input, &hierarchy_offset, 1);
info->cp += xcf_read_int32 (info->input, &layer_mask_offset, 1);
info->cp += xcf_read_offset (info->input, &hierarchy_offset, 1);
info->cp += xcf_read_offset (info->input, &layer_mask_offset, 1);
/* read in the hierarchy (ignore it for group layers, both as an
* optimization and because the hierarchy's extents don't match
@ -1664,7 +1663,7 @@ xcf_load_channel (XcfInfo *info,
GimpImage *image)
{
GimpChannel *channel;
guint32 hierarchy_offset;
goffset hierarchy_offset;
gint width;
gint height;
gboolean is_fs_drawable;
@ -1697,7 +1696,7 @@ xcf_load_channel (XcfInfo *info,
xcf_progress_update (info);
/* read the hierarchy and layer mask offsets */
info->cp += xcf_read_int32 (info->input, &hierarchy_offset, 1);
info->cp += xcf_read_offset (info->input, &hierarchy_offset, 1);
/* read in the hierarchy */
if (!xcf_seek_pos (info, hierarchy_offset, NULL))
@ -1725,7 +1724,7 @@ xcf_load_layer_mask (XcfInfo *info,
{
GimpLayerMask *layer_mask;
GimpChannel *channel;
guint32 hierarchy_offset;
goffset hierarchy_offset;
gint width;
gint height;
gboolean is_fs_drawable;
@ -1759,7 +1758,7 @@ xcf_load_layer_mask (XcfInfo *info,
xcf_progress_update (info);
/* read the hierarchy and layer mask offsets */
info->cp += xcf_read_int32 (info->input, &hierarchy_offset, 1);
info->cp += xcf_read_offset (info->input, &hierarchy_offset, 1);
/* read in the hierarchy */
if (! xcf_seek_pos (info, hierarchy_offset, NULL))
@ -1787,7 +1786,7 @@ xcf_load_buffer (XcfInfo *info,
GeglBuffer *buffer)
{
const Babl *format;
guint32 offset;
goffset offset;
gint width;
gint height;
gint bpp;
@ -1806,7 +1805,7 @@ xcf_load_buffer (XcfInfo *info,
bpp != babl_format_get_bytes_per_pixel (format))
return FALSE;
info->cp += xcf_read_int32 (info->input, &offset, 1); /* top level */
info->cp += xcf_read_offset (info->input, &offset, 1); /* top level */
/* seek to the level offset */
if (!xcf_seek_pos (info, offset, NULL))
@ -1829,8 +1828,9 @@ xcf_load_level (XcfInfo *info,
{
const Babl *format;
gint bpp;
guint32 saved_pos;
guint32 offset, offset2;
goffset saved_pos;
goffset offset;
goffset offset2;
gint n_tile_rows;
gint n_tile_cols;
guint ntiles;
@ -1853,7 +1853,7 @@ xcf_load_level (XcfInfo *info,
* if it is '0', then this tile level is empty
* and we can simply return.
*/
info->cp += xcf_read_int32 (info->input, &offset, 1);
info->cp += xcf_read_offset (info->input, &offset, 1);
if (offset == 0)
return TRUE;
@ -1881,11 +1881,13 @@ xcf_load_level (XcfInfo *info,
saved_pos = info->cp;
/* read in the offset of the next tile so we can calculate the amount
of data needed for this tile*/
info->cp += xcf_read_int32 (info->input, &offset2, 1);
* of data needed for this tile
*/
info->cp += xcf_read_offset (info->input, &offset2, 1);
/* if the offset is 0 then we need to read in the maximum possible
allowing for negative compression */
* allowing for negative compression
*/
if (offset2 == 0)
offset2 = offset + XCF_TILE_WIDTH * XCF_TILE_WIDTH * bpp * 1.5;
/* 1.5 is probably more
@ -1943,13 +1945,14 @@ xcf_load_level (XcfInfo *info,
return FALSE;
/* read in the offset of the next tile */
info->cp += xcf_read_int32 (info->input, &offset, 1);
info->cp += xcf_read_offset (info->input, &offset, 1);
}
if (offset != 0)
{
gimp_message (info->gimp, G_OBJECT (info->progress), GIMP_MESSAGE_ERROR,
"encountered garbage after reading level: %d", offset);
"encountered garbage after reading level: " G_GOFFSET_FORMAT,
offset);
return FALSE;
}

View file

@ -98,14 +98,15 @@ struct _XcfInfo
GInputStream *input;
GOutputStream *output;
GSeekable *seekable;
guint cp;
goffset cp;
gint bytes_per_offset;
GFile *file;
GimpTattoo tattoo_state;
GimpLayer *active_layer;
GimpChannel *active_channel;
GimpDrawable *floating_sel_drawable;
GimpLayer *floating_sel;
guint floating_sel_offset;
goffset floating_sel_offset;
XcfCompressionType compression;
gint file_version;
};

View file

@ -50,6 +50,29 @@ xcf_read_int32 (GInputStream *input,
return total;
}
guint
xcf_read_offset (GInputStream *input,
goffset *data,
gint count)
{
guint total = 0;
gint32 *int_offsets = g_alloca (count * sizeof (gint32));
if (count > 0)
{
total += xcf_read_int8 (input, (guint8 *) int_offsets, count * 4);
while (count--)
{
*data = g_ntohl (*int_offsets);
int_offsets++;
data++;
}
}
return total;
}
guint
xcf_read_float (GInputStream *input,
gfloat *data,

View file

@ -22,6 +22,9 @@
guint xcf_read_int32 (GInputStream *input,
guint32 *data,
gint count);
guint xcf_read_offset (GInputStream *input,
goffset *data,
gint count);
guint xcf_read_float (GInputStream *input,
gfloat *data,
gint count);

View file

@ -139,13 +139,22 @@ static gboolean xcf_save_vectors (XcfInfo *info,
} \
} G_STMT_END
#define xcf_write_zero_int32_check_error(info, count) G_STMT_START { \
info->cp += xcf_write_zero_int32 (info->output, count, &tmp_error); \
if (tmp_error) \
{ \
g_propagate_error (error, tmp_error); \
return FALSE; \
} \
#define xcf_write_offset_check_error(info, data, count) G_STMT_START { \
info->cp += xcf_write_offset (info->output, data, count, &tmp_error); \
if (tmp_error) \
{ \
g_propagate_error (error, tmp_error); \
return FALSE; \
} \
} G_STMT_END
#define xcf_write_zero_offset_check_error(info, count) G_STMT_START { \
info->cp += xcf_write_zero_offset (info->output, count, &tmp_error); \
if (tmp_error) \
{ \
g_propagate_error (error, tmp_error); \
return FALSE; \
} \
} G_STMT_END
#define xcf_write_int8_check_error(info, data, count) G_STMT_START { \
@ -202,8 +211,8 @@ xcf_save_image (XcfInfo *info,
GList *all_layers;
GList *all_channels;
GList *list;
guint32 saved_pos;
guint32 offset;
goffset saved_pos;
goffset offset;
guint32 value;
guint n_layers;
guint n_channels;
@ -255,9 +264,7 @@ xcf_save_image (XcfInfo *info,
max_progress = 1 + n_layers + n_channels;
/* write the property information for the image.
*/
/* write the property information for the image */
xcf_check_error (xcf_save_image_props (info, image, error));
xcf_progress_update (info);
@ -266,7 +273,7 @@ xcf_save_image (XcfInfo *info,
saved_pos = info->cp;
/* write an empty offset table */
xcf_write_zero_int32_check_error (info, n_layers + n_channels + 2);
xcf_write_zero_offset_check_error (info, n_layers + n_channels + 2);
/* 'offset' is where we will write the next layer or channel */
offset = info->cp;
@ -279,7 +286,7 @@ xcf_save_image (XcfInfo *info,
* offset of the layer
*/
xcf_check_error (xcf_seek_pos (info, saved_pos, error));
xcf_write_int32_check_error (info, &offset, 1);
xcf_write_offset_check_error (info, &offset, 1);
/* remember the next slot in the offset table */
saved_pos = info->cp;
@ -297,7 +304,7 @@ xcf_save_image (XcfInfo *info,
/* skip a '0' in the offset table to indicate the end of the layer
* offsets
*/
saved_pos += 4;
saved_pos += info->bytes_per_offset;
for (list = all_channels; list; list = g_list_next (list))
{
@ -307,7 +314,7 @@ xcf_save_image (XcfInfo *info,
* offset of the channel
*/
xcf_check_error (xcf_seek_pos (info, saved_pos, error));
xcf_write_int32_check_error (info, &offset, 1);
xcf_write_offset_check_error (info, &offset, 1);
/* remember the next slot in the offset table */
saved_pos = info->cp;
@ -454,6 +461,7 @@ xcf_save_image_props (XcfInfo *info,
gimp_parasite_name (compat_parasite));
gimp_parasite_free (compat_parasite);
}
xcf_check_error (xcf_save_prop (info, image, PROP_END, error));
return TRUE;
@ -683,15 +691,15 @@ xcf_save_prop (XcfInfo *info,
case PROP_FLOATING_SELECTION:
{
guint32 dummy;
goffset dummy;
dummy = 0;
size = 4;
size = info->bytes_per_offset;
xcf_write_prop_type_check_error (info, prop_type);
xcf_write_int32_check_error (info, &size, 1);
info->floating_sel_offset = info->cp;
xcf_write_int32_check_error (info, &dummy, 1);
xcf_write_offset_check_error (info, &dummy, 1);
}
break;
@ -1067,16 +1075,19 @@ xcf_save_prop (XcfInfo *info,
if (gimp_parasite_list_persistent_length (list) > 0)
{
guint32 base, length = 0;
long pos;
goffset base;
goffset pos;
guint32 length = 0;
xcf_write_prop_type_check_error (info, prop_type);
/* because we don't know how much room the parasite list will take
* we save the file position and write the length later
/* because we don't know how much room the parasite list
* will take we save the file position and write the
* length later
*/
pos = info->cp;
xcf_write_int32_check_error (info, &length, 1);
base = info->cp;
xcf_check_error (xcf_save_parasite_list (info, list, error));
@ -1108,13 +1119,14 @@ xcf_save_prop (XcfInfo *info,
case PROP_PATHS:
{
guint32 base, length = 0;
glong pos;
goffset base;
goffset pos;
guint32 length = 0;
xcf_write_prop_type_check_error (info, prop_type);
/* because we don't know how much room the paths list will take
* we save the file position and write the length later
/* because we don't know how much room the paths list will
* take we save the file position and write the length later
*/
pos = info->cp;
xcf_write_int32_check_error (info, &length, 1);
@ -1169,13 +1181,14 @@ xcf_save_prop (XcfInfo *info,
case PROP_VECTORS:
{
guint32 base, length = 0;
glong pos;
goffset base;
goffset pos;
guint32 length = 0;
xcf_write_prop_type_check_error (info, prop_type);
/* because we don't know how much room the paths list will take
* we save the file position and write the length later
/* because we don't know how much room the paths list will
* take we save the file position and write the length later
*/
pos = info->cp;
xcf_write_int32_check_error (info, &length, 1);
@ -1253,8 +1266,8 @@ xcf_save_layer (XcfInfo *info,
GimpLayer *layer,
GError **error)
{
guint32 saved_pos;
guint32 offset;
goffset saved_pos;
goffset offset;
guint32 value;
const gchar *string;
GError *tmp_error = NULL;
@ -1266,7 +1279,7 @@ xcf_save_layer (XcfInfo *info,
{
saved_pos = info->cp;
xcf_check_error (xcf_seek_pos (info, info->floating_sel_offset, error));
xcf_write_int32_check_error (info, &saved_pos, 1);
xcf_write_offset_check_error (info, &saved_pos, 1);
xcf_check_error (xcf_seek_pos (info, saved_pos, error));
}
@ -1288,13 +1301,13 @@ xcf_save_layer (XcfInfo *info,
xcf_save_layer_props (info, image, layer, error);
/* write out the layer tile hierarchy */
offset = info->cp + 8;
xcf_write_int32_check_error (info, &offset, 1);
offset = info->cp + 2 * info->bytes_per_offset;
xcf_write_offset_check_error (info, &offset, 1);
saved_pos = info->cp;
/* write a zero layer mask offset */
xcf_write_zero_int32_check_error (info, 1);
xcf_write_zero_offset_check_error (info, 1);
xcf_check_error (xcf_save_buffer (info,
gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)),
@ -1308,7 +1321,7 @@ xcf_save_layer (XcfInfo *info,
GimpLayerMask *mask = gimp_layer_get_mask (layer);
xcf_check_error (xcf_seek_pos (info, saved_pos, error));
xcf_write_int32_check_error (info, &offset, 1);
xcf_write_offset_check_error (info, &offset, 1);
xcf_check_error (xcf_seek_pos (info, offset, error));
xcf_check_error (xcf_save_channel (info, image, GIMP_CHANNEL (mask),
@ -1324,8 +1337,8 @@ xcf_save_channel (XcfInfo *info,
GimpChannel *channel,
GError **error)
{
guint32 saved_pos;
guint32 offset;
goffset saved_pos;
goffset offset;
guint32 value;
const gchar *string;
GError *tmp_error = NULL;
@ -1337,7 +1350,7 @@ xcf_save_channel (XcfInfo *info,
{
saved_pos = info->cp;
xcf_check_error (xcf_seek_pos (info, info->floating_sel_offset, error));
xcf_write_int32_check_error (info, &saved_pos, 1);
xcf_write_offset_check_error (info, &saved_pos, 1);
xcf_check_error (xcf_seek_pos (info, saved_pos, error));
}
@ -1356,8 +1369,8 @@ xcf_save_channel (XcfInfo *info,
xcf_save_channel_props (info, image, channel, error);
/* write out the channel tile hierarchy */
offset = info->cp + 4;
xcf_write_int32_check_error (info, &offset, 1);
offset = info->cp + info->bytes_per_offset;
xcf_write_offset_check_error (info, &offset, 1);
xcf_check_error (xcf_save_buffer (info,
gimp_drawable_get_buffer (GIMP_DRAWABLE (channel)),
@ -1370,7 +1383,7 @@ static gint
xcf_calc_levels (gint size,
gint tile_size)
{
int levels;
gint levels;
levels = 1;
while (size > tile_size)
@ -1389,8 +1402,8 @@ xcf_save_buffer (XcfInfo *info,
GError **error)
{
const Babl *format;
guint32 saved_pos;
guint32 offset;
goffset saved_pos;
goffset offset;
guint32 width;
guint32 height;
guint32 bpp;
@ -1409,8 +1422,6 @@ xcf_save_buffer (XcfInfo *info,
xcf_write_int32_check_error (info, (guint32 *) &height, 1);
xcf_write_int32_check_error (info, (guint32 *) &bpp, 1);
saved_pos = info->cp;
tmp1 = xcf_calc_levels (width, XCF_TILE_WIDTH);
tmp2 = xcf_calc_levels (height, XCF_TILE_HEIGHT);
nlevels = MAX (tmp1, tmp2);
@ -1419,7 +1430,7 @@ xcf_save_buffer (XcfInfo *info,
saved_pos = info->cp;
/* write an empty offset table */
xcf_write_zero_int32_check_error (info, nlevels + 1);
xcf_write_zero_offset_check_error (info, nlevels + 1);
/* 'offset' is where we will write the next level */
offset = info->cp;
@ -1430,7 +1441,7 @@ xcf_save_buffer (XcfInfo *info,
* offset of the level
*/
xcf_check_error (xcf_seek_pos (info, saved_pos, error));
xcf_write_int32_check_error (info, &offset, 1);
xcf_write_offset_check_error (info, &offset, 1);
/* remember the next slot in the offset table */
saved_pos = info->cp;
@ -1471,10 +1482,10 @@ xcf_save_level (XcfInfo *info,
GError **error)
{
const Babl *format;
guint32 *offset_table;
guint32 *next_offset;
guint32 saved_pos;
guint32 offset;
goffset *offset_table;
goffset *next_offset;
goffset saved_pos;
goffset offset;
guint32 width;
guint32 height;
gint bpp;
@ -1511,15 +1522,15 @@ xcf_save_level (XcfInfo *info,
* tile, see bug #686862. allocate ntiles + 1 slots because a zero
* offset indicates the offset table's end.
*/
offset_table = g_alloca ((ntiles + 1) * sizeof (gint32));
memset (offset_table, 0, (ntiles + 1) * sizeof (gint32));
offset_table = g_alloca ((ntiles + 1) * sizeof (goffset));
memset (offset_table, 0, (ntiles + 1) * sizeof (goffset));
next_offset = offset_table;
/* 'saved_pos' is the offset of the tile offset table */
saved_pos = info->cp;
/* write an empty offset table */
xcf_write_zero_int32_check_error (info, ntiles + 1);
xcf_write_zero_offset_check_error (info, ntiles + 1);
/* 'offset' is where we will write the next tile */
offset = info->cp;
@ -1561,7 +1572,7 @@ xcf_save_level (XcfInfo *info,
/* seek back to the offset table and write it */
xcf_check_error (xcf_seek_pos (info, saved_pos, error));
xcf_write_int32_check_error (info, offset_table, ntiles + 1);
xcf_write_offset_check_error (info, offset_table, ntiles + 1);
/* seek to the end of the file */
xcf_check_error (xcf_seek_pos (info, offset, error));

View file

@ -29,7 +29,7 @@
gboolean
xcf_seek_pos (XcfInfo *info,
guint pos,
goffset pos,
GError **error)
{
if (info->cp != pos)

View file

@ -19,9 +19,9 @@
#define __XCF_SEEK_H__
gboolean xcf_seek_pos (XcfInfo *info,
guint pos,
GError **error);
gboolean xcf_seek_pos (XcfInfo *info,
goffset pos,
GError **error);
#endif /* __XCF_SEEK_H__ */

View file

@ -56,9 +56,38 @@ xcf_write_int32 (GOutputStream *output,
}
guint
xcf_write_zero_int32 (GOutputStream *output,
gint count,
GError **error)
xcf_write_offset (GOutputStream *output,
const goffset *data,
gint count,
GError **error)
{
GError *tmp_error = NULL;
gint i;
if (count > 0)
{
for (i = 0; i < count; i++)
{
guint32 tmp = g_htonl (data[i]);
xcf_write_int8 (output, (const guint8 *) &tmp, 4, &tmp_error);
if (tmp_error)
{
g_propagate_error (error, tmp_error);
return i * 4;
}
}
}
return count * 4;
}
guint
xcf_write_zero_offset (GOutputStream *output,
gint count,
GError **error)
{
if (count > 0)
{

View file

@ -19,25 +19,29 @@
#define __XCF_WRITE_H__
guint xcf_write_int32 (GOutputStream *output,
const guint32 *data,
gint count,
GError **error);
guint xcf_write_zero_int32 (GOutputStream *output,
gint count,
GError **error);
guint xcf_write_float (GOutputStream *output,
const gfloat *data,
gint count,
GError **error);
guint xcf_write_int8 (GOutputStream *output,
const guint8 *data,
gint count,
GError **error);
guint xcf_write_string (GOutputStream *output,
gchar **data,
gint count,
GError **error);
guint xcf_write_int32 (GOutputStream *output,
const guint32 *data,
gint count,
GError **error);
guint xcf_write_offset (GOutputStream *output,
const goffset *data,
gint count,
GError **error);
guint xcf_write_zero_offset (GOutputStream *output,
gint count,
GError **error);
guint xcf_write_float (GOutputStream *output,
const gfloat *data,
gint count,
GError **error);
guint xcf_write_int8 (GOutputStream *output,
const guint8 *data,
gint count,
GError **error);
guint xcf_write_string (GOutputStream *output,
gchar **data,
gint count,
GError **error);
#endif /* __XCF_WRITE_H__ */

View file

@ -280,6 +280,8 @@ xcf_load_stream (Gimp *gimp,
success = TRUE;
info.bytes_per_offset = 4;
info.cp += xcf_read_int8 (info.input, (guint8 *) id, 14);
if (! g_str_has_prefix (id, "gimp xcf "))
@ -367,6 +369,8 @@ xcf_save_stream (Gimp *gimp,
COMPRESS_ZLIB,
NULL, NULL);
info.bytes_per_offset = 4;
if (progress)
gimp_progress_start (progress, FALSE, _("Saving '%s'"), filename);