Coverage Report

Created: 2025-08-28 06:57

/src/gdal/frmts/gtiff/tifvsi.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  GeoTIFF Driver
4
 * Purpose:  Implement system hook functions for libtiff on top of CPL/VSI,
5
 *           including > 2GB support.  Based on tif_unix.c from libtiff
6
 *           distribution.
7
 * Author:   Frank Warmerdam, warmerdam@pobox.com
8
 *
9
 ******************************************************************************
10
 * Copyright (c) 2005, Frank Warmerdam, warmerdam@pobox.com
11
 * Copyright (c) 2010-2012, Even Rouault <even dot rouault at spatialys.com>
12
 *
13
 * SPDX-License-Identifier: MIT
14
 ****************************************************************************/
15
16
// TIFF Library UNIX-specific Routines.
17
18
#include "cpl_port.h"
19
#include "tifvsi.h"
20
21
#include <assert.h>
22
#include <string.h>
23
#include <cerrno>
24
25
#include "cpl_conv.h"
26
#include "cpl_vsi.h"
27
#include "cpl_string.h"
28
29
// We avoid including xtiffio.h since it drags in the libgeotiff version
30
// of the VSI functions.
31
32
#ifdef RENAME_INTERNAL_LIBGEOTIFF_SYMBOLS
33
#include "gdal_libgeotiff_symbol_rename.h"
34
#endif
35
36
#include "xtiffio.h"
37
38
#include <limits>
39
40
#if (TIFFLIB_VERSION > 20220520) || defined(INTERNAL_LIBTIFF)  // > 4.4.0
41
#define SUPPORTS_LIBTIFF_OPEN_OPTIONS
42
43
extern int GTiffWarningHandlerExt(TIFF *tif, void *user_data,
44
                                  const char *module, const char *fmt,
45
                                  va_list ap);
46
extern int GTiffErrorHandlerExt(TIFF *tif, void *user_data, const char *module,
47
                                const char *fmt, va_list ap);
