Coverage Report

Created: 2025-07-12 06:50

/src/libtiff/libtiff/tif_stream.cxx
Line
Count
Source (jump to first uncovered line)
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
785k
    {
110
785k
        tiffis_data *data = reinterpret_cast<tiffis_data *>(fd);
111
112
        // Verify that type does not overflow.
113
785k
        streamsize request_size = size;
114
785k
        if (static_cast<tmsize_t>(request_size) != size)
115
0
            return static_cast<tmsize_t>(-1);
116
117
785k
        data->stream->read((char *)buf, request_size);
118
119
785k
        return static_cast<tmsize_t>(data->stream->gcount());
120
785k
    }
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
849k
    {
243
849k
        tiffis_data *data = reinterpret_cast<tiffis_data *>(fd);
244
245
849k
        switch (whence)
246
849k
        {
247
849k
            case SEEK_SET:
248
849k
            {
249
                // Compute 64-bit offset
250
849k
                uint64_t new_offset =
251
849k
                    static_cast<uint64_t>(data->start_pos) + off;
252
253
                // Verify that value does not overflow
254
849k
                ios::off_type offset = static_cast<ios::off_type>(new_offset);
255
849k
                if (static_cast<uint64_t>(offset) != new_offset)
256
0
                    return static_cast<uint64_t>(-1);
257
258
849k
                data->stream->seekg(offset, ios::beg);
259
849k
                break;
260
849k
            }
261
0
            case SEEK_CUR:
262
0
            {
263
                // Verify that value does not overflow
264
0
                ios::off_type offset = static_cast<ios::off_type>(off);
265
0
                if (static_cast<uint64_t>(offset) != off)
266
0
                    return static_cast<uint64_t>(-1);
267
268
0
                data->stream->seekg(offset, ios::cur);
269
0
                break;
270
0
            }
271
0
            case SEEK_END:
272
0
            {
273
                // Verify that value does not overflow
274
0
                ios::off_type offset = static_cast<ios::off_type>(off);
275
0
                if (static_cast<uint64_t>(offset) != off)
276
0
                    return static_cast<uint64_t>(-1);
277
278
0
                data->stream->seekg(offset, ios::end);
279
0
                break;
280
0
            }
281
849k
        }
282
283
849k
        return (uint64_t)(data->stream->tellg() - data->start_pos);
284
849k
    }
285
286
    static uint64_t _tiffosSizeProc(thandle_t fd)
287
0
    {
288
0
        tiffos_data *data = reinterpret_cast<tiffos_data *>(fd);
289
0
        ostream *os = data->stream;
290
0
        ios::pos_type pos = os->tellp();
291
0
        ios::pos_type len;
292
293
0
        os->seekp(0, ios::end);
294
0
        len = os->tellp();
295
0
        os->seekp(pos);
296
297
0
        return (uint64_t)len;
298
0
    }
299
300
    static uint64_t _tiffisSizeProc(thandle_t fd)
301
4.80k
    {
302
4.80k
        tiffis_data *data = reinterpret_cast<tiffis_data *>(fd);
303
4.80k
        ios::pos_type pos = data->stream->tellg();
304
4.80k
        ios::pos_type len;
305
306
4.80k
        data->stream->seekg(0, ios::end);
307
4.80k
        len = data->stream->tellg();
308
4.80k
        data->stream->seekg(pos);
309
310
4.80k
        return (uint64_t)len;
311
4.80k
    }
312
313
    static int _tiffosCloseProc(thandle_t fd)
314
0
    {
315
        // Our stream was not allocated by us, so it shouldn't be closed by us.
316
0
        delete reinterpret_cast<tiffos_data *>(fd);
317
0
        return 0;
318
0
    }
319
320
    static int _tiffisCloseProc(thandle_t fd)
321
5.55k
    {
322
        // Our stream was not allocated by us, so it shouldn't be closed by us.
323
5.55k
        delete reinterpret_cast<tiffis_data *>(fd);
324
5.55k
        return 0;
325
5.55k
    }
326
327
    static int _tiffDummyMapProcCxx(thandle_t, void **base, toff_t *size)
328
0
    {
329
0
        (void)base;
330
0
        (void)size;
331
0
        return (0);
332
0
    }
333
334
    static void _tiffDummyUnmapProcCxx(thandle_t, void *base, toff_t size)
335
0
    {
336
0
        (void)base;
337
0
        (void)size;
338
0
    }
339
340
    /*
341
     * Open a TIFF file descriptor for read/writing.
342
     */
343
    static TIFF *_tiffStreamOpen(const char *name, const char *mode, void *fd)
344
8.06k
    {
345
8.06k
        TIFF *tif;
346
347
8.06k
        if (strchr(mode, 'w'))
348
0
        {
349
0
            tiffos_data *data = new tiffos_data;
350
0
            data->stream = reinterpret_cast<ostream *>(fd);
351
0
            data->start_pos = data->stream->tellp();
352
353
            // Open for writing.
354
0
            tif = TIFFClientOpen(
355
0
                name, mode, reinterpret_cast<thandle_t>(data), _tiffosReadProc,
356
0
                _tiffosWriteProc, _tiffosSeekProc, _tiffosCloseProc,
357
0
                _tiffosSizeProc, _tiffDummyMapProcCxx, _tiffDummyUnmapProcCxx);
358
0
            if (!tif)
359
0
            {
360
0
                delete data;
361
0
            }
362
0
        }
363
8.06k
        else
364
8.06k
        {
365
8.06k
            tiffis_data *data = new tiffis_data;
366
8.06k
            data->stream = reinterpret_cast<istream *>(fd);
367
8.06k
            data->start_pos = data->stream->tellg();
368
            // Open for reading.
369
8.06k
            tif = TIFFClientOpen(
370
8.06k
                name, mode, reinterpret_cast<thandle_t>(data), _tiffisReadProc,
371
8.06k
                _tiffisWriteProc, _tiffisSeekProc, _tiffisCloseProc,
372
8.06k
                _tiffisSizeProc, _tiffDummyMapProcCxx, _tiffDummyUnmapProcCxx);
373
8.06k
            if (!tif)
374
2.51k
            {
375
2.51k
                delete data;
376
2.51k
            }
377
8.06k
        }
378
379
8.06k
        return (tif);
380
8.06k
    }
381
382
} /* extern "C" */
383
384
TIFF *TIFFStreamOpen(const char *name, ostream *os)
385
0
{
386
    // If os is either a ostrstream or ostringstream, and has no data
387
    // written to it yet, then tellp() will return -1 which will break us.
388
    // We workaround this by writing out a dummy character and
389
    // then seek back to the beginning.
390
0
    if (!os->fail() && static_cast<int>(os->tellp()) < 0)
391
0
    {
392
0
        *os << '\0';
393
0
        os->seekp(0);
394
0
    }
395
396
    // NB: We don't support mapped files with streams so add 'm'
397
0
    return _tiffStreamOpen(name, "wm", os);
398
0
}
399
400
TIFF *TIFFStreamOpen(const char *name, istream *is)
401
8.06k
{
402
    // NB: We don't support mapped files with streams so add 'm'
403
8.06k
    return _tiffStreamOpen(name, "rm", is);
404
8.06k
}