Coverage Report

Created: 2025-06-13 06:29

/src/gdal/port/cpl_vsil_subfile.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  VSI Virtual File System
4
 * Purpose:  Implementation of subfile virtual IO functions.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
9
 * Copyright (c) 2009-2014, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_port.h"
15
#include "cpl_vsi.h"
16
17
#include <cerrno>
18
#include <cstddef>
19
#include <cstring>
20
#if HAVE_FCNTL_H
21
#include <fcntl.h>
22
#endif
23
#include <limits>
24
25
#include "cpl_conv.h"
26
#include "cpl_multiproc.h"
27
#include "cpl_string.h"
28
#include "cpl_vsi_virtual.h"
29
30
/************************************************************************/
31
/* ==================================================================== */
32
/*                           VSISubFileHandle                           */
33
/* ==================================================================== */
34
/************************************************************************/
35
36
class VSISubFileHandle final : public VSIVirtualHandle
37
{
38
    CPL_DISALLOW_COPY_ASSIGN(VSISubFileHandle)
39
40
  public:
41
    VSILFILE *fp = nullptr;
42
    vsi_l_offset nSubregionOffset = 0;
43
    vsi_l_offset nSubregionSize = 0;
44
    bool bAtEOF = false;
45
    bool bError = false;
46
47
312
    VSISubFileHandle() = default;
48
    ~VSISubFileHandle() override;
49
50
    int Seek(vsi_l_offset nOffset, int nWhence) override;
51
    vsi_l_offset Tell() override;
52
    size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
53
    size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
54
    void ClearErr() override;
55
    int Eof() override;
56
    int Error() override;
57
    int Close() override;
58
};
59
60
/************************************************************************/
61
/* ==================================================================== */
62
/*                   VSISubFileFilesystemHandler                        */
63
/* ==================================================================== */
64
/************************************************************************/
65
66
class VSISubFileFilesystemHandler final : public VSIFilesystemHandler
67
{
68
    CPL_DISALLOW_COPY_ASSIGN(VSISubFileFilesystemHandler)
69
70
  public:
71
1
    VSISubFileFilesystemHandler() = default;
72
    ~VSISubFileFilesystemHandler() override = default;
73
74
    static int DecomposePath(const char *pszPath, CPLString &osFilename,
75
                             vsi_l_offset &nSubFileOffset,
76
                             vsi_l_offset &nSubFileSize);
77
78
    VSIVirtualHandle *Open(const char *pszFilename, const char *pszAccess,
79
                           bool bSetError,
80
                           CSLConstList /* papszOptions */) override;
81
    int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
82
             int nFlags) override;
83
    int Unlink(const char *pszFilename) override;
84
    int Mkdir(const char *pszDirname, long nMode) override;
85
    int Rmdir(const char *pszDirname) override;
86
    char **ReadDirEx(const char *pszDirname, int nMaxFiles) override;