48
49
#endif
50
51
constexpr int BUFFER_SIZE = 65536;
52
53
struct GDALTiffHandle;
54
55
struct GDALTiffHandleShared
56
{
57
    VSILFILE *fpL;
58
    bool bReadOnly;
59
    bool bLazyStrileLoading;
60
    char *pszName;
61
    GDALTiffHandle *psActiveHandle;  // only used on the parent
62
    int nUserCounter;
63
    bool bAtEndOfFile;
64
    vsi_l_offset nFileLength;
65
};
66
67
struct GDALTiffHandle
68
{
69
    bool bFree;
70
71
    GDALTiffHandle *psParent;  // nullptr for the parent itself
72
    GDALTiffHandleShared *psShared;
73
74
    GByte *abyWriteBuffer;
75
    int nWriteBufferSize;
76
77
    // For pseudo-mmap'ed /vsimem/ file
78
    vsi_l_offset nDataLength;
79
    void *pBase;
80
81
    // If we pre-cached data (typically from /vsicurl/ )
82
    int nCachedRanges;
83
    void **ppCachedData;
84
    vsi_l_offset *panCachedOffsets;
85
    size_t *panCachedSizes;
86
};
87
88
static bool GTHFlushBuffer(thandle_t th);
89
90
static void SetActiveGTH(GDALTiffHandle *psGTH)
91
0
{
92
0
    auto psShared = psGTH->psShared;
93
0
    if (psShared->psActiveHandle != psGTH)
94
0
    {
95
0
        if (psShared->psActiveHandle != nullptr)
96
0
        {
97
0
            GTHFlushBuffer(static_cast<thandle_t>(psShared->psActiveHandle));
98
0
        }
99
0
        psShared->psActiveHandle = psGTH;
100
0
    }
101
0
}
102
103
void *VSI_TIFFGetCachedRange(thandle_t th, vsi_l_offset nOffset, size_t nSize)
104
0
{
105
0
    GDALTiffHandle *psGTH = reinterpret_cast<GDALTiffHandle *>(th);
106
0
    for (int i = 0; i < psGTH->nCachedRanges; i++)
107
0
    {
108
0
        if (nOffset >= psGTH->panCachedOffsets[i] &&
109
0
            nOffset + nSize <=
110
0
                psGTH->panCachedOffsets[i] + psGTH->panCachedSizes[i])
111
0
        {
112
0
            return static_cast<GByte *>(psGTH->ppCachedData[i]) +
113
0
                   (nOffset - psGTH->panCachedOffsets[i]);
114
0
        }
115
0
        if (nOffset < psGTH->panCachedOffsets[i])
116
0
            break;
117
0
    }
118
0
    return nullptr;
119
0
}
120
121
static tsize_t _tiffReadProc(thandle_t th, tdata_t buf, tsize_t size)
122
0
{
123
0
    GDALTiffHandle *psGTH = reinterpret_cast<GDALTiffHandle *>(th);
124
    // SetActiveGTH(psGTH);
125
126
0
    if (psGTH->nCachedRanges)
127
0
    {
128
0
        const vsi_l_offset nCurOffset = VSIFTellL(psGTH->psShared->fpL);
129
0
        void *data =
130
0
            VSI_TIFFGetCachedRange(th, nCurOffset, static_cast<size_t>(size));
131
0
        if (data)
132
0
        {
133
0
            memcpy(buf, data, size);
134
0
            VSIFSeekL(psGTH->psShared->fpL, nCurOffset + size, SEEK_SET);
135
0
            return size;
136
0
        }
137
0
    }
138
139
#ifdef DEBUG_VERBOSE_EXTRA
140
    CPLDebug("GTiff", "Reading %d bytes at offset " CPL_FRMT_GUIB,
141
             static_cast<int>(size), VSIFTellL(psGTH->psShared->fpL));
142
#endif
143
0
    return VSIFReadL(buf, 1, size, psGTH->psShared->fpL);
144
0
}
145
146
static bool GTHFlushBuffer(thandle_t th)
147
0
{
148
0
    GDALTiffHandle *psGTH = static_cast<GDALTiffHandle *>(th);
149
0
    bool bRet = true;
150
0
    if (psGTH->abyWriteBuffer && psGTH->nWriteBufferSize)
151
0
    {
152
0
        const tsize_t nRet =
153
0
            VSIFWriteL(psGTH->abyWriteBuffer, 1, psGTH->nWriteBufferSize,
154
0
                       psGTH->psShared->fpL);
155
0
        bRet = nRet == psGTH->nWriteBufferSize;
156
0
        if (!bRet)
157
0
        {
158
0
            TIFFErrorExt(th, "_tiffWriteProc", "%s", VSIStrerror(errno));
159
0
        }
160
0
        psGTH->nWriteBufferSize = 0;
161
0
    }
162
0
    return bRet;
163
0
}
164
165
static tsize_t _tiffWriteProc(thandle_t th, tdata_t buf, tsize_t size)
166
0
{
167
0
    GDALTiffHandle *psGTH = reinterpret_cast<GDALTiffHandle *>(th);
168
0
    SetActiveGTH(psGTH);
169
170
    // If we have a write buffer and are at end of file, then accumulate
171
    // the bytes until the buffer is full.
172
0
    if (psGTH->psShared->bAtEndOfFile && psGTH->abyWriteBuffer)
173
0
    {
174
0
        const GByte *pabyData = reinterpret_cast<GByte *>(buf);
175
0
        tsize_t nRemainingBytes = size;
176
0
        while (true)
177
0
        {
178
0
            if (psGTH->nWriteBufferSize + nRemainingBytes <= BUFFER_SIZE)
179
0
            {
180
0
                memcpy(psGTH->abyWriteBuffer + psGTH->nWriteBufferSize,
181
0
                       pabyData, nRemainingBytes);
182
0
                psGTH->nWriteBufferSize += static_cast<int>(nRemainingBytes);
183
0
                if (psGTH->psShared->bAtEndOfFile)
184
0
                {
185
0
                    psGTH->psShared->nFileLength += size;
186
0
                }
187
0
                return size;
188
0
            }
189
190
0
            int nAppendable = BUFFER_SIZE - psGTH->nWriteBufferSize;
191
0
            memcpy(psGTH->abyWriteBuffer + psGTH->nWriteBufferSize, pabyData,
192
0
                   nAppendable);
193
0
            const tsize_t nRet = VSIFWriteL(psGTH->abyWriteBuffer, 1,
194
0
                                            BUFFER_SIZE, psGTH->psShared->fpL);
195
0
            psGTH->nWriteBufferSize = 0;
196
0
            if (nRet != BUFFER_SIZE)
197
0
            {
198
0
                TIFFErrorExt(th, "_tiffWriteProc", "%s", VSIStrerror(errno));
199
0
                return 0;
200
0
            }
201
202
0
            pabyData += nAppendable;
203
0
            nRemainingBytes -= nAppendable;
204
0
        }
205
0
    }
206
207
0
    const tsize_t nRet = VSIFWriteL(buf, 1, size, psGTH->psShared->fpL);
208
0
    if (nRet < size)
209
0
    {
210
0
        TIFFErrorExt(th, "_tiffWriteProc", "%s", VSIStrerror(errno));
211
0
    }
212
213
0
    if (psGTH->psShared->bAtEndOfFile)
214
0
    {
215
0
        psGTH->psShared->nFileLength += nRet;
216
0
    }
217
0
    return nRet;
218
0
}
219
220
static toff_t _tiffSeekProc(thandle_t th, toff_t off, int whence)
221
0
{
222
0
    GDALTiffHandle *psGTH = reinterpret_cast<GDALTiffHandle *>(th);
223
0
    SetActiveGTH(psGTH);
224
225
    // Optimization: if we are already at end, then no need to
226
    // issue a VSIFSeekL().
227
0
    if (whence == SEEK_END)
228
0
    {
229
0
        if (psGTH->psShared->bAtEndOfFile)
230
0
        {
231
0
            return static_cast<toff_t>(psGTH->psShared->nFileLength);
232
0
        }
233
234
0
        if (VSIFSeekL(psGTH->psShared->fpL, off, whence) != 0)
235
0
        {
236
0
            TIFFErrorExt(th, "_tiffSeekProc", "%s", VSIStrerror(errno));
237
0
            return static_cast<toff_t>(-1);
238
0
        }
239
0
        psGTH->psShared->bAtEndOfFile = true;
240
0
        psGTH->psShared->nFileLength = VSIFTellL(psGTH->psShared->fpL);
241
0
        return static_cast<toff_t>(psGTH->psShared->nFileLength);
242
0
    }
243
244
0
    GTHFlushBuffer(th);
245
0
    psGTH->psShared->bAtEndOfFile = false;
246
0
    psGTH->psShared->nFileLength = 0;
247
248
0
    if (VSIFSeekL(psGTH->psShared->fpL, off, whence) == 0)
249
0
    {
250
0
        return static_cast<toff_t>(VSIFTellL(psGTH->psShared->fpL));
251
0
    }
252
0
    else
253
0
    {
254
0
        TIFFErrorExt(th, "_tiffSeekProc", "%s", VSIStrerror(errno));
255
0
        return static_cast<toff_t>(-1);
256
0
    }
257
0
}
258
259
static void FreeGTH(GDALTiffHandle *psGTH)
260
0
{
261
0
    psGTH->psShared->nUserCounter--;
262
0
    if (psGTH->psParent == nullptr)
263
0
    {
264
0
        assert(psGTH->psShared->nUserCounter == 0);
265
0
        CPLFree(psGTH->psShared->pszName);
266
0
        CPLFree(psGTH->psShared);
267
0
    }
268
0
    else
269
0
    {
270
0
        if (psGTH->psShared->psActiveHandle == psGTH)
271
0
            psGTH->psShared->psActiveHandle = nullptr;
272
0
    }
273
0
    CPLFree(psGTH->abyWriteBuffer);
274
0
    CPLFree(psGTH->ppCachedData);
275
0
    CPLFree(psGTH->panCachedOffsets);
276
0
    CPLFree(psGTH->panCachedSizes);
277
0
    CPLFree(psGTH);
278
0
}
279
280
static int _tiffCloseProc(thandle_t th)
281
0
{
282
0
    GDALTiffHandle *psGTH = reinterpret_cast<GDALTiffHandle *>(th);
283
0
    SetActiveGTH(psGTH);
284
0
    GTHFlushBuffer(th);
285
0
    if (psGTH->bFree)
286
0
        FreeGTH(psGTH);
287
0
    return 0;
288
0
}
289
290
static toff_t _tiffSizeProc(thandle_t th)
291
0
{
292
0
    GDALTiffHandle *psGTH = reinterpret_cast<GDALTiffHandle *>(th);
293
0
    SetActiveGTH(psGTH);
294
295
0
    if (psGTH->psShared->bAtEndOfFile)
296
0
    {
297
0
        return static_cast<toff_t>(psGTH->psShared->nFileLength);
298
0
    }
299
300
0
    const vsi_l_offset old_off = VSIFTellL(psGTH->psShared->fpL);
301
0
    CPL_IGNORE_RET_VAL(VSIFSeekL(psGTH->psShared->fpL, 0, SEEK_END));
302
303
0
    const toff_t file_size =
304
0
        static_cast<toff_t>(VSIFTellL(psGTH->psShared->fpL));
305
0
    CPL_IGNORE_RET_VAL(VSIFSeekL(psGTH->psShared->fpL, old_off, SEEK_SET));
306
307
0
    return file_size;
308
0
}
309
310
static int _tiffMapProc(thandle_t th, tdata_t *pbase, toff_t *psize)
311
0
{
312
0
    GDALTiffHandle *psGTH = reinterpret_cast<GDALTiffHandle *>(th);
313
    // SetActiveGTH(psGTH);
314
315
0
    if (psGTH->pBase)
316
0
    {
317
0
        *pbase = psGTH->pBase;
318
0
        *psize = static_cast<toff_t>(psGTH->nDataLength);
319
0
        return 1;
320
0
    }
321
0
    return 0;
322
0
}
323
324
static void _tiffUnmapProc(thandle_t /* th */, tdata_t /* base */,
325
                           toff_t /* size */)
