Coverage Report

Created: 2025-06-13 06:18

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