87
};
88
89
/************************************************************************/
90
/* ==================================================================== */
91
/*                             VSISubFileHandle                         */
92
/* ==================================================================== */
93
/************************************************************************/
94
95
VSISubFileHandle::~VSISubFileHandle()
96
312
{
97
312
    VSISubFileHandle::Close();
98
312
}
99
100
/************************************************************************/
101
/*                               Close()                                */
102
/************************************************************************/
103
104
int VSISubFileHandle::Close()
105
106
624
{
107
624
    if (fp == nullptr)
108
312
        return -1;
109
312
    int nRet = VSIFCloseL(fp);
110
312
    fp = nullptr;
111
112
312
    return nRet;
113
624
}
114
115
/************************************************************************/
116
/*                                Seek()                                */
117
/************************************************************************/
118
119
int VSISubFileHandle::Seek(vsi_l_offset nOffset, int nWhence)
120
121
959
{
122
959
    bAtEOF = false;
123
124
959
    if (nWhence == SEEK_SET)
125
569
    {
126
569
        if (nOffset >
127
569
            std::numeric_limits<vsi_l_offset>::max() - nSubregionOffset)
128
0
            return -1;
129
569
        nOffset += nSubregionOffset;
130
569
    }
131
390
    else if (nWhence == SEEK_CUR)
132
0
    {
133
        // handle normally.
134
0
    }
135
390
    else if (nWhence == SEEK_END)
136
390
    {
137
390
        if (nSubregionSize != 0)
138
0
        {
139
0
            nOffset = nSubregionOffset + nSubregionSize;
140
0
            nWhence = SEEK_SET;
141
0
        }
142
390
    }
143
0
    else
144
0
    {
145
0
        errno = EINVAL;
146
0
        return -1;
147
0
    }
148
149
959
    return VSIFSeekL(fp, nOffset, nWhence);
150
959
}
151
152
/************************************************************************/
153
/*                                Tell()                                */
154
/************************************************************************/
155
156
vsi_l_offset VSISubFileHandle::Tell()
157
158
390
{
159
390
    vsi_l_offset nBasePos = VSIFTellL(fp);
160
390
    if (nBasePos >= nSubregionOffset)
161
390
        return nBasePos - nSubregionOffset;
162
0
    return 0;
163
390
}
164
165
/************************************************************************/
166
/*                                Read()                                */
167
/************************************************************************/
168
169
size_t VSISubFileHandle::Read(void *pBuffer, size_t nSize, size_t nCount)
170
171
274
{
172
274
    size_t nRet = 0;
173
274
    if (nSubregionSize == 0)
174
274
    {
175
274
        nRet = VSIFReadL(pBuffer, nSize, nCount, fp);
176
274
    }
177
0
    else
178
0
    {
179
0
        if (nSize == 0)
180
0
            return 0;
181
182
0
        const vsi_l_offset nCurOffset = VSIFTellL(fp);
183
0
        if (nCurOffset >= nSubregionOffset + nSubregionSize)
184
0
        {
185
0
            bAtEOF = true;
186
0
            return 0;
187
0
        }
188
189
0
        const size_t nByteToRead = nSize * nCount;
190
0
        if (nCurOffset + nByteToRead > nSubregionOffset + nSubregionSize)
191
0
        {
192
0
            const int nRead = static_cast<int>(
193
0
                VSIFReadL(pBuffer, 1,
194
0
                          static_cast<size_t>(nSubregionOffset +
195
0
                                              nSubregionSize - nCurOffset),
196
0
                          fp));
197
0
            nRet = nRead / nSize;
198
0
        }
199
0
        else
200
0
        {
201
0
            nRet = VSIFReadL(pBuffer, nSize, nCount, fp);
202
0
        }
203
0
    }
204
205
274
    if (nRet < nCount)
206
274
    {
207
274
        if (fp->Eof())
208
274
            bAtEOF = true;
209
0
        else /* if (fp->Error()) */
210
0
            bError = true;
211
274
    }
212
213
274
    return nRet;
214
274
}
215
216
/************************************************************************/
217
/*                               Write()                                */
218
/************************************************************************/
219
220
size_t VSISubFileHandle::Write(const void *pBuffer, size_t nSize, size_t nCount)
221
222
0
{
223
0
    bAtEOF = false;
224
225
0
    if (nSubregionSize == 0)
226
0
        return VSIFWriteL(pBuffer, nSize, nCount, fp);
227
228
0
    if (nSize == 0)
229
0
        return 0;
230
231
0
    const vsi_l_offset nCurOffset = VSIFTellL(fp);
232
0
    if (nCurOffset >= nSubregionOffset + nSubregionSize)
233
0
        return 0;
234
235
0
    const size_t nByteToWrite = nSize * nCount;
236
0
    if (nCurOffset + nByteToWrite > nSubregionOffset + nSubregionSize)
237
0
    {
238
0
        const int nWritten = static_cast<int>(VSIFWriteL(
239
0
            pBuffer, 1,
240
0
            static_cast<size_t>(nSubregionOffset + nSubregionSize - nCurOffset),
241
0
            fp));
242
0
        return nWritten / nSize;
243
0
    }
244
245
0
    return VSIFWriteL(pBuffer, nSize, nCount, fp);
246
0
}
247
248
/************************************************************************/
249
/*                             ClearErr()                               */
250
/************************************************************************/
251
252
void VSISubFileHandle::ClearErr()
253
254
0
{
255
0
    fp->ClearErr();
256
0
    bAtEOF = false;
257
0
    bError = false;
258
0
}
259
260
/************************************************************************/
261
/*                              Error()                                 */
262
/************************************************************************/
263
264
int VSISubFileHandle::Error()
265
266
0
{
267
0
    return bError;
268
0
}
269
270
/************************************************************************/
271
/*                                Eof()                                 */
272
/************************************************************************/
273
274
int VSISubFileHandle::Eof()
275
276
184
{
277
184
    return bAtEOF;
278
184
}
279
280
/************************************************************************/
281
/* ==================================================================== */
282
/*                       VSISubFileFilesystemHandler                    */
283
/* ==================================================================== */
284
/************************************************************************/
285
286
/************************************************************************/
287
/*                           DecomposePath()                            */
288
/*                                                                      */
289
/*      Parse a path like /vsisubfile/1000_2000,data/abc.tif into an    */
290
/*      offset (1000), a size (2000) and a path (data/abc.tif).         */
291
/************************************************************************/
292
293
int VSISubFileFilesystemHandler::DecomposePath(const char *pszPath,
294
                                               CPLString &osFilename,
295
                                               vsi_l_offset &nSubFileOffset,
296
                                               vsi_l_offset &nSubFileSize)