326
0
{
327
0
}
328
329
VSILFILE *VSI_TIFFGetVSILFile(thandle_t th)
330
0
{
331
0
    GDALTiffHandle *psGTH = reinterpret_cast<GDALTiffHandle *>(th);
332
0
    SetActiveGTH(psGTH);
333
0
    VSI_TIFFFlushBufferedWrite(th);
334
0
    return psGTH->psShared->fpL;
335
0
}
336
337
int VSI_TIFFFlushBufferedWrite(thandle_t th)
338
0
{
339
0
    GDALTiffHandle *psGTH = reinterpret_cast<GDALTiffHandle *>(th);
340
0
    SetActiveGTH(psGTH);
341
0
    psGTH->psShared->bAtEndOfFile = false;
342
0
    return GTHFlushBuffer(th);
343
0
}
344
345
int VSI_TIFFHasCachedRanges(thandle_t th)
346
0
{
347
0
    GDALTiffHandle *psGTH = reinterpret_cast<GDALTiffHandle *>(th);
348
0
    return psGTH->nCachedRanges != 0;
349
0
}
350
351
toff_t VSI_TIFFSeek(TIFF *tif, toff_t off, int whence)
352
0
{
353
0
    thandle_t th = TIFFClientdata(tif);
354
0
    return _tiffSeekProc(th, off, whence);
355
0
}
356
357
int VSI_TIFFWrite(TIFF *tif, const void *buffer, size_t buffersize)
358
0
{
359
0
    thandle_t th = TIFFClientdata(tif);
360
0
    return static_cast<size_t>(_tiffWriteProc(th, const_cast<tdata_t>(buffer),
361
0
                                              buffersize)) == buffersize;
362
0
}
363
364
void VSI_TIFFSetCachedRanges(thandle_t th, int nRanges, void **ppData,
365
                             const vsi_l_offset *panOffsets,
366
                             const size_t *panSizes)
