From 0c70f3dc7d1a7793c270585fd7020daf3a57c12d Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Sun, 31 Oct 2021 14:13:18 +0000 Subject: [PATCH] add fail-on : better control over loader error handling (#2360) Instead of a simple fail/don't-fail boolean switch, add fail-on, an enum which sets the sensitivity of loaders to errors. There's a new sensitivity level which tries to detect truncated images, but ignores other types of error. --- libvips/deprecated/im_tiff2vips.c | 7 ++++--- libvips/foreign/csvload.c | 21 +++++++++++++-------- libvips/foreign/foreign.c | 27 ++++++++++++++++++++++----- libvips/foreign/jp2kload.c | 25 +++++++++++++++++-------- libvips/foreign/jpeg2vips.c | 25 ++++++++++++------------- libvips/foreign/jpegload.c | 20 +++++++++++--------- libvips/foreign/nsgifload.c | 11 ++++++++--- libvips/foreign/pforeign.h | 22 +++++++--------------- libvips/foreign/pngload.c | 8 +++++++- libvips/foreign/spngload.c | 10 +++++----- libvips/foreign/tiff2vips.c | 16 +++++++++++----- libvips/foreign/tiffload.c | 6 ++++-- libvips/foreign/vipspng.c | 12 ++++++------ libvips/include/vips/enumtypes.h | 2 ++ libvips/include/vips/foreign.h | 30 ++++++++++++++++++++++++++---- libvips/iofuncs/enumtypes.c | 20 ++++++++++++++++++++ test/test-suite/test_foreign.py | 28 ++++++++++++++++++++++++++++ 17 files changed, 203 insertions(+), 87 deletions(-) diff --git a/libvips/deprecated/im_tiff2vips.c b/libvips/deprecated/im_tiff2vips.c index 785faf8d..806c8fd7 100644 --- a/libvips/deprecated/im_tiff2vips.c +++ b/libvips/deprecated/im_tiff2vips.c @@ -75,8 +75,8 @@ im_tiff_read_header( const char *filename, VipsImage *out, if( !(source = vips_source_new_from_file( filename )) ) return( -1 ); - if( vips__tiff_read_header_source( source, - out, page, n, autorotate, -1 ) ) { + if( vips__tiff_read_header_source( source, out, + page, n, autorotate, -1, VIPS_FAIL_ON_ERROR ) ) { VIPS_UNREF( source ); return( -1 ); } @@ -93,7 +93,8 @@ im_tiff_read( const char *filename, VipsImage *out, if( !(source = vips_source_new_from_file( filename )) ) return( -1 ); - if( vips__tiff_read_source( source, out, page, n, autorotate, -1 ) ) { + if( vips__tiff_read_source( source, out, + page, n, autorotate, -1, VIPS_FAIL_ON_ERROR ) ) { VIPS_UNREF( source ); return( -1 ); } diff --git a/libvips/foreign/csvload.c b/libvips/foreign/csvload.c index 14a39b9b..6164555c 100644 --- a/libvips/foreign/csvload.c +++ b/libvips/foreign/csvload.c @@ -421,12 +421,17 @@ vips_foreign_load_csv_load( VipsForeignLoad *load ) */ ch = EOF; + /* Some lines may be shorter. + */ + memset( csv->linebuf, 0, load->real->Xsize * sizeof( double ) ); + for( x = 0; x < load->real->Xsize; x++ ) { double value; csv->colno += 1; ch = vips_foreign_load_csv_read_double( csv, &value ); - if( ch == EOF ) { + if( ch == EOF && + load->fail_on >= VIPS_FAIL_ON_TRUNCATED ) { vips_error( class->nickname, "%s", _( "unexpected end of file" ) ); return( -1 ); @@ -436,7 +441,9 @@ vips_foreign_load_csv_load( VipsForeignLoad *load ) vips_error( class->nickname, _( "line %d has only %d columns" ), csv->lineno, csv->colno ); - if( load->fail ) + /* Unequal length lines, but no EOF. + */ + if( load->fail_on >= VIPS_FAIL_ON_ERROR ) return( -1 ); } @@ -671,7 +678,7 @@ vips_foreign_load_csv_source_init( VipsForeignLoadCsvSource *source ) * * @lines: read this many lines from file * * @whitespace: set of whitespace characters * * @separator: set of separator characters - * * @fail: %gboolean, fail on errors + * * @fail_on: #VipsFailOn, types of read error to fail on * * Load a CSV (comma-separated values) file. The output image is always 1 * band (monochrome), #VIPS_FORMAT_DOUBLE. Use vips_bandfold() to turn @@ -695,7 +702,8 @@ vips_foreign_load_csv_source_init( VipsForeignLoadCsvSource *source ) * @separator sets the characters that separate fields. * Default ;,tab. Separators are never run together. * - * Setting @fail to %TRUE makes the reader fail on any errors. + * Use @fail_on to set the type of error that will cause load to fail. By + * default, loaders are permissive, that is, #VIPS_FAIL_ON_NONE. * * See also: vips_image_new_from_file(), vips_bandfold(). * @@ -726,7 +734,7 @@ vips_csvload( const char *filename, VipsImage **out, ... ) * * @lines: read this many lines from file * * @whitespace: set of whitespace characters * * @separator: set of separator characters - * * @fail: %gboolean, fail on errors + * * @fail_on: #VipsFailOn, types of read error to fail on * * Exactly as vips_csvload(), but read from a source. * @@ -747,6 +755,3 @@ vips_csvload_source( VipsSource *source, VipsImage **out, ... ) return( result ); } - - - diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 07e2199e..37c52bae 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -24,6 +24,8 @@ * - block _start if one start fails, see #893 * 1/4/18 * - drop incompatible ICC profiles before save + * 24/7/21 + * - add fail-on */ /* @@ -1061,6 +1063,13 @@ vips_foreign_load_build( VipsObject *object ) if( sequential ) load->nocache = TRUE; + /* The deprecated "fail" field sets fail_on warning. + */ + if( vips_object_argument_isset( object, "fail" ) && + !vips_object_argument_isset( object, "fail_on" ) ) + load->fail_on = load->fail ? + VIPS_FAIL_ON_WARNING : VIPS_FAIL_ON_NONE; + if( VIPS_OBJECT_CLASS( vips_foreign_load_parent_class )-> build( object ) ) return( -1 ); @@ -1184,21 +1193,28 @@ vips_foreign_load_class_init( VipsForeignLoadClass *class ) G_STRUCT_OFFSET( VipsForeignLoad, access ), VIPS_TYPE_ACCESS, VIPS_ACCESS_RANDOM ); - VIPS_ARG_BOOL( class, "sequential", 109, + VIPS_ARG_ENUM( class, "fail-on", 109, + _( "Fail on" ), + _( "Error level to fail on" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsForeignLoad, fail_on ), + VIPS_TYPE_FAIL_ON, VIPS_FAIL_ON_NONE ); + + VIPS_ARG_BOOL( class, "sequential", 110, _( "Sequential" ), _( "Sequential read only" ), VIPS_ARGUMENT_OPTIONAL_INPUT | VIPS_ARGUMENT_DEPRECATED, G_STRUCT_OFFSET( VipsForeignLoad, sequential ), FALSE ); - VIPS_ARG_BOOL( class, "fail", 110, + VIPS_ARG_BOOL( class, "fail", 111, _( "Fail" ), - _( "Fail on first error" ), - VIPS_ARGUMENT_OPTIONAL_INPUT, + _( "Fail on first warning" ), + VIPS_ARGUMENT_OPTIONAL_INPUT | VIPS_ARGUMENT_DEPRECATED, G_STRUCT_OFFSET( VipsForeignLoad, fail ), FALSE ); - VIPS_ARG_BOOL( class, "disc", 111, + VIPS_ARG_BOOL( class, "disc", 112, _( "Disc" ), _( "Open to disc" ), VIPS_ARGUMENT_OPTIONAL_INPUT | VIPS_ARGUMENT_DEPRECATED, @@ -1212,6 +1228,7 @@ vips_foreign_load_init( VipsForeignLoad *load ) { load->disc = TRUE; load->access = VIPS_ACCESS_RANDOM; + load->fail_on = VIPS_FAIL_ON_NONE; } /* diff --git a/libvips/foreign/jp2kload.c b/libvips/foreign/jp2kload.c index b142e510..e7ec398c 100644 --- a/libvips/foreign/jp2kload.c +++ b/libvips/foreign/jp2kload.c @@ -827,7 +827,10 @@ vips_foreign_load_jp2k_generate( VipsRegion *out, y += hit.height; } - if( load->fail && + /* jp2k files can't be truncated (they fail to open), so all we can + * spot is errors. + */ + if( load->fail_on >= VIPS_FAIL_ON_ERROR && jp2k->n_errors > 0 ) return( -1 ); @@ -1280,20 +1283,21 @@ vips__foreign_load_jp2k_decompress( VipsImage *out, * Optional arguments: * * * @page: %gint, load this page + * * @fail_on: #VipsFailOn, types of read error to fail on * * Read a JPEG2000 image. The loader supports 8, 16 and 32-bit int pixel - * values, signed and unsigned. - * It supports greyscale, RGB, YCC, CMYK and - * multispectral colour spaces. - * It will read any ICC profile on - * the image. + * values, signed and unsigned. It supports greyscale, RGB, YCC, CMYK and + * multispectral colour spaces. It will read any ICC profile on the image. * - * It will only load images where all channels are the same format. + * It will only load images where all channels have the same format. * * Use @page to set the page to load, where page 0 is the base resolution * image and higher-numbered pages are x2 reductions. Use the metadata item * "n-pages" to find the number of pyramid layers. * + * Use @fail_on to set the type of error that will cause load to fail. By + * default, loaders are permissive, that is, #VIPS_FAIL_ON_NONE. + * * See also: vips_image_new_from_file(). * * Returns: 0 on success, -1 on error. @@ -1321,8 +1325,12 @@ vips_jp2kload( const char *filename, VipsImage **out, ... ) * Optional arguments: * * * @page: %gint, load this page + * * @fail_on: #VipsFailOn, types of read error to fail on * - * Exactly as vips_jp2kload(), but read from a source. + * Exactly as vips_jp2kload(), but read from a buffer. + * + * You must not free the buffer while @out is active. The + * #VipsObject::postclose signal on @out is a good place to free. * * Returns: 0 on success, -1 on error. */ @@ -1355,6 +1363,7 @@ vips_jp2kload_buffer( void *buf, size_t len, VipsImage **out, ... ) * Optional arguments: * * * @page: %gint, load this page + * * @fail_on: #VipsFailOn, types of read error to fail on * * Exactly as vips_jp2kload(), but read from a source. * diff --git a/libvips/foreign/jpeg2vips.c b/libvips/foreign/jpeg2vips.c index 32b3281e..925c9977 100644 --- a/libvips/foreign/jpeg2vips.c +++ b/libvips/foreign/jpeg2vips.c @@ -110,6 +110,8 @@ * - better handling of JFIF res unit 0 * 13/9/20 * - set resolution unit from JFIF + * 24/7/21 + * - add fail-on support */ /* @@ -171,9 +173,9 @@ typedef struct _ReadJpeg { */ int shrink; - /* Fail on warning. + /* Types of error to cause failure. */ - gboolean fail; + VipsFailOn fail_on; struct jpeg_decompress_struct cinfo; ErrorManager eman; @@ -249,7 +251,7 @@ source_fill_input_buffer( j_decompress_ptr cinfo ) src->pub.bytes_in_buffer = read; } else { - if( src->jpeg->fail ) + if( src->jpeg->fail_on >= VIPS_FAIL_ON_TRUNCATED ) ERREXIT( cinfo, JERR_INPUT_EOF ); else WARNMS( cinfo, JWRN_JPEG_EOF ); @@ -355,7 +357,7 @@ readjpeg_minimise_cb( VipsImage *image, ReadJpeg *jpeg ) static ReadJpeg * readjpeg_new( VipsSource *source, VipsImage *out, - int shrink, gboolean fail, gboolean autorotate ) + int shrink, VipsFailOn fail_on, gboolean autorotate ) { ReadJpeg *jpeg; @@ -365,7 +367,7 @@ readjpeg_new( VipsSource *source, VipsImage *out, jpeg->source = source; g_object_ref( source ); jpeg->shrink = shrink; - jpeg->fail = fail; + jpeg->fail_on = fail_on; jpeg->cinfo.err = jpeg_std_error( &jpeg->eman.pub ); jpeg->eman.pub.error_exit = vips__new_error_exit; jpeg->eman.pub.output_message = vips__new_output_message; @@ -805,13 +807,8 @@ read_jpeg_generate( VipsRegion *or, return( -1 ); } - /* If --fail is set, we make read fail on any warnings. This - * will stop on any errors from the previous jpeg_read_scanlines(). - * libjpeg warnings are used for serious image corruption, like - * truncated files. - */ if( jpeg->eman.pub.num_warnings > 0 && - jpeg->fail ) { + jpeg->fail_on >= VIPS_FAIL_ON_WARNING ) { VIPS_GATE_STOP( "read_jpeg_generate: work" ); /* Only fail once. @@ -952,11 +949,13 @@ vips__jpeg_read( ReadJpeg *jpeg, VipsImage *out, gboolean header_only ) int vips__jpeg_read_source( VipsSource *source, VipsImage *out, - gboolean header_only, int shrink, int fail, gboolean autorotate ) + gboolean header_only, int shrink, VipsFailOn fail_on, + gboolean autorotate ) { ReadJpeg *jpeg; - if( !(jpeg = readjpeg_new( source, out, shrink, fail, autorotate )) ) + if( !(jpeg = readjpeg_new( source, out, shrink, fail_on, + autorotate )) ) return( -1 ); if( setjmp( jpeg->eman.jmp ) ) diff --git a/libvips/foreign/jpegload.c b/libvips/foreign/jpegload.c index 9f0d96c1..cfcb61f0 100644 --- a/libvips/foreign/jpegload.c +++ b/libvips/foreign/jpegload.c @@ -4,6 +4,8 @@ * - wrap a class around the jpeg writer * 29/11/11 * - split to make load, load from buffer and load from file + * 24/7/21 + * - add fail-on support */ /* @@ -137,7 +139,8 @@ vips_foreign_load_jpeg_header( VipsForeignLoad *load ) VipsForeignLoadJpeg *jpeg = (VipsForeignLoadJpeg *) load; if( vips__jpeg_read_source( jpeg->source, - load->out, TRUE, jpeg->shrink, load->fail, jpeg->autorotate ) ) + load->out, TRUE, jpeg->shrink, load->fail_on, + jpeg->autorotate ) ) return( -1 ); return( 0 ); @@ -149,7 +152,7 @@ vips_foreign_load_jpeg_load( VipsForeignLoad *load ) VipsForeignLoadJpeg *jpeg = (VipsForeignLoadJpeg *) load; if( vips__jpeg_read_source( jpeg->source, - load->real, FALSE, jpeg->shrink, load->fail, + load->real, FALSE, jpeg->shrink, load->fail_on, jpeg->autorotate ) ) return( -1 ); @@ -435,7 +438,7 @@ vips_foreign_load_jpeg_buffer_init( VipsForeignLoadJpegBuffer *buffer ) * Optional arguments: * * * @shrink: %gint, shrink by this much on load - * * @fail: %gboolean, fail on errors + * * @fail_on: #VipsFailOn, types of read error to fail on * * @autorotate: %gboolean, rotate image upright during load * * Read a JPEG file into a VIPS image. It can read most 8-bit JPEG images, @@ -445,9 +448,8 @@ vips_foreign_load_jpeg_buffer_init( VipsForeignLoadJpegBuffer *buffer ) * are 1, 2, 4 and 8. Shrinking during read is very much faster than * decompressing the whole image and then shrinking later. * - * Setting @fail to %TRUE makes the JPEG reader fail on any errors. - * This can be useful for detecting truncated files, for example. Normally - * reading these produces a warning, but no fatal error. + * Use @fail_on to set the type of error that will cause load to fail. By + * default, loaders are permissive, that is, #VIPS_FAIL_ON_NONE. * * Setting @autorotate to %TRUE will make the loader interpret the * orientation tag and automatically rotate the image appropriately during @@ -465,7 +467,7 @@ vips_foreign_load_jpeg_buffer_init( VipsForeignLoadJpegBuffer *buffer ) * |[ * vips_jpegload( "fred.jpg", &out, * "shrink", 8, - * "fail", TRUE, + * "fail-on", VIPS_FAIL_ON_TRUNCATED, * NULL ); * ]| * @@ -517,7 +519,7 @@ vips_jpegload( const char *filename, VipsImage **out, ... ) * Optional arguments: * * * @shrink: %gint, shrink by this much on load - * * @fail: %gboolean, fail on errors + * * @fail_on: #VipsFailOn, types of read error to fail on * * @autorotate: %gboolean, use exif Orientation tag to rotate the image * during load * @@ -560,7 +562,7 @@ vips_jpegload_buffer( void *buf, size_t len, VipsImage **out, ... ) * Optional arguments: * * * @shrink: %gint, shrink by this much on load - * * @fail: %gboolean, fail on errors + * * @fail_on: #VipsFailOn, types of read error to fail on * * @autorotate: %gboolean, use exif Orientation tag to rotate the image * during load * diff --git a/libvips/foreign/nsgifload.c b/libvips/foreign/nsgifload.c index 7298f79d..5197c1e3 100644 --- a/libvips/foreign/nsgifload.c +++ b/libvips/foreign/nsgifload.c @@ -384,7 +384,7 @@ vips_foreign_load_nsgif_header( VipsForeignLoad *load ) return( -1 ); } else if( result == GIF_INSUFFICIENT_FRAME_DATA && - load->fail ) { + load->fail_on >= VIPS_FAIL_ON_TRUNCATED ) { vips_error( class->nickname, "%s", _( "truncated GIF" ) ); return( -1 ); } @@ -887,6 +887,7 @@ vips_foreign_load_nsgif_source_init( VipsForeignLoadNsgifSource *source ) * * * @page: %gint, page (frame) to read * * @n: %gint, load this many pages + * * @fail_on: #VipsFailOn, types of read error to fail on * * Read a GIF file into a libvips image. * @@ -896,6 +897,9 @@ vips_foreign_load_nsgif_source_init( VipsForeignLoadNsgifSource *source ) * rendered in a vertical column. Set to -1 to mean "until the end of the * document". Use vips_grid() to change page layout. * + * Use @fail_on to set the type of error that will cause load to fail. By + * default, loaders are permissive, that is, #VIPS_FAIL_ON_NONE. + * * The output image is RGBA for GIFs containing transparent elements, RGB * otherwise. * @@ -927,9 +931,9 @@ vips_gifload( const char *filename, VipsImage **out, ... ) * * * @page: %gint, page (frame) to read * * @n: %gint, load this many pages + * * @fail_on: #VipsFailOn, types of read error to fail on * - * Read a GIF-formatted memory block into a VIPS image. Exactly as - * vips_gifload(), but read from a memory buffer. + * Exactly as vips_gifload(), but read from a memory buffer. * * You must not free the buffer while @out is active. The * #VipsObject::postclose signal on @out is a good place to free. @@ -968,6 +972,7 @@ vips_gifload_buffer( void *buf, size_t len, VipsImage **out, ... ) * * * @page: %gint, page (frame) to read * * @n: %gint, load this many pages + * * @fail_on: #VipsFailOn, types of read error to fail on * * Exactly as vips_gifload(), but read from a source. * diff --git a/libvips/foreign/pforeign.h b/libvips/foreign/pforeign.h index 44d955f0..28695aed 100644 --- a/libvips/foreign/pforeign.h +++ b/libvips/foreign/pforeign.h @@ -96,9 +96,9 @@ int vips__tiff_write_buf( VipsImage *in, gboolean vips__istiff_source( VipsSource *source ); gboolean vips__istifftiled_source( VipsSource *source ); int vips__tiff_read_header_source( VipsSource *source, VipsImage *out, - int page, int n, gboolean autorotate, int subifd ); + int page, int n, gboolean autorotate, int subifd, VipsFailOn fail_on ); int vips__tiff_read_source( VipsSource *source, VipsImage *out, - int page, int n, gboolean autorotate, int subifd ); + int page, int n, gboolean autorotate, int subifd, VipsFailOn fail_on ); extern const char *vips__foreign_tiff_suffs[]; @@ -108,16 +108,6 @@ int vips__analyze_read( const char *filename, VipsImage *out ); extern const char *vips__foreign_csv_suffs[]; -int vips__csv_read( const char *filename, VipsImage *out, - int skip, int lines, const char *whitespace, const char *separator, - gboolean fail ); -int vips__csv_read_header( const char *filename, VipsImage *out, - int skip, int lines, const char *whitespace, const char *separator, - gboolean fail ); - -int vips__csv_write( VipsImage *in, const char *filename, - const char *separator ); - int vips__matrix_read_header( const char *filename, int *width, int *height, double *scale, double *offset ); int vips__matrix_ismatrix( const char *filename ); @@ -183,13 +173,15 @@ int vips__jpeg_write_target( VipsImage *in, VipsTarget *target, VipsForeignSubsample subsample_mode, int restart_interval ); int vips__jpeg_read_source( VipsSource *source, VipsImage *out, - gboolean header_only, int shrink, int fail, gboolean autorotate ); + gboolean header_only, int shrink, VipsFailOn fail_on, + gboolean autorotate ); int vips__isjpeg_source( VipsSource *source ); int vips__png_ispng_source( VipsSource *source ); -int vips__png_header_source( VipsSource *source, VipsImage *out, gboolean unlimited ); +int vips__png_header_source( VipsSource *source, VipsImage *out, + gboolean unlimited ); int vips__png_read_source( VipsSource *source, VipsImage *out, - gboolean fail, gboolean unlimited ); + VipsFailOn fail_on, gboolean unlimited ); gboolean vips__png_isinterlaced_source( VipsSource *source ); extern const char *vips__png_suffs[]; diff --git a/libvips/foreign/pngload.c b/libvips/foreign/pngload.c index 1687cbc4..d4222a46 100644 --- a/libvips/foreign/pngload.c +++ b/libvips/foreign/pngload.c @@ -136,7 +136,7 @@ vips_foreign_load_png_load( VipsForeignLoad *load ) VipsForeignLoadPng *png = (VipsForeignLoadPng *) load; if( vips__png_read_source( png->source, load->real, - load->fail, png->unlimited ) ) + load->fail_on, png->unlimited ) ) return( -1 ); return( 0 ); @@ -412,6 +412,7 @@ vips_foreign_load_png_buffer_init( VipsForeignLoadPngBuffer *buffer ) * * Optional arguments: * + * * @fail_on: #VipsFailOn, types of read error to fail on * * @unlimited: %gboolean, remove all denial of service limits * * Read a PNG file into a VIPS image. It can read all png images, including 8- @@ -420,6 +421,9 @@ vips_foreign_load_png_buffer_init( VipsForeignLoadPngBuffer *buffer ) * Any ICC profile is read and attached to the VIPS image. It also supports * XMP metadata. * + * Use @fail_on to set the type of error that will cause load to fail. By + * default, loaders are permissive, that is, #VIPS_FAIL_ON_NONE. + * * By default, the PNG loader limits the number of text and data chunks to * block some denial of service attacks. Set @unlimited to disable these * limits. @@ -450,6 +454,7 @@ vips_pngload( const char *filename, VipsImage **out, ... ) * * Optional arguments: * + * * @fail_on: #VipsFailOn, types of read error to fail on * * @unlimited: %gboolean, Remove all denial of service limits * * Exactly as vips_pngload(), but read from a PNG-formatted memory block. @@ -489,6 +494,7 @@ vips_pngload_buffer( void *buf, size_t len, VipsImage **out, ... ) * * Optional arguments: * + * * @fail_on: #VipsFailOn, types of read error to fail on * * @unlimited: %gboolean, Remove all denial of service limits * * Exactly as vips_pngload(), but read from a source. diff --git a/libvips/foreign/spngload.c b/libvips/foreign/spngload.c index 91efa809..f0c7c27d 100644 --- a/libvips/foreign/spngload.c +++ b/libvips/foreign/spngload.c @@ -342,10 +342,10 @@ vips_foreign_load_png_header( VipsForeignLoad *load ) /* In non-fail mode, ignore CRC errors. */ flags = 0; - if( !load->fail ) + if( load->fail_on >= VIPS_FAIL_ON_ERROR ) flags |= SPNG_CTX_IGNORE_ADLER32; png->ctx = spng_ctx_new( flags ); - if( !load->fail ) + if( load->fail_on >= VIPS_FAIL_ON_ERROR ) /* Ignore and don't calculate checksums. */ spng_set_crc_action( png->ctx, SPNG_CRC_USE, SPNG_CRC_USE ); @@ -557,11 +557,11 @@ vips_foreign_load_png_generate( VipsRegion *or, g_warning( "%s: %s", class->nickname, spng_strerror( error ) ); - /* And bail if fail is on. + /* And bail if trunc is on. */ - if( load->fail ) { + if( load->fail_on >= VIPS_FAIL_ON_TRUNCATED ) { vips_error( class->nickname, - "%s", _( "libpng read error" ) ); + "%s", _( "libspng read error" ) ); return( -1 ); } } diff --git a/libvips/foreign/tiff2vips.c b/libvips/foreign/tiff2vips.c index 3c8a6fb7..f62a657f 100644 --- a/libvips/foreign/tiff2vips.c +++ b/libvips/foreign/tiff2vips.c @@ -203,6 +203,8 @@ * - support 2 and 4 bit greyscale load * 27/3/21 * - add jp2k decompresion + * 24/7/21 + * - add fail_on * 30/9/21 * - fix tiled + packed formats */ @@ -361,6 +363,7 @@ typedef struct _Rtiff { int n; gboolean autorotate; int subifd; + VipsFailOn fail_on; /* The TIFF we read. */ @@ -574,7 +577,7 @@ rtiff_minimise_cb( VipsImage *image, Rtiff *rtiff ) static Rtiff * rtiff_new( VipsSource *source, VipsImage *out, - int page, int n, gboolean autorotate, int subifd ) + int page, int n, gboolean autorotate, int subifd, VipsFailOn fail_on ) { Rtiff *rtiff; @@ -588,6 +591,7 @@ rtiff_new( VipsSource *source, VipsImage *out, rtiff->n = n; rtiff->autorotate = autorotate; rtiff->subifd = subifd; + rtiff->fail_on = fail_on; rtiff->tiff = NULL; rtiff->n_pages = 0; rtiff->current_page = -1; @@ -2863,13 +2867,14 @@ vips__istifftiled_source( VipsSource *source ) int vips__tiff_read_header_source( VipsSource *source, VipsImage *out, - int page, int n, gboolean autorotate, int subifd ) + int page, int n, gboolean autorotate, int subifd, VipsFailOn fail_on ) { Rtiff *rtiff; vips__tiff_init(); - if( !(rtiff = rtiff_new( source, out, page, n, autorotate, subifd )) || + if( !(rtiff = rtiff_new( source, out, + page, n, autorotate, subifd, fail_on )) || rtiff_header_read_all( rtiff ) ) return( -1 ); @@ -2892,7 +2897,7 @@ vips__tiff_read_header_source( VipsSource *source, VipsImage *out, int vips__tiff_read_source( VipsSource *source, VipsImage *out, - int page, int n, gboolean autorotate, int subifd ) + int page, int n, gboolean autorotate, int subifd, VipsFailOn fail_on ) { Rtiff *rtiff; @@ -2902,7 +2907,8 @@ vips__tiff_read_source( VipsSource *source, VipsImage *out, vips__tiff_init(); - if( !(rtiff = rtiff_new( source, out, page, n, autorotate, subifd )) || + if( !(rtiff = rtiff_new( source, out, + page, n, autorotate, subifd, fail_on )) || rtiff_header_read_all( rtiff ) ) return( -1 ); diff --git a/libvips/foreign/tiffload.c b/libvips/foreign/tiffload.c index 58cbb6ab..13d19219 100644 --- a/libvips/foreign/tiffload.c +++ b/libvips/foreign/tiffload.c @@ -137,7 +137,8 @@ vips_foreign_load_tiff_header( VipsForeignLoad *load ) VipsForeignLoadTiff *tiff = (VipsForeignLoadTiff *) load; if( vips__tiff_read_header_source( tiff->source, load->out, - tiff->page, tiff->n, tiff->autorotate, tiff->subifd ) ) + tiff->page, tiff->n, tiff->autorotate, tiff->subifd, + load->fail_on ) ) return( -1 ); return( 0 ); @@ -149,7 +150,8 @@ vips_foreign_load_tiff_load( VipsForeignLoad *load ) VipsForeignLoadTiff *tiff = (VipsForeignLoadTiff *) load; if( vips__tiff_read_source( tiff->source, load->real, - tiff->page, tiff->n, tiff->autorotate, tiff->subifd ) ) + tiff->page, tiff->n, tiff->autorotate, tiff->subifd, + load->fail_on ) ) return( -1 ); return( 0 ); diff --git a/libvips/foreign/vipspng.c b/libvips/foreign/vipspng.c index 2590c474..19bc4ab0 100644 --- a/libvips/foreign/vipspng.c +++ b/libvips/foreign/vipspng.c @@ -174,7 +174,7 @@ user_warning_function( png_structp png_ptr, png_const_charp warning_msg ) typedef struct { char *name; VipsImage *out; - gboolean fail; + VipsFailOn fail_on; gboolean unlimited; int y_pos; @@ -259,7 +259,7 @@ vips_png_read_source( png_structp pPng, png_bytep data, png_size_t length ) static Read * read_new( VipsSource *source, VipsImage *out, - gboolean fail, gboolean unlimited ) + VipsFailOn fail_on, gboolean unlimited ) { Read *read; @@ -267,7 +267,7 @@ read_new( VipsSource *source, VipsImage *out, return( NULL ); read->name = NULL; - read->fail = fail; + read->fail_on = fail_on; read->out = out; read->y_pos = 0; read->pPng = NULL; @@ -727,7 +727,7 @@ png2vips_generate( VipsRegion *or, * message, since the handler we install just does * g_warning(). */ - if( read->fail ) { + if( read->fail_on >= VIPS_FAIL_ON_TRUNCATED ) { vips_error( "vipspng", "%s", _( "libpng read error" ) ); return( -1 ); @@ -805,11 +805,11 @@ vips__png_header_source( VipsSource *source, VipsImage *out, int vips__png_read_source( VipsSource *source, VipsImage *out, - gboolean fail, gboolean unlimited ) + VipsFailOn fail_on, gboolean unlimited ) { Read *read; - if( !(read = read_new( source, out, fail, unlimited )) || + if( !(read = read_new( source, out, fail_on, unlimited )) || png2vips_image( read, out ) || vips_source_decode( source ) ) { vips_error( "png2vips", _( "unable to read source %s" ), diff --git a/libvips/include/vips/enumtypes.h b/libvips/include/vips/enumtypes.h index 772e14a1..8fa06412 100644 --- a/libvips/include/vips/enumtypes.h +++ b/libvips/include/vips/enumtypes.h @@ -56,6 +56,8 @@ GType vips_combine_mode_get_type (void) G_GNUC_CONST; /* enumerations from "../../../libvips/include/vips/foreign.h" */ GType vips_foreign_flags_get_type (void) G_GNUC_CONST; #define VIPS_TYPE_FOREIGN_FLAGS (vips_foreign_flags_get_type()) +GType vips_fail_on_get_type (void) G_GNUC_CONST; +#define VIPS_TYPE_FAIL_ON (vips_fail_on_get_type()) GType vips_saveable_get_type (void) G_GNUC_CONST; #define VIPS_TYPE_SAVEABLE (vips_saveable_get_type()) GType vips_foreign_subsample_get_type (void) G_GNUC_CONST; diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index 099a4a5f..5793c265 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -100,6 +100,27 @@ typedef enum /*< flags >*/ { VIPS_FOREIGN_ALL = 7 /* All flags set */ } VipsForeignFlags; +/** + * VipsFailOn: + * @VIPS_FAIL_ON_NONE: never stop + * @VIPS_FAIL_ON_TRUNCATED: stop on image truncated, nothing else + * @VIPS_FAIL_ON_ERROR: stop on serious error or truncation + * @VIPS_FAIL_ON_WARNING: stop on anything, even warnings + * + * How sensitive loaders are to errors, from never stop (very insensitive), to + * stop on the smallest warning (very sensitive). + * + * Each one implies the ones before it, so #VIPS_FAIL_ON_ERROR implies + * #VIPS_FAIL_ON_TRUNCATED. + */ +typedef enum { + VIPS_FAIL_ON_NONE, + VIPS_FAIL_ON_TRUNCATED, + VIPS_FAIL_ON_ERROR, + VIPS_FAIL_ON_WARNING, + VIPS_FAIL_ON_LAST +} VipsFailOn; + #define VIPS_TYPE_FOREIGN_LOAD (vips_foreign_load_get_type()) #define VIPS_FOREIGN_LOAD( obj ) \ (G_TYPE_CHECK_INSTANCE_CAST( (obj), \ @@ -131,12 +152,13 @@ typedef struct _VipsForeignLoad { */ VipsForeignFlags flags; - /* Stop load on first warning. + /* Behaviour on error. + */ + VipsFailOn fail_on; + + /* Deprecated and unused. Just here for compat. */ gboolean fail; - - /* Deprecated and unused, just here for compat. - */ gboolean sequential; /*< public >*/ diff --git a/libvips/iofuncs/enumtypes.c b/libvips/iofuncs/enumtypes.c index 0109ed30..0dd410ce 100644 --- a/libvips/iofuncs/enumtypes.c +++ b/libvips/iofuncs/enumtypes.c @@ -479,6 +479,26 @@ vips_foreign_flags_get_type( void ) return( etype ); } GType +vips_fail_on_get_type( void ) +{ + static GType etype = 0; + + if( etype == 0 ) { + static const GEnumValue values[] = { + {VIPS_FAIL_ON_NONE, "VIPS_FAIL_ON_NONE", "none"}, + {VIPS_FAIL_ON_TRUNCATED, "VIPS_FAIL_ON_TRUNCATED", "truncated"}, + {VIPS_FAIL_ON_ERROR, "VIPS_FAIL_ON_ERROR", "error"}, + {VIPS_FAIL_ON_WARNING, "VIPS_FAIL_ON_WARNING", "warning"}, + {VIPS_FAIL_ON_LAST, "VIPS_FAIL_ON_LAST", "last"}, + {0, NULL, NULL} + }; + + etype = g_enum_register_static( "VipsFailOn", values ); + } + + return( etype ); +} +GType vips_saveable_get_type( void ) { static GType etype = 0; diff --git a/test/test-suite/test_foreign.py b/test/test-suite/test_foreign.py index d9bcff8e..df753880 100644 --- a/test/test-suite/test_foreign.py +++ b/test/test-suite/test_foreign.py @@ -1277,5 +1277,33 @@ class TestForeign: assert x1.get("page-height") == x2.get("page-height") assert x1.get("loop") == x2.get("loop") + def test_fail_on(self): + # csvload should spot trunc correctly + target = pyvips.Target.new_to_memory() + self.mono.write_to_target(target, ".csv") + buf = target.get("blob") + + source = pyvips.Source.new_from_memory(buf) + im = pyvips.Image.csvload_source(source) + assert im.avg() > 0 + + # truncation should be OK by default + buf_trunc = buf[:-100] + source = pyvips.Source.new_from_memory(buf_trunc) + im = pyvips.Image.csvload_source(source) + assert im.avg() > 0 + + # set trunc should make it fail + with pytest.raises(Exception) as e_info: + im = pyvips.Image.csvload_source(source, fail_on="truncated") + # this will now force parsing of the whole file, which should + # trigger an error + im.avg() > 0 + + # warn should fail too, since trunc implies warn + with pytest.raises(Exception) as e_info: + im = pyvips.Image.csvload_source(source, fail_on="warning") + im.avg() > 0 + if __name__ == '__main__': pytest.main()