297
298
2.58k
{
299
2.58k
    if (!STARTS_WITH(pszPath, "/vsisubfile/"))
300
0
        return FALSE;
301
302
2.58k
    osFilename = "";
303
2.58k
    nSubFileOffset = 0;
304
2.58k
    nSubFileSize = 0;
305
306
2.58k
    nSubFileOffset =
307
2.58k
        CPLScanUIntBig(pszPath + 12, static_cast<int>(strlen(pszPath + 12)));
308
158k
    for (int i = 12; pszPath[i] != '\0'; i++)
309
158k
    {
310
158k
        if (pszPath[i] == '_' && nSubFileSize == 0)
311
3.59k
        {
312
            // -1 is sometimes passed to mean that we don't know the file size
313
            // for example when creating a JPEG2000 datastream in a NITF file
314
            // Transform it into 0 for correct behavior of Read(), Write() and
315
            // Eof().
316
3.59k
            if (pszPath[i + 1] == '-')
317
382
                nSubFileSize = 0;
318
3.21k
            else
319
3.21k
                nSubFileSize = CPLScanUIntBig(
320
3.21k
                    pszPath + i + 1, static_cast<int>(strlen(pszPath + i + 1)));
321
3.59k
        }
322
154k
        else if (pszPath[i] == ',')
323
2.02k
        {
324
2.02k
            osFilename = pszPath + i + 1;
325
2.02k
            return TRUE;
326
2.02k
        }
327
152k
        else if (pszPath[i] == '/')
328
222
        {
329
            // Missing comma!
330
222
            return FALSE;
331
222
        }
332
158k
    }
333
334
339
    return FALSE;
335
2.58k
}
336
337
/************************************************************************/
338
/*                                Open()                                */
339
/************************************************************************/
340
341
VSIVirtualHandle *
342
VSISubFileFilesystemHandler::Open(const char *pszFilename,
343
                                  const char *pszAccess, bool /* bSetError */,
344
                                  CSLConstList /* papszOptions */)
345
346
1.38k
{
347
1.38k
    if (!STARTS_WITH_CI(pszFilename, "/vsisubfile/"))
348
1
        return nullptr;
349
350
1.37k
    CPLString osSubFilePath;
351
1.37k
    vsi_l_offset nOff = 0;
352
1.37k
    vsi_l_offset nSize = 0;
353
354
1.37k
    if (!DecomposePath(pszFilename, osSubFilePath, nOff, nSize))
355
336
    {
356
336
        errno = ENOENT;
357
336
        return nullptr;
358
336
    }
359
1.04k
    if (nOff > std::numeric_limits<vsi_l_offset>::max() - nSize)
360
136
    {
361
136
        return nullptr;
362
136
    }
363
364
    /* -------------------------------------------------------------------- */
365
    /*      We can't open the containing file with "w" access, so if        */
366
    /*      that is requested use "r+" instead to update in place.          */
367
    /* -------------------------------------------------------------------- */
368
907
    if (pszAccess[0] == 'w')
369
0
        pszAccess = "r+";
370
371
    /* -------------------------------------------------------------------- */
372
    /*      Open the underlying file.                                       */
373
    /* -------------------------------------------------------------------- */
374
907
    VSILFILE *fp = VSIFOpenL(osSubFilePath, pszAccess);
375
376
907
    if (fp == nullptr)
377
595
        return nullptr;
378
379
    /* -------------------------------------------------------------------- */
380
    /*      Setup the file handle on this file.                             */
381
    /* -------------------------------------------------------------------- */
382
312
    VSISubFileHandle *poHandle = new VSISubFileHandle;
383
384
312
    poHandle->fp = fp;
385
312
    poHandle->nSubregionOffset = nOff;
386
312
    poHandle->nSubregionSize = nSize;
387
388
    // In read-only mode validate (offset, size) against underlying file size
389
312
    if (strchr(pszAccess, 'r') != nullptr && strchr(pszAccess, '+') == nullptr)
390
312
    {
391
312
        if (VSIFSeekL(fp, 0, SEEK_END) != 0)
392
1
        {
393
1
            poHandle->Close();
394
1
            delete poHandle;
395
1
            return nullptr;
396
1
        }
397
311
        vsi_l_offset nFpSize = VSIFTellL(fp);
398
        // For a directory, the size will be max(vsi_l_offset) / 2
399
311
        if (nFpSize == ~(static_cast<vsi_l_offset>(0)) / 2 || nOff > nFpSize)
400
83
        {
401
83
            poHandle->Close();
402
83
            delete poHandle;
403
83
            return nullptr;
404
83
        }
405
228
        if (nOff + nSize > nFpSize)
406
142
        {
407
142
            nSize = nFpSize - nOff;
408
142
            poHandle->nSubregionSize = nSize;
409
142
        }
410
228
    }
411
412
228
    if (VSIFSeekL(fp, nOff, SEEK_SET) != 0)
413
0
    {
414
0
        poHandle->Close();
415
0
        delete poHandle;
416
0
        poHandle = nullptr;
417
0
    }
418
419
228
    return poHandle;
420
312
}
421
422
/************************************************************************/
423
/*                                Stat()                                */
424
/************************************************************************/
425
426
int VSISubFileFilesystemHandler::Stat(const char *pszFilename,
427
                                      VSIStatBufL *psStatBuf, int nFlags)
