Coverage Report

Created: 2026-07-30 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libtiff/libtiff/tif_stream.cxx
Line
Count
Source
1
/*
2
 * Copyright (c) 1988-1996 Sam Leffler
3
 * Copyright (c) 1991-1996 Silicon Graphics, Inc.
4
 *
5
 * Permission to use, copy, modify, distribute, and sell this software and
6
 * its documentation for any purpose is hereby granted without fee, provided
7
 * that (i) the above copyright notices and this permission notice appear in
8
 * all copies of the software and related documentation, and (ii) the names of
9
 * Sam Leffler and Silicon Graphics may not be used in any advertising or
10
 * publicity relating to the software without the specific, prior written
11
 * permission of Sam Leffler and Silicon Graphics.
12
 *
13
 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
14
 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
15
 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
16
 *
17
 * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
18
 * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
19
 * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
20
 * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF
21
 * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
22
 * OF THIS SOFTWARE.
23
 */
24
25
/*
26
 * TIFF Library UNIX-specific Routines.
27
 */
28
#include "tiffiop.h"
29
#include <iostream>
30
31
using namespace std;
32
33
/*
34
  ISO C++ uses a 'std::streamsize' type to define counts.  This makes
35
  it similar to, (but perhaps not the same as) size_t.
36
37
  The std::ios::pos_type is used to represent stream positions as used
38
  by tellg(), tellp(), seekg(), and seekp().  This makes it similar to
39
  (but perhaps not the same as) 'off_t'.  The std::ios::streampos type
40
  is used for character streams, but is documented to not be an
41
  integral type anymore, so it should *not* be assigned to an integral
42
  type.
43
44
  The std::ios::off_type is used to specify relative offsets needed by
45
  the variants of seekg() and seekp() which accept a relative offset
46
  argument.
47
48
  Useful prototype knowledge:
49
50
  Obtain read position
51
    ios::pos_type basic_istream::tellg()
52
53
  Set read position
54
    basic_istream& basic_istream::seekg(ios::pos_type)
55
    basic_istream& basic_istream::seekg(ios::off_type, ios_base::seekdir)
56
57
  Read data
58
    basic_istream& istream::read(char *str, streamsize count)
59
60
  Number of characters read in last unformatted read
61
    streamsize istream::gcount();
62
63
  Obtain write position
64
    ios::pos_type basic_ostream::tellp()
65
66
  Set write position
67
    basic_ostream& basic_ostream::seekp(ios::pos_type)
68
    basic_ostream& basic_ostream::seekp(ios::off_type, ios_base::seekdir)
69
70
  Write data
71
    basic_ostream& ostream::write(const char *str, streamsize count)
72
*/
73
74
struct tiffis_data;
75
struct tiffos_data;
76
77
extern "C"
78
{
79
80
    static tmsize_t _tiffosReadProc(thandle_t, void *, tmsize_t);
81
    static tmsize_t _tiffisReadProc(thandle_t fd, void *buf, tmsize_t size);
82
    static tmsize_t _tiffosWriteProc(thandle_t fd, void *buf, tmsize_t size);
83
    static tmsize_t _tiffisWriteProc(thandle_t, void *, tmsize_t);
84
    static uint64_t _tiffosSeekProc(thandle_t fd, uint64_t off, int whence);
85
    static uint64_t _tiffisSeekProc(thandle_t fd, uint64_t off, int whence);
86
    static uint64_t _tiffosSizeProc(thandle_t fd);
87
    static uint64_t _tiffisSizeProc(thandle_t fd);
88
    static int _tiffosCloseProc(thandle_t fd);
89
    static int _tiffisCloseProc(thandle_t fd);
90
    static int _tiffDummyMapProcCxx(thandle_t, void **base, toff_t *size);
91
    static void _tiffDummyUnmapProcCxx(thandle_t, void *base, toff_t size);
92
    static TIFF *_tiffStreamOpen(const char *name, const char *mode, void *fd);
93
94
    struct tiffis_data
95
    {
96
        istream *stream;
97
        ios::pos_type start_pos;
98
    };
99
100
    struct tiffos_data
101
    {
102
        ostream *stream;
103
        ios::pos_type start_pos;
104
    };
105
106
0
    static tmsize_t _tiffosReadProc(thandle_t, void *, tmsize_t) { return 0; }
107
108
    static tmsize_t _tiffisReadProc(thandle_t fd, void *buf, tmsize_t size)
109
0
    {
110
0
        tiffis_data *data = reinterpret_cast<tiffis_data *>(fd);
111
112
        // Verify that type does not overflow.
113
0
        streamsize request_size = size;
114
0
        if (static_cast<tmsize_t>(request_size) != size)
115
0
            return static_cast<tmsize_t>(-1);
116
117
0
        data->stream->read((char *)buf, request_size);
118
119
0
        return static_cast<tmsize_t>(data->stream->gcount());
120
0
    }
121
122
    static tmsize_t _tiffosWriteProc(thandle_t fd, void *buf, tmsize_t size)
123
0
    {
124
0
        tiffos_data *data = reinterpret_cast<tiffos_data *>(fd);
125
0
        ostream *os = data->stream;
126
0
        ios::pos_type pos = os->tellp();
127
128
        // Verify that type does not overflow.
129
0
        streamsize request_size = size;
130
0
        if (static_cast<tmsize_t>(request_size) != size)
131
0
            return static_cast<tmsize_t>(-1);
132
133
0
        os->write(reinterpret_cast<const char *>(buf), request_size);
134
135
0
        return static_cast<tmsize_t>(os->tellp() - pos);
136
0
    }
137
138
0
    static tmsize_t _tiffisWriteProc(thandle_t, void *, tmsize_t) { return 0; }
139
140
    static uint64_t _tiffosSeekProc(thandle_t fd, uint64_t off, int whence)
141
0
    {
142
0
        tiffos_data *data = reinterpret_cast<tiffos_data *>(fd);
143
0
        ostream *os = data->stream;
144
145
        // if the stream has already failed, don't do anything
146
0
        if (os->fail())
147
0
            return static_cast<uint64_t>(-1);
148
149
0
        switch (whence)
150
0
        {
151
0
            case SEEK_SET:
152
0
            {
153
                // Compute 64-bit offset
154
0
                uint64_t new_offset =
155
0
                    static_cast<uint64_t>(data->start_pos) + off;
156
157
                // Verify that value does not overflow
158
0
                ios::off_type offset = static_cast<ios::off_type>(new_offset);
159
0
                if (static_cast<uint64_t>(offset) != new_offset)
160
0
                    return static_cast<uint64_t>(-1);
161
162
0
                os->seekp(offset, ios::beg);
163
0
                break;
164
0
            }
165
0
            case SEEK_CUR:
166
0
            {
167
                // Verify that value does not overflow
168
0
                ios::off_type offset = static_cast<ios::off_type>(off);
169
0
                if (static_cast<uint64_t>(offset) != off)
170
0
                    return static_cast<uint64_t>(-1);
171
172
0
                os->seekp(offset, ios::cur);
173
0
                break;
174
0
            }
175
0
            case SEEK_END:
176
0
            {
177
                // Verify that value does not overflow
178
0
                ios::off_type offset = static_cast<ios::off_type>(off);
179
0
                if (static_cast<uint64_t>(offset) != off)
180
0
                    return static_cast<uint64_t>(-1);
181
182
0
                os->seekp(offset, ios::end);
183
0
                break;
184
0
            }
185
0
        }
186
187
        // Attempt to workaround problems with seeking past the end of the
188
        // stream.  ofstream doesn't have a problem with this but
189
        // ostrstream/ostringstream does. In that situation, add intermediate
190
        // '\0' characters.
191
0
        if (os->fail())
192
0
        {
193
0
            ios::iostate old_state;
194
0
            ios::pos_type origin;
195
196
0
            old_state = os->rdstate();
197
            // reset the fail bit or else tellp() won't work below
198
0
            os->clear(os->rdstate() & ~ios::failbit);
199
0
            switch (whence)
200
0
            {
201
0
                case SEEK_SET:
202
0
                default:
203
0
                    origin = data->start_pos;
204
0
                    break;
205
0
                case SEEK_CUR:
206
0
                    origin = os->tellp();
207
0
                    break;
208
0
                case SEEK_END:
209
0
                    os->seekp(0, ios::end);
210
0
                    origin = os->tellp();
211
0
                    break;
212
0
            }
213
            // restore original stream state
214
0
            os->clear(old_state);
215
216
            // only do something if desired seek position is valid
217
0
            if ((static_cast<uint64_t>(origin) + off) >
218
0
                static_cast<uint64_t>(data->start_pos))
219
0
            {
220
0
                uint64_t num_fill;
221
222
                // clear the fail bit
223
0
                os->clear(os->rdstate() & ~ios::failbit);
224
225
                // extend the stream to the expected size
226
0
                os->seekp(0, ios::end);
227
0
                num_fill = (static_cast<uint64_t>(origin)) + off - os->tellp();
228
0
                for (uint64_t i = 0; i < num_fill; i++)
229
0
                    os->put('\0');
230
231
                // retry the seek
232
0
                os->seekp(static_cast<ios::off_type>(
233
0
                              static_cast<uint64_t>(origin) + off),
234
0
                          ios::beg);
235
0
            }
236
0
        }
237
238
0
        return static_cast<uint64_t>(os->tellp());
239
0
    }
240
241
    static uint64_t _tiffisSeekProc(thandle_t fd, uint64_t off, int whence)
242
0
    {
243
0
        tiffis_data *data = reinterpret_cast<tiffis_data *>(fd);
244
245
0
        if (data->start_pos == ios::pos_type(-1))
246
0
            return static_cast<uint64_t>(-1);
247
248
0
        switch (whence)
249
0
        {
250
0
            case SEEK_SET:
251
0
            {
252
                // Compute 64-bit offset
253
0
                uint64_t new_offset =
254
0
                    static_cast<uint64_t>(data->start_pos) + off;
255
256
                // Verify that value does not overflow
257
0
                ios::off_type offset = static_cast<ios::off_type>(new_offset);
258
0
                if (static_cast<uint64_t>(offset) != new_offset)
259
0
                    return static_cast<uint64_t>(-1);
260
261
0
                data->stream->seekg(offset, ios::beg);
262
0
                break;
263
0
            }
264
0
            case SEEK_CUR:
265
0
            {
266
                // Verify that value does not overflow
267
0
                ios::off_type offset = static_cast<ios::off_type>(off);
268
0
                if (static_cast<uint64_t>(offset) != off)
269
0
                    return static_cast<uint64_t>(-1);
270
271
0
                data->stream->seekg(offset, ios::cur);
272
0
                break;
273
0
            }
274
0
            case SEEK_END:
275
0
            {
276
                // Verify that value does not overflow
277
0
                ios::off_type offset = static_cast<ios::off_type>(off);
278
0
                if (static_cast<uint64_t>(offset) != off)
279
0
                    return static_cast<uint64_t>(-1);
280
281
0
                data->stream->seekg(offset, ios::end);
282
0
                break;
283
0
            }
284
0
        }
285
286
0
        ios::pos_type pos = data->stream->tellg();
287
0
        if (pos == ios::pos_type(-1))
288
0
            return static_cast<uint64_t>(-1);
289
290
0
        ios::off_type relative_pos = pos - data->start_pos;
291
0
        if (relative_pos < 0)
292
0
            return static_cast<uint64_t>(-1);
293
294
0
        return static_cast<uint64_t>(relative_pos);
295
0
    }
296
297
    static uint64_t _tiffosSizeProc(thandle_t fd)
298
0
    {
299
0
        tiffos_data *data = reinterpret_cast<tiffos_data *>(fd);
300
0
        ostream *os = data->stream;
301
0
        ios::pos_type pos = os->tellp();
302
0
        ios::pos_type len;
303
304
        // Failed stream positions must not be converted to unsigned sizes.
305
0
        if (pos == ios::pos_type(-1))
306
0
            return 0;
307
308
0
        os->seekp(0, ios::end);
309
0
        if (os->fail())
310
0
        {
311
0
            ios::iostate old_state = os->rdstate();
312
            // Temporarily clear failbit to restore the original position.
313
0
            os->clear(os->rdstate() & ~ios::failbit);
314
0
            os->seekp(pos);
315
0
            os->clear(old_state);
316
0
            return 0;
317
0
        }
318
0
        len = os->tellp();
319
0
        if (len == ios::pos_type(-1))
320
0
        {
321
0
            ios::iostate old_state = os->rdstate();
322
            // Temporarily clear failbit to restore the original position.
323
0
            os->clear(os->rdstate() & ~ios::failbit);
324
0
            os->seekp(pos);
325
0
            os->clear(old_state);
326
0
            return 0;
327
0
        }
328
0
        os->seekp(pos);
329
330
0
        return static_cast<uint64_t>(len);
331
0
    }
332
333
    static uint64_t _tiffisSizeProc(thandle_t fd)
334
0
    {
335
0
        tiffis_data *data = reinterpret_cast<tiffis_data *>(fd);
336
0
        ios::pos_type pos = data->stream->tellg();
337
0
        ios::pos_type len;
338
339
        // Failed stream positions must not be converted to unsigned sizes.
340
0
        if (pos == ios::pos_type(-1))
341
0
            return 0;
342
343
0
        data->stream->seekg(0, ios::end);
344
0
        if (data->stream->fail())
345
0
        {
346
0
            ios::iostate old_state = data->stream->rdstate();
347
            // Temporarily clear failbit to restore the original position.
348
0
            data->stream->clear(data->stream->rdstate() & ~ios::failbit);
349
0
            data->stream->seekg(pos);
350
0
            data->stream->clear(old_state);
351
0
            return 0;
352
0
        }
353
0
        len = data->stream->tellg();
354
0
        if (len == ios::pos_type(-1))
355
0
        {
356
0
            ios::iostate old_state = data->stream->rdstate();
357
            // Temporarily clear failbit to restore the original position.
358
0
            data->stream->clear(data->stream->rdstate() & ~ios::failbit);
359
0
            data->stream->seekg(pos);
360
0
            data->stream->clear(old_state);
361
0
            return 0;
362
0
        }
363
0
        data->stream->seekg(pos);
364
365
0
        return static_cast<uint64_t>(len);
366
0
    }
367
368
    static int _tiffosCloseProc(thandle_t fd)
369
0
    {
370
        // Our stream was not allocated by us, so it shouldn't be closed by us.
371
0
        delete reinterpret_cast<tiffos_data *>(fd);
372
0
        return 0;
373
0
    }
374
375
    static int _tiffisCloseProc(thandle_t fd)
376
0
    {
377
        // Our stream was not allocated by us, so it shouldn't be closed by us.
378
0
        delete reinterpret_cast<tiffis_data *>(fd);
379
0
        return 0;
380
0
    }
381
382
    static int _tiffDummyMapProcCxx(thandle_t, void **base, toff_t *size)
383
0
    {
384
0
        (void)base;
385
0
        (void)size;
386
0
        return (0);
387
0
    }
388
389
    static void _tiffDummyUnmapProcCxx(thandle_t, void *base, toff_t size)
390
0
    {
391
0
        (void)base;
392
0
        (void)size;
393
0
    }
394
395
    /*
396
     * Open a TIFF file descriptor for read/writing.
397
     */
398
    static TIFF *_tiffStreamOpen(const char *name, const char *mode, void *fd)
399
0
    {
400
0
        TIFF *tif;
401
402
0
        if (strchr(mode, 'w'))
403
0
        {
404
0
            tiffos_data *data = new tiffos_data;
405
0
            data->stream = reinterpret_cast<ostream *>(fd);
406
0
            data->start_pos = data->stream->tellp();
407
408
            // Open for writing.
409
0
            tif = TIFFClientOpen(
410
0
                name, mode, reinterpret_cast<thandle_t>(data), _tiffosReadProc,
411
0
                _tiffosWriteProc, _tiffosSeekProc, _tiffosCloseProc,
412
0
                _tiffosSizeProc, _tiffDummyMapProcCxx, _tiffDummyUnmapProcCxx);
413
0
            if (!tif)
414
0
            {
415
0
                delete data;
416
0
            }
417
0
        }
418
0
        else
419
0
        {
420
0
            tiffis_data *data = new tiffis_data;
421
0
            data->stream = reinterpret_cast<istream *>(fd);
422
0
            data->start_pos = data->stream->tellg();
423
            // Open for reading.
424
0
            tif = TIFFClientOpen(
425
0
                name, mode, reinterpret_cast<thandle_t>(data), _tiffisReadProc,
426
0
                _tiffisWriteProc, _tiffisSeekProc, _tiffisCloseProc,
427
0
                _tiffisSizeProc, _tiffDummyMapProcCxx, _tiffDummyUnmapProcCxx);
428
0
            if (!tif)
429
0
            {
430
0
                delete data;
431
0
            }
432
0
        }
433
434
0
        return (tif);
435
0
    }
436
437
} /* extern "C" */
438
439
TIFF *TIFFStreamOpen(const char *name, ostream *os)
440
0
{
441
    // If os is either a ostrstream or ostringstream, and has no data
442
    // written to it yet, then tellp() will return -1 which will break us.
443
    // We workaround this by writing out a dummy character and
444
    // then seek back to the beginning.
445
0
    if (!os->fail() && static_cast<int>(os->tellp()) < 0)
446
0
    {
447
0
        *os << '\0';
448
0
        os->seekp(0);
449
0
    }
450
451
    // NB: We don't support mapped files with streams so add 'm'
452
0
    return _tiffStreamOpen(name, "wm", os);
453
0
}
454
455
TIFF *TIFFStreamOpen(const char *name, istream *is)
456
0
{
457
    // NB: We don't support mapped files with streams so add 'm'
458
0
    return _tiffStreamOpen(name, "rm", is);
459
0
}