1 /**********************************************************************
2  *
3  * Project:  CPL - Common Portability Library
4  * Purpose:  Implement VSI large file api for stdin
5  * Author:   Even Rouault, <even dot rouault at spatialys.com>
6  *
7  **********************************************************************
8  * Copyright (c) 2010-2012, Even Rouault <even dot rouault at spatialys.com>
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  ****************************************************************************/
28 
29 //! @cond Doxygen_Suppress
30 
31 #include "cpl_port.h"
32 #include "cpl_vsi.h"
33 
34 #include <cstddef>
35 #include <cstdio>
36 #include <cstring>
37 #if HAVE_FCNTL_H
38 #  include <fcntl.h>
39 #endif
40 #if HAVE_SYS_STAT_H
41 #include <sys/stat.h>
42 #endif
43 
44 #include <algorithm>
45 
46 #include "cpl_conv.h"
47 #include "cpl_error.h"
48 #include "cpl_vsi_virtual.h"
49 
50 #ifdef WIN32
51 #include <io.h>
52 #include <fcntl.h>
53 #endif
54 
55 CPL_CVSID("$Id: cpl_vsil_stdin.cpp a044c83f8091becdd11e27be6e9c08d0d3478126 2021-02-24 11:38:17 +0100 Even Rouault $")
56 
57 // We buffer the first 1MB of standard input to enable drivers
58 // to autodetect data. In the first MB, backward and forward seeking
59 // is allowed, after only forward seeking will work.
60 // TODO(schwehr): Make BUFFER_SIZE a static const.
61 #define BUFFER_SIZE (1024 * 1024)
62 
63 static GByte* pabyBuffer = nullptr;
64 static GUInt32 nBufferLen = 0;
65 static GUIntBig nRealPos = 0;
66 
67 /************************************************************************/
68 /*                           VSIStdinInit()                             */
69 /************************************************************************/
70 
VSIStdinInit()71 static void VSIStdinInit()
72 {
73     if( pabyBuffer == nullptr )
74     {
75 #ifdef WIN32
76         setmode( fileno( stdin ), O_BINARY );
77 #endif
78         pabyBuffer = static_cast<GByte *>(CPLMalloc(BUFFER_SIZE));
79     }
80 }
81 
82 /************************************************************************/
83 /* ==================================================================== */
84 /*                       VSIStdinFilesystemHandler                     */
85 /* ==================================================================== */
86 /************************************************************************/
87 
88 class VSIStdinFilesystemHandler final : public VSIFilesystemHandler
89 {
90     CPL_DISALLOW_COPY_ASSIGN(VSIStdinFilesystemHandler)
91 
92   public:
93     VSIStdinFilesystemHandler();
94     ~VSIStdinFilesystemHandler() override;
95 
96     VSIVirtualHandle *Open( const char *pszFilename,
97                             const char *pszAccess,
98                             bool bSetError,
99                             CSLConstList /* papszOptions */ ) override;
100     int Stat( const char *pszFilename, VSIStatBufL *pStatBuf,
101               int nFlags ) override;
102 };
103 
104 /************************************************************************/
105 /* ==================================================================== */
106 /*                        VSIStdinHandle                               */
107 /* ==================================================================== */
108 /************************************************************************/
109 
110 class VSIStdinHandle final : public VSIVirtualHandle
111 {
112   private:
113     CPL_DISALLOW_COPY_ASSIGN(VSIStdinHandle)
114 
115     GUIntBig nCurOff = 0;
116     int               ReadAndCache( void* pBuffer, int nToRead );
117 
118   public:
119     VSIStdinHandle() = default;
120     ~VSIStdinHandle() override = default;
121 
122     int Seek( vsi_l_offset nOffset, int nWhence ) override;
123     vsi_l_offset Tell() override;
124     size_t Read( void *pBuffer, size_t nSize, size_t nMemb ) override;
125     size_t Write( const void *pBuffer, size_t nSize, size_t nMemb ) override;
126     int Eof() override;
127     int Close() override;
128 };
129 
130 /************************************************************************/
131 /*                              ReadAndCache()                          */
132 /************************************************************************/
133 
ReadAndCache(void * pBuffer,int nToRead)134 int VSIStdinHandle::ReadAndCache( void* pBuffer, int nToRead )
135 {
136     CPLAssert(nCurOff == nRealPos);
137 
138     int nRead = static_cast<int>(fread(pBuffer, 1, nToRead, stdin));
139 
140     if( nRealPos < BUFFER_SIZE )
141     {
142         const int nToCopy =
143             std::min(BUFFER_SIZE - static_cast<int>(nRealPos), nRead);
144         memcpy(pabyBuffer + nRealPos, pBuffer, nToCopy);
145         nBufferLen += nToCopy;
146     }
147 
148     nCurOff += nRead;
149     nRealPos = nCurOff;
150 
151     return nRead;
152 }
153 
154 /************************************************************************/
155 /*                                Seek()                                */
156 /************************************************************************/
157 
Seek(vsi_l_offset nOffset,int nWhence)158 int VSIStdinHandle::Seek( vsi_l_offset nOffset, int nWhence )
159 
160 {
161     if( nWhence == SEEK_SET && nOffset == nCurOff )
162         return 0;
163 
164     VSIStdinInit();
165     if( nRealPos < BUFFER_SIZE )
166     {
167         nRealPos += fread(pabyBuffer + nRealPos, 1,
168                           BUFFER_SIZE - static_cast<int>(nRealPos), stdin);
169         nBufferLen = static_cast<int>(nRealPos);
170     }
171 
172     if( nWhence == SEEK_END )
173     {
174         if( nOffset != 0 )
175         {
176             CPLError(CE_Failure, CPLE_NotSupported,
177                      "Seek(xx != 0, SEEK_END) unsupported on /vsistdin");
178             return -1;
179         }
180 
181         if( nBufferLen < BUFFER_SIZE )
182         {
183             nCurOff = nBufferLen;
184             return 0;
185         }
186 
187         CPLError(CE_Failure, CPLE_NotSupported,
188                  "Seek(SEEK_END) unsupported on /vsistdin when stdin > 1 MB");
189         return -1;
190     }
191 
192     if( nWhence == SEEK_CUR )
193         nOffset += nCurOff;
194 
195     if( nRealPos > nBufferLen && nOffset < nRealPos )
196     {
197         CPLError(CE_Failure, CPLE_NotSupported,
198                  "backward Seek() unsupported on /vsistdin above first MB");
199         return -1;
200     }
201 
202     if( nOffset < nBufferLen )
203     {
204         nCurOff = nOffset;
205         return 0;
206     }
207 
208     if( nOffset == nCurOff )
209         return 0;
210 
211     CPLDebug("VSI", "Forward seek from " CPL_FRMT_GUIB " to " CPL_FRMT_GUIB,
212              nCurOff, nOffset);
213 
214     char abyTemp[8192] = {};
215     nCurOff = nRealPos;
216     while( true )
217     {
218         const vsi_l_offset nMaxToRead = 8192;
219         const int nToRead = static_cast<int>(std::min(nMaxToRead,
220                                                       nOffset - nCurOff));
221         const int nRead = ReadAndCache(abyTemp, nToRead);
222 
223         if( nRead < nToRead )
224             return -1;
225         if( nToRead < 8192 )
226             break;
227     }
228 
229     return 0;
230 }
231 
232 /************************************************************************/
233 /*                                Tell()                                */
234 /************************************************************************/
235 
Tell()236 vsi_l_offset VSIStdinHandle::Tell()
237 {
238     return nCurOff;
239 }
240 
241 /************************************************************************/
242 /*                                Read()                                */
243 /************************************************************************/
244 
Read(void * pBuffer,size_t nSize,size_t nCount)245 size_t VSIStdinHandle::Read( void * pBuffer, size_t nSize, size_t nCount )
246 
247 {
248     VSIStdinInit();
249 
250     if( nCurOff < nBufferLen )
251     {
252         if( nCurOff + nSize * nCount < nBufferLen )
253         {
254             memcpy(pBuffer, pabyBuffer + nCurOff, nSize * nCount);
255             nCurOff += nSize * nCount;
256             return nCount;
257         }
258 
259         const int nAlreadyCached = static_cast<int>(nBufferLen - nCurOff);
260         memcpy(pBuffer, pabyBuffer + nCurOff, nAlreadyCached);
261 
262         nCurOff += nAlreadyCached;
263 
264         const int nRead =
265             ReadAndCache( static_cast<GByte *>(pBuffer) + nAlreadyCached,
266                           static_cast<int>(nSize*nCount - nAlreadyCached) );
267 
268         return (nRead + nAlreadyCached) / nSize;
269     }
270 
271     int nRead = ReadAndCache( pBuffer, static_cast<int>(nSize * nCount) );
272     return nRead / nSize;
273 }
274 
275 /************************************************************************/
276 /*                               Write()                                */
277 /************************************************************************/
278 
Write(const void *,size_t,size_t)279 size_t VSIStdinHandle::Write( const void * /* pBuffer */,
280                               size_t /* nSize */,
281                               size_t /* nCount */ )
282 {
283     CPLError(CE_Failure, CPLE_NotSupported,
284              "Write() unsupported on /vsistdin");
285     return 0;
286 }
287 
288 /************************************************************************/
289 /*                                Eof()                                 */
290 /************************************************************************/
291 
Eof()292 int VSIStdinHandle::Eof()
293 
294 {
295     if( nCurOff < nBufferLen )
296         return FALSE;
297     return feof(stdin);
298 }
299 
300 /************************************************************************/
301 /*                               Close()                                */
302 /************************************************************************/
303 
Close()304 int VSIStdinHandle::Close()
305 
306 {
307     return 0;
308 }
309 
310 /************************************************************************/
311 /* ==================================================================== */
312 /*                       VSIStdinFilesystemHandler                     */
313 /* ==================================================================== */
314 /************************************************************************/
315 
316 /************************************************************************/
317 /*                        VSIStdinFilesystemHandler()                   */
318 /************************************************************************/
319 
VSIStdinFilesystemHandler()320 VSIStdinFilesystemHandler::VSIStdinFilesystemHandler()
321 {
322     pabyBuffer = nullptr;
323     nBufferLen = 0;
324     nRealPos = 0;
325 }
326 
327 /************************************************************************/
328 /*                       ~VSIStdinFilesystemHandler()                   */
329 /************************************************************************/
330 
~VSIStdinFilesystemHandler()331 VSIStdinFilesystemHandler::~VSIStdinFilesystemHandler()
332 {
333     CPLFree(pabyBuffer);
334     pabyBuffer = nullptr;
335 }
336 
337 /************************************************************************/
338 /*                                Open()                                */
339 /************************************************************************/
340 
341 VSIVirtualHandle *
Open(const char * pszFilename,const char * pszAccess,bool,CSLConstList)342 VSIStdinFilesystemHandler::Open( const char *pszFilename,
343                                  const char *pszAccess,
344                                  bool /* bSetError */,
345                                  CSLConstList /* papszOptions */ )
346 
347 {
348     if( strcmp(pszFilename, "/vsistdin/") != 0 )
349         return nullptr;
350 
351     if( !CPLTestBool(CPLGetConfigOption("CPL_ALLOW_VSISTDIN", "YES")) )
352     {
353         CPLError(CE_Failure, CPLE_NotSupported,
354                  "/vsistdin/ disabled. Set CPL_ALLOW_VSISTDIN to YES to "
355                 "enable it");
356         return nullptr;
357     }
358 
359     if( strchr(pszAccess, 'w') != nullptr ||
360         strchr(pszAccess, '+') != nullptr )
361     {
362         CPLError(CE_Failure, CPLE_NotSupported,
363                  "Write or update mode not supported on /vsistdin");
364         return nullptr;
365     }
366 
367     return new VSIStdinHandle;
368 }
369 
370 /************************************************************************/
371 /*                                Stat()                                */
372 /************************************************************************/
373 
Stat(const char * pszFilename,VSIStatBufL * pStatBuf,int nFlags)374 int VSIStdinFilesystemHandler::Stat( const char * pszFilename,
375                                      VSIStatBufL * pStatBuf,
376                                      int nFlags )
377 
378 {
379     memset( pStatBuf, 0, sizeof(VSIStatBufL) );
380 
381     if( strcmp(pszFilename, "/vsistdin/") != 0 )
382         return -1;
383 
384     if( !CPLTestBool(CPLGetConfigOption("CPL_ALLOW_VSISTDIN", "YES")) )
385     {
386         CPLError(CE_Failure, CPLE_NotSupported,
387                  "/vsistdin/ disabled. Set CPL_ALLOW_VSISTDIN to YES to "
388                 "enable it");
389         return -1;
390     }
391 
392     if( nFlags & VSI_STAT_SIZE_FLAG )
393     {
394         VSIStdinInit();
395         if( nBufferLen == 0 )
396             nRealPos = nBufferLen =
397                 static_cast<int>(fread(pabyBuffer, 1, BUFFER_SIZE, stdin));
398 
399         pStatBuf->st_size = nBufferLen;
400     }
401 
402     pStatBuf->st_mode = S_IFREG;
403     return 0;
404 }
405 
406 //! @endcond
407 
408 /************************************************************************/
409 /*                       VSIInstallStdinHandler()                       */
410 /************************************************************************/
411 
412 /**
413  * \brief Install /vsistdin/ file system handler
414  *
415  * A special file handler is installed that allows reading from the standard
416  * input stream.
417  *
418  * The file operations available are of course limited to Read() and
419  * forward Seek() (full seek in the first MB of a file).
420  *
421  * @since GDAL 1.8.0
422  */
VSIInstallStdinHandler()423 void VSIInstallStdinHandler()
424 
425 {
426     VSIFileManager::InstallHandler("/vsistdin/", new VSIStdinFilesystemHandler);
427 }
428