428
429
1.26k
{
430
1.26k
    if (!STARTS_WITH_CI(pszFilename, "/vsisubfile/"))
431
57
        return -1;
432
433
1.20k
    CPLString osSubFilePath;
434
1.20k
    vsi_l_offset nOff = 0;
435
1.20k
    vsi_l_offset nSize = 0;
436
437
1.20k
    memset(psStatBuf, 0, sizeof(VSIStatBufL));
438
439
1.20k
    if (!DecomposePath(pszFilename, osSubFilePath, nOff, nSize))
440
225
    {
441
225
        errno = ENOENT;
442
225
        return -1;
443
225
    }
444
445
979
    const int nResult = VSIStatExL(osSubFilePath, psStatBuf, nFlags);
446
447
979
    if (nResult == 0)
448
350
    {
449
350
        if (nSize != 0)
450
236
            psStatBuf->st_size = nSize;
451
114
        else if (static_cast<vsi_l_offset>(psStatBuf->st_size) >= nOff)
452
89
            psStatBuf->st_size -= nOff;
453
25
        else
454
25
            psStatBuf->st_size = 0;
455
350
    }
456
457
979
    return nResult;
458
1.20k
}
459
460
/************************************************************************/
461
/*                               Unlink()                               */
462
/************************************************************************/
463
464
int VSISubFileFilesystemHandler::Unlink(const char * /* pszFilename */)
465
0
{
466
0
    errno = EACCES;
467
0
    return -1;
468
0
}
469
470
/************************************************************************/
471
/*                               Mkdir()                                */
472
/************************************************************************/
473
474
int VSISubFileFilesystemHandler::Mkdir(const char * /* pszPathname */,
475
                                       long /* nMode */)
476
0
{
477
0
    errno = EACCES;
478
0
    return -1;
479
0
}
480
481
/************************************************************************/
482
/*                               Rmdir()                                */
483
/************************************************************************/
484
485
int VSISubFileFilesystemHandler::Rmdir(const char * /* pszPathname */)
486
487
0
{
488
0
    errno = EACCES;
489
0
    return -1;
490
0
}
491
492
/************************************************************************/
493
/*                             ReadDirEx()                              */
494
/************************************************************************/
495
496
char **VSISubFileFilesystemHandler::ReadDirEx(const char * /* pszPath */,
497
                                              int /* nMaxFiles */)
498
0
{
499
0
    errno = EACCES;
500
0
    return nullptr;
501
0
}
502
503
/************************************************************************/
504
/*                 VSIInstallSubFileFilesystemHandler()                 */
505
/************************************************************************/
506
507
/*!
508
 \brief Install /vsisubfile/ virtual file handler.
509
510
 \verbatim embed:rst
511
 See :ref:`/vsisubfile/ documentation <vsisubfile>`
512
 \endverbatim
513
 */
514
515
void VSIInstallSubFileHandler()
516
1
{
517
1
    VSIFileManager::InstallHandler("/vsisubfile/",
518
1
                                   new VSISubFileFilesystemHandler);
519
1
}