Skip to content

Commit f628128

Browse files
committed
fix VipsSource and named pipes
We used to assume (in several places) that any source with a filename was seekable. This patch adds a is_file test, and makes all the loaders use it. see libvips#2467
1 parent 60bae63 commit f628128

File tree

8 files changed

+140
-80
lines changed

8 files changed

+140
-80
lines changed

ChangeLog

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- flatten handles out of range alpha and max_alpha correctly
1111
- don't use atexit for cleanup, it's too unreliable
1212
- tiff writer loops for the whole image rather than per page [LionelArn2]
13+
- fix VipsSource with named pipes [vibbix]
1314

1415
16/8/21 started 8.11.4
1516
- fix off-by-one error in new rank fast path

libvips/foreign/fitsload.c

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,17 +88,22 @@ vips_foreign_load_fits_build( VipsObject *object )
8888
VipsForeignLoadFits *fits =
8989
(VipsForeignLoadFits *) object;
9090

91-
/* We can only open source which have an associated filename, since
91+
/* We can only open sources which have an associated filename, since
9292
* the fits library works in terms of filenames.
9393
*/
9494
if( fits->source ) {
95-
fits->filename = vips_connection_filename( VIPS_CONNECTION(
96-
fits->source ) );
97-
if( !fits->filename ) {
95+
VipsConnection *connection = VIPS_CONNECTION( fits->source );
96+
97+
const char *filename;
98+
99+
if( !vips_source_is_file( fits->source ) ||
100+
!(filename = vips_connection_filename( connection )) ) {
98101
vips_error( class->nickname, "%s",
99102
_( "no filename available" ) );
100103
return( -1 );
101104
}
105+
106+
fits->filename = filename;
102107
}
103108

104109
if( VIPS_OBJECT_CLASS( vips_foreign_load_fits_parent_class )->
@@ -296,10 +301,12 @@ vips_foreign_load_fits_source_build( VipsObject *object )
296301
static gboolean
297302
vips_foreign_load_fits_source_is_a_source( VipsSource *source )
298303
{
304+
VipsConnection *connection = VIPS_CONNECTION( source );
305+
299306
const char *filename;
300307

301-
return( (filename =
302-
vips_connection_filename( VIPS_CONNECTION( source ) )) &&
308+
return( vips_source_is_file( source ) &&
309+
(filename = vips_connection_filename( connection )) &&
303310
vips__fits_isfits( filename ) );
304311
}
305312

libvips/foreign/niftiload.c

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,18 @@ vips_foreign_load_nifti_build( VipsObject *object )
121121
* the nifti library works in terms of filenames.
122122
*/
123123
if( nifti->source ) {
124-
nifti->filename = vips_connection_filename( VIPS_CONNECTION(
125-
nifti->source ) );
126-
if( !nifti->filename ) {
127-
vips_error( class->nickname, "%s",
128-
_( "no filename available" ) );
124+
VipsConnection *connection = VIPS_CONNECTION( nifti->source );
125+
126+
const char *filename;
127+
128+
if( !vips_source_is_file( nifti->source ) ||
129+
!(filename = vips_connection_filename( connection )) ) {
130+
vips_error( class->nickname,
131+
"%s", _( "no filename available" ) );
129132
return( -1 );
130133
}
134+
135+
nifti->filename = filename;
131136
}
132137

133138
if( VIPS_OBJECT_CLASS( vips_foreign_load_nifti_parent_class )->
@@ -747,10 +752,12 @@ vips_foreign_load_nifti_source_build( VipsObject *object )
747752
static gboolean
748753
vips_foreign_load_nifti_source_is_a_source( VipsSource *source )
749754
{
755+
VipsConnection *connection = VIPS_CONNECTION( source );
756+
750757
const char *filename;
751758

752-
return( (filename =
753-
vips_connection_filename( VIPS_CONNECTION( source ) )) &&
759+
return( vips_source_is_file( source ) &&
760+
(filename = vips_connection_filename( connection )) &&
754761
vips_foreign_load_nifti_is_a( filename ) );
755762
}
756763

libvips/foreign/openslideload.c

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -761,14 +761,19 @@ vips_foreign_load_openslide_build( VipsObject *object )
761761
* the openslide library works in terms of filenames.
762762
*/
763763
if( openslide->source ) {
764-
openslide->filename =
765-
vips_connection_filename( VIPS_CONNECTION(
766-
openslide->source ) );
767-
if( !openslide->filename ) {
764+
VipsConnection *connection =
765+
VIPS_CONNECTION( openslide->source );
766+
767+
const char *filename;
768+
769+
if( !vips_source_is_file( openslide->source ) ||
770+
!(filename = vips_connection_filename( connection )) ) {
768771
vips_error( class->nickname, "%s",
769772
_( "no filename available" ) );
770773
return( -1 );
771774
}
775+
776+
openslide->filename = filename;
772777
}
773778

774779
if( VIPS_OBJECT_CLASS( vips_foreign_load_openslide_parent_class )->
@@ -1033,10 +1038,12 @@ vips_foreign_load_openslide_source_build( VipsObject *object )
10331038
static gboolean
10341039
vips_foreign_load_openslide_source_is_a_source( VipsSource *source )
10351040
{
1041+
VipsConnection *connection = VIPS_CONNECTION( source );
1042+
10361043
const char *filename;
10371044

1038-
return( (filename =
1039-
vips_connection_filename( VIPS_CONNECTION( source ) )) &&
1045+
return( vips_source_is_file( source ) &&
1046+
(filename = vips_connection_filename( connection )) &&
10401047
vips__openslide_isslide( filename ) );
10411048
}
10421049

libvips/foreign/radiance.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -873,7 +873,7 @@ rad2vips_get_header( Read *read, VipsImage *out )
873873
interpretation,
874874
1, read->aspect );
875875

876-
VIPS_SETSTR( out->filename,
876+
VIPS_SETSTR( out->filename,
877877
vips_connection_filename(
878878
VIPS_CONNECTION( read->sbuf->source ) ) );
879879

libvips/foreign/vipsload.c

Lines changed: 11 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -110,31 +110,24 @@ vips_foreign_load_vips_get_flags_filename( const char *filename )
110110
static int
111111
vips_foreign_load_vips_header( VipsForeignLoad *load )
112112
{
113+
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load );
113114
VipsForeignLoadVips *vips = (VipsForeignLoadVips *) load;
115+
VipsConnection *connection = VIPS_CONNECTION( vips->source );
114116

115117
const char *filename;
116118
VipsImage *image;
117119
VipsImage *x;
118120

119-
if( (filename =
120-
vips_connection_filename( VIPS_CONNECTION( vips->source ) )) ) {
121-
if( !(image = vips_image_new_mode( filename, "r" )) )
122-
return( -1 );
123-
}
124-
else {
125-
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( load );
126-
127-
/* We could add load vips from memory, fd, via mmap etc. here.
128-
* We should perhaps move iofuncs/vips.c into this file.
129-
*
130-
* For now, just fail unless there's a filename associated
131-
* with this source.
132-
*/
121+
if( !vips_source_is_file( vips->source ) ||
122+
!(filename = vips_connection_filename( connection )) ) {
133123
vips_error( class->nickname,
134124
"%s", _( "no filename associated with source" ) );
135125
return( -1 );
136126
}
137127

128+
if( !(image = vips_image_new_mode( filename, "r" )) )
129+
return( -1 );
130+
138131
/* What a hack. Remove the @out that's there now and replace it with
139132
* our image.
140133
*/
@@ -280,10 +273,12 @@ vips_foreign_load_vips_source_build( VipsObject *object )
280273
static gboolean
281274
vips_foreign_load_vips_source_is_a_source( VipsSource *source )
282275
{
276+
VipsConnection *connection = VIPS_CONNECTION( source );
277+
283278
const char *filename;
284279

285-
return( (filename =
286-
vips_connection_filename( VIPS_CONNECTION( source ) )) &&
280+
return( vips_source_is_file( source ) &&
281+
(filename = vips_connection_filename( connection )) &&
287282
vips__file_magic( filename ) );
288283
}
289284

libvips/include/vips/connection.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ int vips_source_unminimise( VipsSource *source );
218218
int vips_source_decode( VipsSource *source );
219219
gint64 vips_source_read( VipsSource *source, void *data, size_t length );
220220
gboolean vips_source_is_mappable( VipsSource *source );
221+
gboolean vips_source_is_file( VipsSource *source );
221222
const void *vips_source_map( VipsSource *source, size_t *length );
222223
VipsBlob *vips_source_map_blob( VipsSource *source );
223224
gint64 vips_source_seek( VipsSource *source, gint64 offset, int whence );

libvips/iofuncs/source.c

Lines changed: 86 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
* 26/11/20
1111
* - use _setmode() on win to force binary read for previously opened
1212
* descriptors
13+
* 8/10/21
14+
* - fix named pipes
1315
*/
1416

1517
/*
@@ -129,59 +131,66 @@ vips_pipe_read_limit_set( gint64 limit )
129131

130132
G_DEFINE_TYPE( VipsSource, vips_source, VIPS_TYPE_CONNECTION );
131133

132-
/* We can't test for seekability or length during _build, since the read and
133-
* seek signal handlers might not have been connected yet. Instead, we test
134-
* when we first need to know.
134+
/* Does this source support seek. You must unminimise before calling this.
135135
*/
136136
static int
137-
vips_source_test_features( VipsSource *source )
137+
vips_source_test_seek( VipsSource *source )
138138
{
139-
VipsSourceClass *class = VIPS_SOURCE_GET_CLASS( source );
140-
141-
if( source->have_tested_seek )
142-
return( 0 );
143-
source->have_tested_seek = TRUE;
139+
if( !source->have_tested_seek ) {
140+
VipsSourceClass *class = VIPS_SOURCE_GET_CLASS( source );
144141

145-
VIPS_DEBUG_MSG( "vips_source_test_features: testing seek ..\n" );
142+
source->have_tested_seek = TRUE;
146143

147-
/* We'll need a descriptor to test.
148-
*/
149-
if( vips_source_unminimise( source ) )
150-
return( -1 );
144+
VIPS_DEBUG_MSG( "vips_source_can_seek: testing seek ..\n" );
151145

152-
/* Can we seek this input?
153-
*
154-
* We need to call the method directly rather than via
155-
* vips_source_seek() etc. or we might trigger seek emulation.
156-
*/
157-
if( source->data ||
158-
class->seek( source, 0, SEEK_CUR ) != -1 ) {
159-
gint64 length;
146+
/* Can we seek this input?
147+
*
148+
* We need to call the method directly rather than via
149+
* vips_source_seek() etc. or we might trigger seek emulation.
150+
*/
151+
if( source->data ||
152+
class->seek( source, 0, SEEK_CUR ) != -1 ) {
153+
gint64 length;
160154

161-
VIPS_DEBUG_MSG( " seekable source\n" );
155+
VIPS_DEBUG_MSG( " seekable source\n" );
162156

163-
/* We should be able to get the length of seekable
164-
* objects.
165-
*/
166-
if( (length = vips_source_length( source )) == -1 )
167-
return( -1 );
157+
/* We should be able to get the length of seekable
158+
* objects.
159+
*/
160+
if( (length = vips_source_length( source )) == -1 )
161+
return( -1 );
168162

169-
source->length = length;
163+
source->length = length;
170164

171-
/* If we can seek, we won't need to save header bytes.
172-
*/
173-
VIPS_FREEF( g_byte_array_unref, source->header_bytes );
174-
}
175-
else {
176-
/* Not seekable. This must be some kind of pipe.
177-
*/
178-
VIPS_DEBUG_MSG( " not seekable\n" );
179-
source->is_pipe = TRUE;
165+
/* If we can seek, we won't need to save header bytes.
166+
*/
167+
VIPS_FREEF( g_byte_array_unref, source->header_bytes );
168+
}
169+
else {
170+
/* Not seekable. This must be some kind of pipe.
171+
*/
172+
VIPS_DEBUG_MSG( " not seekable\n" );
173+
source->is_pipe = TRUE;
174+
}
180175
}
181176

182177
return( 0 );
183178
}
184179

180+
/* We can't test for seekability or length during _build, since the read and
181+
* seek signal handlers might not have been connected yet. Instead, we test
182+
* when we first need to know.
183+
*/
184+
static int
185+
vips_source_test_features( VipsSource *source )
186+
{
187+
if( vips_source_unminimise( source ) ||
188+
vips_source_test_seek( source ) )
189+
return( -1 );
190+
191+
return( 0 );
192+
}
193+
185194
#ifdef TEST_SANITY
186195
static void
187196
vips_source_sanity( VipsSource *source )
@@ -634,12 +643,19 @@ vips_source_unminimise( VipsSource *source )
634643
connection->tracked_descriptor = fd;
635644
connection->descriptor = fd;
636645

637-
VIPS_DEBUG_MSG( "vips_source_unminimise: "
638-
"restoring read position %" G_GINT64_FORMAT "\n",
639-
source->read_position );
640-
if( vips__seek( connection->descriptor,
641-
source->read_position, SEEK_SET ) == -1 )
642-
return( -1 );
646+
if( vips_source_test_seek( source ) )
647+
return( -1 );
648+
649+
/* It might be a named pipe.
650+
*/
651+
if( !source->is_pipe ) {
652+
VIPS_DEBUG_MSG( "vips_source_unminimise: restoring "
653+
"read position %" G_GINT64_FORMAT "\n",
654+
source->read_position );
655+
if( vips__seek( connection->descriptor,
656+
source->read_position, SEEK_SET ) == -1 )
657+
return( -1 );
658+
}
643659
}
644660

645661
return( 0 );
@@ -976,6 +992,32 @@ vips_source_is_mappable( VipsSource *source )
976992
VIPS_CONNECTION( source )->descriptor != -1) );
977993
}
978994

995+
/**
996+
* vips_source_is_file:
997+
* @source: source to operate on
998+
*
999+
* Test if this source is a simple file with support for seek. Named pipes,
1000+
* for example, will fail this test. If TRUE, you can use
1001+
* vips_connection_filename() to find the filename.
1002+
*
1003+
* Use this to add basic source support for older loaders which can only work
1004+
* on files.
1005+
*
1006+
* Returns: %TRUE if the source is a simple file.
1007+
*/
1008+
gboolean
1009+
vips_source_is_file( VipsSource *source )
1010+
{
1011+
if( vips_source_unminimise( source ) ||
1012+
vips_source_test_features( source ) )
1013+
return( -1 );
1014+
1015+
/* There's a filename, and it supports seek.
1016+
*/
1017+
return( VIPS_CONNECTION( source )->filename &&
1018+
!source->is_pipe );
1019+
}
1020+
9791021
/**
9801022
* vips_source_map:
9811023
* @source: source to operate on

0 commit comments

Comments
 (0)