Coverage Report

Created: 2025-06-13 06:18

/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
0
    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
0
{
97
0
    VSISubFileHandle::Close();
98
0
}
99
100
/************************************************************************/
101
/*                               Close()                                */
102
/************************************************************************/
103
104
int VSISubFileHandle::Close()
105
106
0
{
107
0
    if (fp == nullptr)
108
0
        return -1;
109
0
    int nRet = VSIFCloseL(fp);
110
0
    fp = nullptr;
111
112
0
    return nRet;
113
0
}
114
115
/************************************************************************/
116
/*                                Seek()                                */
117
/************************************************************************/
118
119
int VSISubFileHandle::Seek(vsi_l_offset nOffset, int nWhence)
120
121
0
{
122
0
    bAtEOF = false;
123
124
0
    if (nWhence == SEEK_SET)
125
0
    {
126
0
        if (nOffset >
127
0
            std::numeric_limits<vsi_l_offset>::max() - nSubregionOffset)
128
0
            return -1;
129
0
        nOffset += nSubregionOffset;
130
0
    }
131
0
    else if (nWhence == SEEK_CUR)
132
0
    {
133
        // handle normally.
134
0
    }
135
0
    else if (nWhence == SEEK_END)
136
0
    {
137
0
        if (nSubregionSize != 0)
138
0
        {
139
0
            nOffset = nSubregionOffset + nSubregionSize;
140
0
            nWhence = SEEK_SET;
141
0
        }
142
0
    }
143
0
    else
144
0
    {
145
0
        errno = EINVAL;
146
0
        return -1;
147
0
    }
148
149
0
    return VSIFSeekL(fp, nOffset, nWhence);
150
0
}
151
152
/************************************************************************/
153
/*                                Tell()                                */
154
/************************************************************************/
155
156
vsi_l_offset VSISubFileHandle::Tell()
157
158
0
{
159
0
    vsi_l_offset nBasePos = VSIFTellL(fp);
160
0
    if (nBasePos >= nSubregionOffset)
161
0
        return nBasePos - nSubregionOffset;
162
0
    return 0;
163
0
}
164
165
/************************************************************************/
166
/*                                Read()                                */
167
/************************************************************************/
168
169
size_t VSISubFileHandle::Read(void *pBuffer, size_t nSize, size_t nCount)
170
171
0
{
172
0
    size_t nRet = 0;
173
0
    if (nSubregionSize == 0)
174
0
    {
175
0
        nRet = VSIFReadL(pBuffer, nSize, nCount, fp);
176
0
    }
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
0
    if (nRet < nCount)
206
0
    {
207
0
        if (fp->Eof())
208
0
            bAtEOF = true;
209
0
        else /* if (fp->Error()) */
210
0
            bError = true;
211
0
    }
212
213
0
    return nRet;
214
0
}
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
0
{
277
0
    return bAtEOF;
278
0
}
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
0
{
299
0
    if (!STARTS_WITH(pszPath, "/vsisubfile/"))
300
0
        return FALSE;
301
302
0
    osFilename = "";
303
0
    nSubFileOffset = 0;
304
0
    nSubFileSize = 0;
305
306
0
    nSubFileOffset =
307
0
        CPLScanUIntBig(pszPath + 12, static_cast<int>(strlen(pszPath + 12)));
308
0
    for (int i = 12; pszPath[i] != '\0'; i++)
309
0
    {
310
0
        if (pszPath[i] == '_' && nSubFileSize == 0)
311
0
        {
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
0
            if (pszPath[i + 1] == '-')
317
0
                nSubFileSize = 0;
318
0
            else
319
0
                nSubFileSize = CPLScanUIntBig(
320
0
                    pszPath + i + 1, static_cast<int>(strlen(pszPath + i + 1)));
321
0
        }
322
0
        else if (pszPath[i] == ',')
323
0
        {
324
0
            osFilename = pszPath + i + 1;
325
0
            return TRUE;
326
0
        }
327
0
        else if (pszPath[i] == '/')
328
0
        {
329
            // Missing comma!
330
0
            return FALSE;
331
0
        }
332
0
    }
333
334
0
    return FALSE;
335
0
}
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
0
{
347
0
    if (!STARTS_WITH_CI(pszFilename, "/vsisubfile/"))
348
0
        return nullptr;
349
350
0
    CPLString osSubFilePath;
351
0
    vsi_l_offset nOff = 0;
352
0
    vsi_l_offset nSize = 0;
353
354
0
    if (!DecomposePath(pszFilename, osSubFilePath, nOff, nSize))
355
0
    {
356
0
        errno = ENOENT;
357
0
        return nullptr;
358
0
    }
359
0
    if (nOff > std::numeric_limits<vsi_l_offset>::max() - nSize)
360
0
    {
361
0
        return nullptr;
362
0
    }
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
0
    if (pszAccess[0] == 'w')
369
0
        pszAccess = "r+";
370
371
    /* -------------------------------------------------------------------- */
372
    /*      Open the underlying file.                                       */
373
    /* -------------------------------------------------------------------- */
374
0
    VSILFILE *fp = VSIFOpenL(osSubFilePath, pszAccess);
375
376
0
    if (fp == nullptr)
377
0
        return nullptr;
378
379
    /* -------------------------------------------------------------------- */
380
    /*      Setup the file handle on this file.                             */
381
    /* -------------------------------------------------------------------- */
382
0
    VSISubFileHandle *poHandle = new VSISubFileHandle;
383
384
0
    poHandle->fp = fp;
385
0
    poHandle->nSubregionOffset = nOff;
386
0
    poHandle->nSubregionSize = nSize;
387
388
    // In read-only mode validate (offset, size) against underlying file size
389
0
    if (strchr(pszAccess, 'r') != nullptr && strchr(pszAccess, '+') == nullptr)
390
0
    {
391
0
        if (VSIFSeekL(fp, 0, SEEK_END) != 0)
392
0
        {
393
0
            poHandle->Close();
394
0
            delete poHandle;
395
0
            return nullptr;
396
0
        }
397
0
        vsi_l_offset nFpSize = VSIFTellL(fp);
398
        // For a directory, the size will be max(vsi_l_offset) / 2
399
0
        if (nFpSize == ~(static_cast<vsi_l_offset>(0)) / 2 || nOff > nFpSize)
400
0
        {
401
0
            poHandle->Close();
402
0
            delete poHandle;
403
0
            return nullptr;
404
0
        }
405
0
        if (nOff + nSize > nFpSize)
406
0
        {
407
0
            nSize = nFpSize - nOff;
408
0
            poHandle->nSubregionSize = nSize;
409
0
        }
410
0
    }
411
412
0
    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
0
    return poHandle;
420
0
}
421
422
/************************************************************************/
423
/*                                Stat()                                */
424
/************************************************************************/
425
426
int VSISubFileFilesystemHandler::Stat(const char *pszFilename,
427
                                      VSIStatBufL *psStatBuf, int nFlags)
428
429
0
{
430
0
    if (!STARTS_WITH_CI(pszFilename, "/vsisubfile/"))
431
0
        return -1;
432
433
0
    CPLString osSubFilePath;
434
0
    vsi_l_offset nOff = 0;
435
0
    vsi_l_offset nSize = 0;
436
437
0
    memset(psStatBuf, 0, sizeof(VSIStatBufL));
438
439
0
    if (!DecomposePath(pszFilename, osSubFilePath, nOff, nSize))
440
0
    {
441
0
        errno = ENOENT;
442
0
        return -1;
443
0
    }
444
445
0
    const int nResult = VSIStatExL(osSubFilePath, psStatBuf, nFlags);
446
447
0
    if (nResult == 0)
448
0
    {
449
0
        if (nSize != 0)
450
0
            psStatBuf->st_size = nSize;
451
0
        else if (static_cast<vsi_l_offset>(psStatBuf->st_size) >= nOff)
452
0
            psStatBuf->st_size -= nOff;
453
0
        else
454
0
            psStatBuf->st_size = 0;
455
0
    }
456
457
0
    return nResult;
458
0
}
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
}