367
0
{
368
0
    GDALTiffHandle *psGTH = reinterpret_cast<GDALTiffHandle *>(th);
369
0
    psGTH->nCachedRanges = nRanges;
370
0
    if (nRanges)
371
0
    {
372
0
        psGTH->ppCachedData = static_cast<void **>(
373
0
            CPLRealloc(psGTH->ppCachedData, nRanges * sizeof(void *)));
374
0
        memcpy(psGTH->ppCachedData, ppData, nRanges * sizeof(void *));
375
376
0
        psGTH->panCachedOffsets = static_cast<vsi_l_offset *>(CPLRealloc(
377
0
            psGTH->panCachedOffsets, nRanges * sizeof(vsi_l_offset)));
378
0
        memcpy(psGTH->panCachedOffsets, panOffsets,
379
0
               nRanges * sizeof(vsi_l_offset));
380
381
0
        psGTH->panCachedSizes = static_cast<size_t *>(
382
0
            CPLRealloc(psGTH->panCachedSizes, nRanges * sizeof(size_t)));
383
0
        memcpy(psGTH->panCachedSizes, panSizes, nRanges * sizeof(size_t));
384
0
    }
385
0
}
386
387
static bool IsReadOnly(const char *mode)
388
0
{
389
0
    bool bReadOnly = true;
390
0
    for (int i = 0; mode[i] != '\0'; i++)
391
0
    {
392
0
        if (mode[i] == 'w' || mode[i] == '+' || mode[i] == 'a')
393
0
        {
394
0
            bReadOnly = false;
395
0
        }
396
0
    }
397
0
    return bReadOnly;
398
0
}
399
400
static void InitializeWriteBuffer(GDALTiffHandle *psGTH, const char *pszMode)
401
0
{
402
    // No need to buffer on /vsimem/
403
0
    const bool bReadOnly = IsReadOnly(pszMode);
404
0
    bool bAllocBuffer = !bReadOnly;
405
0
    if (STARTS_WITH(psGTH->psShared->pszName, "/vsimem/"))
406
0
    {
407
0
        if (bReadOnly &&
408
0
            CPLTestBool(CPLGetConfigOption("GTIFF_USE_MMAP", "NO")))
409
0
        {
410
0
            psGTH->nDataLength = 0;
411
0
            psGTH->pBase = VSIGetMemFileBuffer(psGTH->psShared->pszName,
412
0
                                               &psGTH->nDataLength, FALSE);
413
0
        }
414
0
        bAllocBuffer = false;
415
0
    }
416
417
0
    psGTH->abyWriteBuffer =
418
0
        bAllocBuffer ? static_cast<GByte *>(VSIMalloc(BUFFER_SIZE)) : nullptr;
419
0
    psGTH->nWriteBufferSize = 0;
420
0
}
421
422
#ifdef SUPPORTS_LIBTIFF_OPEN_OPTIONS
423
static void VSI_TIFFSetOpenOptions(TIFFOpenOptions *opts)
424
0
{
425
0
    TIFFOpenOptionsSetErrorHandlerExtR(opts, GTiffErrorHandlerExt, nullptr);
426
0
    TIFFOpenOptionsSetWarningHandlerExtR(opts, GTiffWarningHandlerExt, nullptr);
427
0
#if defined(INTERNAL_LIBTIFF) || TIFFLIB_VERSION > 20230908
428
    // Read-once and stored in static storage otherwise affects
429
    // autotest/benchmark/test_gtiff.py::test_gtiff_byte
430
0
    static const GIntBig nMemLimit = []() -> GIntBig
431
0
    {
432
0
        if (const char *pszLimit =
433
0
                CPLGetConfigOption("GTIFF_MAX_CUMULATED_MEM_USAGE", nullptr))
434
0
            return CPLAtoGIntBig(pszLimit);
435
0
        else
436
0
        {
437
0
            const auto nUsableRAM = CPLGetUsablePhysicalRAM();
438
0
            if (nUsableRAM > 0)
439
0
            {
440
                // coverity[return_overflow]
441
0
                return nUsableRAM / 10 * 9;
442
0
            }
443
0
            else
444
0
                return 0;
445
0
        }
446
0
    }();
447
0
    if (nMemLimit > 0 && nMemLimit < std::numeric_limits<tmsize_t>::max())
448
0
    {
449
        //CPLDebug("GTiff", "TIFFOpenOptionsSetMaxCumulatedMemAlloc(%" PRIu64 ")",
450
        //         static_cast<uint64_t>(nMemLimit));
451
0
        TIFFOpenOptionsSetMaxCumulatedMemAlloc(
452
0
            opts, static_cast<tmsize_t>(nMemLimit));
453
0
    }
454
0
#endif
455
0
}
456
#endif
457
458
static TIFF *VSI_TIFFOpen_common(GDALTiffHandle *psGTH, const char *pszMode)
459
0
{
460
0
    InitializeWriteBuffer(psGTH, pszMode);
461
462
0
#ifdef SUPPORTS_LIBTIFF_OPEN_OPTIONS
463
0
    XTIFFInitialize();
464
0
    TIFFOpenOptions *opts = TIFFOpenOptionsAlloc();
465
0
    if (opts == nullptr)
466
0
    {
467
0
        FreeGTH(psGTH);
468
0
        return nullptr;
469
0
    }
470
0
    VSI_TIFFSetOpenOptions(opts);
471
0
    TIFF *tif = TIFFClientOpenExt(
472
0
        psGTH->psShared->pszName, pszMode, reinterpret_cast<thandle_t>(psGTH),
473
0
        _tiffReadProc, _tiffWriteProc, _tiffSeekProc, _tiffCloseProc,
474
0
        _tiffSizeProc, _tiffMapProc, _tiffUnmapProc, opts);
475
0
    TIFFOpenOptionsFree(opts);
476
#else
477
    TIFF *tif = XTIFFClientOpen(
478
        psGTH->psShared->pszName, pszMode, reinterpret_cast<thandle_t>(psGTH),
479
        _tiffReadProc, _tiffWriteProc, _tiffSeekProc, _tiffCloseProc,
480
        _tiffSizeProc, _tiffMapProc, _tiffUnmapProc);
481
#endif
482
0
    if (tif == nullptr)
483
0
        FreeGTH(psGTH);
484
485
0
    return tif;
486
0
}
487
488
// Open a TIFF file for read/writing.
489
TIFF *VSI_TIFFOpen(const char *name, const char *mode, VSILFILE *fpL)
490
0
{
491
492
0
    if (VSIFSeekL(fpL, 0, SEEK_SET) < 0)
493
0
        return nullptr;
494
495
0
    GDALTiffHandle *psGTH =
496
0
        static_cast<GDALTiffHandle *>(CPLCalloc(1, sizeof(GDALTiffHandle)));
497
0
    psGTH->bFree = true;
498
0
    psGTH->psParent = nullptr;
499
0
    psGTH->psShared = static_cast<GDALTiffHandleShared *>(
500
0
        CPLCalloc(1, sizeof(GDALTiffHandleShared)));
501
0
    psGTH->psShared->bReadOnly = (strchr(mode, '+') == nullptr);
502
0
    psGTH->psShared->bLazyStrileLoading = (strchr(mode, 'D') != nullptr);
503
0
    psGTH->psShared->pszName = CPLStrdup(name);
504
0
    psGTH->psShared->fpL = fpL;
505
0
    psGTH->psShared->psActiveHandle = psGTH;
506
0
    psGTH->psShared->nFileLength = 0;
507
0
    psGTH->psShared->bAtEndOfFile = false;
508
0
    psGTH->psShared->nUserCounter = 1;
509
510
0
    return VSI_TIFFOpen_common(psGTH, mode);
511
0
}
512
513
TIFF *VSI_TIFFOpenChild(TIFF *parent)
514
0
{
515
0
    GDALTiffHandle *psGTHParent =
516
0
        reinterpret_cast<GDALTiffHandle *>(TIFFClientdata(parent));
517
518
0
    GDALTiffHandle *psGTH =
519
0
        static_cast<GDALTiffHandle *>(CPLCalloc(1, sizeof(GDALTiffHandle)));
520
0
    psGTH->bFree = true;
521
0
    psGTH->psParent = psGTHParent;
522
0
    psGTH->psShared = psGTHParent->psShared;
523
0
    psGTH->psShared->nUserCounter++;
524
525
0
    SetActiveGTH(psGTH);
526
0
    VSIFSeekL(psGTH->psShared->fpL, 0, SEEK_SET);
527
0
    psGTH->psShared->bAtEndOfFile = false;
528
529
0
    const char *mode =
530
0
        psGTH->psShared->bReadOnly && psGTH->psShared->bLazyStrileLoading
531
0
            ? "rDO"
532
0
        : psGTH->psShared->bReadOnly          ? "r"
533
0
        : psGTH->psShared->bLazyStrileLoading ? "r+D"
534
0
                                              : "r+";
535
0
    return VSI_TIFFOpen_common(psGTH, mode);
536
0
}
537
538
// Re-open a TIFF handle (seeking to the appropriate directory is then needed)
539
TIFF *VSI_TIFFReOpen(TIFF *tif)
540
0
{
541
0
    thandle_t th = TIFFClientdata(tif);
542
0
    GDALTiffHandle *psGTH = reinterpret_cast<GDALTiffHandle *>(th);
543
544
    // Disable freeing of psGTH in _tiffCloseProc(), which could be called
545
    // if XTIFFClientOpen() fails, or obviously by XTIFFClose()
546
0
    psGTH->bFree = false;
547
548
0
    const char *mode =
549
0
        psGTH->psShared->bReadOnly && psGTH->psShared->bLazyStrileLoading
550
0
            ? "rDO"
551
0
        : psGTH->psShared->bReadOnly          ? "r"
552
0
        : psGTH->psShared->bLazyStrileLoading ? "r+D"
553
0
                                              : "r+";
554
555
0
    SetActiveGTH(psGTH);
556
0
    VSIFSeekL(psGTH->psShared->fpL, 0, SEEK_SET);
557
0
    psGTH->psShared->bAtEndOfFile = false;
558
559
0
#ifdef SUPPORTS_LIBTIFF_OPEN_OPTIONS
560
0
    TIFF *newHandle = nullptr;
561
0
    TIFFOpenOptions *opts = TIFFOpenOptionsAlloc();
562
0
    if (opts != nullptr)
563
0
    {
564
0
        VSI_TIFFSetOpenOptions(opts);
565
0
        newHandle = TIFFClientOpenExt(
566
0
            psGTH->psShared->pszName, mode, reinterpret_cast<thandle_t>(psGTH),
567
0
            _tiffReadProc, _tiffWriteProc, _tiffSeekProc, _tiffCloseProc,
568
0
            _tiffSizeProc, _tiffMapProc, _tiffUnmapProc, opts);
569
0
        TIFFOpenOptionsFree(opts);
570
0
    }
571
#else
572
    TIFF *newHandle = XTIFFClientOpen(
573
        psGTH->psShared->pszName, mode, reinterpret_cast<thandle_t>(psGTH),
574
        _tiffReadProc, _tiffWriteProc, _tiffSeekProc, _tiffCloseProc,
575
        _tiffSizeProc, _tiffMapProc, _tiffUnmapProc);
576
#endif
577
0
    if (newHandle != nullptr)
578
0
        XTIFFClose(tif);
579
580
0
    psGTH->bFree = true;
581
582
0
    return newHandle;
583
0
}