1 : /******************************************************************************
2 : * $Id: cpl_vsi_mem.cpp 24698 2012-07-23 09:10:33Z rouault $
3 : *
4 : * Project: VSI Virtual File System
5 : * Purpose: Implementation of Memory Buffer virtual IO functions.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2005, Frank Warmerdam <warmerdam@pobox.com>
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : /* Remove annoying warnings in eVC++ and VC++ 6.0 */
31 : #if defined(WIN32CE)
32 : # pragma warning(disable:4786)
33 : #endif
34 :
35 : #include "cpl_vsi_virtual.h"
36 : #include "cpl_string.h"
37 : #include "cpl_multiproc.h"
38 : #include <map>
39 :
40 : #if defined(WIN32CE)
41 : # include <wce_errno.h>
42 : #endif
43 :
44 :
45 : CPL_CVSID("$Id: cpl_vsi_mem.cpp 24698 2012-07-23 09:10:33Z rouault $");
46 :
47 : /*
48 : ** Notes on Multithreading:
49 : **
50 : ** VSIMemFilesystemHandler: This class maintains a mutex to protect
51 : ** access and update of the oFileList array which has all the "files" in
52 : ** the memory filesystem area. It is expected that multiple threads would
53 : ** want to create and read different files at the same time and so might
54 : ** collide access oFileList without the mutex.
55 : **
56 : ** VSIMemFile: In theory we could allow different threads to update the
57 : ** the same memory file, but for simplicity we restrict to single writer,
58 : ** multiple reader as an expectation on the application code (not enforced
59 : ** here), which means we don't need to do any protection of this class.
60 : **
61 : ** VSIMemHandle: This is essentially a "current location" representing
62 : ** on accessor to a file, and is inherently intended only to be used in
63 : ** a single thread.
64 : **
65 : ** In General:
66 : **
67 : ** Multiple threads accessing the memory filesystem are ok as long as
68 : ** 1) A given VSIMemHandle (ie. FILE * at app level) isn't used by multiple
69 : ** threads at once.
70 : ** 2) A given memory file isn't accessed by more than one thread unless
71 : ** all threads are just reading.
72 : */
73 :
74 : /************************************************************************/
75 : /* ==================================================================== */
76 : /* VSIMemFile */
77 : /* ==================================================================== */
78 : /************************************************************************/
79 :
80 : class VSIMemFile
81 : {
82 : public:
83 : CPLString osFilename;
84 : int nRefCount;
85 :
86 : int bIsDirectory;
87 :
88 : int bOwnData;
89 : GByte *pabyData;
90 : vsi_l_offset nLength;
91 : vsi_l_offset nAllocLength;
92 :
93 : int bEOF;
94 :
95 : VSIMemFile();
96 : virtual ~VSIMemFile();
97 :
98 : bool SetLength( vsi_l_offset nNewSize );
99 : };
100 :
101 : /************************************************************************/
102 : /* ==================================================================== */
103 : /* VSIMemHandle */
104 : /* ==================================================================== */
105 : /************************************************************************/
106 :
107 : class VSIMemHandle : public VSIVirtualHandle
108 31460 : {
109 : public:
110 : VSIMemFile *poFile;
111 : vsi_l_offset nOffset;
112 : int bUpdate;
113 : int bEOF;
114 :
115 : virtual int Seek( vsi_l_offset nOffset, int nWhence );
116 : virtual vsi_l_offset Tell();
117 : virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb );
118 : virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb );
119 : virtual int Eof();
120 : virtual int Close();
121 : virtual int Truncate( vsi_l_offset nNewSize );
122 : };
123 :
124 : /************************************************************************/
125 : /* ==================================================================== */
126 : /* VSIMemFilesystemHandler */
127 : /* ==================================================================== */
128 : /************************************************************************/
129 :
130 : class VSIMemFilesystemHandler : public VSIFilesystemHandler
131 : {
132 : public:
133 : std::map<CPLString,VSIMemFile*> oFileList;
134 : void *hMutex;
135 :
136 : VSIMemFilesystemHandler();
137 : virtual ~VSIMemFilesystemHandler();
138 :
139 : virtual VSIVirtualHandle *Open( const char *pszFilename,
140 : const char *pszAccess);
141 : virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags );
142 : virtual int Unlink( const char *pszFilename );
143 : virtual int Mkdir( const char *pszDirname, long nMode );
144 : virtual int Rmdir( const char *pszDirname );
145 : virtual char **ReadDir( const char *pszDirname );
146 : virtual int Rename( const char *oldpath, const char *newpath );
147 :
148 : static void NormalizePath( CPLString & );
149 : };
150 :
151 : /************************************************************************/
152 : /* ==================================================================== */
153 : /* VSIMemFile */
154 : /* ==================================================================== */
155 : /************************************************************************/
156 :
157 : /************************************************************************/
158 : /* VSIMemFile() */
159 : /************************************************************************/
160 :
161 5003 : VSIMemFile::VSIMemFile()
162 :
163 : {
164 5003 : nRefCount = 0;
165 5003 : bIsDirectory = FALSE;
166 5003 : bOwnData = TRUE;
167 5003 : pabyData = NULL;
168 5003 : nLength = 0;
169 5003 : nAllocLength = 0;
170 5003 : bEOF = FALSE;
171 5003 : }
172 :
173 : /************************************************************************/
174 : /* ~VSIMemFile() */
175 : /************************************************************************/
176 :
177 5003 : VSIMemFile::~VSIMemFile()
178 :
179 : {
180 5003 : if( nRefCount != 0 )
181 : CPLDebug( "VSIMemFile", "Memory file %s deleted with %d references.",
182 0 : osFilename.c_str(), nRefCount );
183 :
184 5003 : if( bOwnData && pabyData )
185 4750 : CPLFree( pabyData );
186 5003 : }
187 :
188 : /************************************************************************/
189 : /* SetLength() */
190 : /************************************************************************/
191 :
192 150968 : bool VSIMemFile::SetLength( vsi_l_offset nNewLength )
193 :
194 : {
195 : /* -------------------------------------------------------------------- */
196 : /* Grow underlying array if needed. */
197 : /* -------------------------------------------------------------------- */
198 150968 : if( nNewLength > nAllocLength )
199 : {
200 : /* If we don't own the buffer, we cannot reallocate it because */
201 : /* the return address might be different from the one passed by */
202 : /* the caller. Hence, the caller would not be able to free */
203 : /* the buffer... */
204 7293 : if( !bOwnData )
205 : {
206 : CPLError(CE_Failure, CPLE_NotSupported,
207 0 : "Cannot extended in-memory file whose ownership was not transfered");
208 0 : return false;
209 : }
210 :
211 : GByte *pabyNewData;
212 7293 : vsi_l_offset nNewAlloc = (nNewLength + nNewLength / 10) + 5000;
213 :
214 7293 : pabyNewData = (GByte *) VSIRealloc(pabyData, (size_t)nNewAlloc);
215 7293 : if( pabyNewData == NULL )
216 : {
217 : CPLError(CE_Failure, CPLE_OutOfMemory,
218 : "Cannot extend in-memory file to " CPL_FRMT_GUIB " bytes due to out-of-memory situation",
219 0 : nNewAlloc);
220 0 : return false;
221 : }
222 :
223 : /* Clear the new allocated part of the buffer */
224 : memset(pabyNewData + nAllocLength, 0,
225 7293 : (size_t) (nNewAlloc - nAllocLength));
226 :
227 7293 : pabyData = pabyNewData;
228 7293 : nAllocLength = nNewAlloc;
229 : }
230 :
231 150968 : nLength = nNewLength;
232 :
233 150968 : return true;
234 : }
235 :
236 : /************************************************************************/
237 : /* ==================================================================== */
238 : /* VSIMemHandle */
239 : /* ==================================================================== */
240 : /************************************************************************/
241 :
242 :
243 : /************************************************************************/
244 : /* Close() */
245 : /************************************************************************/
246 :
247 15730 : int VSIMemHandle::Close()
248 :
249 : {
250 15730 : if( --(poFile->nRefCount) == 0 )
251 26 : delete poFile;
252 :
253 15730 : poFile = NULL;
254 :
255 15730 : return 0;
256 : }
257 :
258 : /************************************************************************/
259 : /* Seek() */
260 : /************************************************************************/
261 :
262 679734 : int VSIMemHandle::Seek( vsi_l_offset nOffset, int nWhence )
263 :
264 : {
265 679734 : if( nWhence == SEEK_CUR )
266 353929 : this->nOffset += nOffset;
267 325805 : else if( nWhence == SEEK_SET )
268 302735 : this->nOffset = nOffset;
269 23070 : else if( nWhence == SEEK_END )
270 23070 : this->nOffset = poFile->nLength + nOffset;
271 : else
272 : {
273 0 : errno = EINVAL;
274 0 : return -1;
275 : }
276 :
277 679734 : bEOF = FALSE;
278 :
279 679734 : if( this->nOffset > poFile->nLength )
280 : {
281 652 : if( !bUpdate ) // Read-only files cannot be extended by seek.
282 : {
283 : CPLDebug( "VSIMemHandle",
284 : "Attempt to extend read-only file '%s' to length %d from %d, .",
285 : poFile->osFilename.c_str(),
286 2 : (int) this->nOffset, (int) poFile->nLength );
287 :
288 2 : this->nOffset = poFile->nLength;
289 2 : errno = EACCES;
290 2 : return -1;
291 : }
292 : else // Writeable files are zero-extended by seek past end.
293 : {
294 650 : if( !poFile->SetLength( this->nOffset ) )
295 0 : return -1;
296 : }
297 : }
298 :
299 679732 : return 0;
300 : }
301 :
302 : /************************************************************************/
303 : /* Tell() */
304 : /************************************************************************/
305 :
306 38126 : vsi_l_offset VSIMemHandle::Tell()
307 :
308 : {
309 38126 : return nOffset;
310 : }
311 :
312 : /************************************************************************/
313 : /* Read() */
314 : /************************************************************************/
315 :
316 2776196 : size_t VSIMemHandle::Read( void * pBuffer, size_t nSize, size_t nCount )
317 :
318 : {
319 : // FIXME: Integer overflow check should be placed here:
320 2776196 : size_t nBytesToRead = nSize * nCount;
321 :
322 2776196 : if( nBytesToRead + nOffset > poFile->nLength )
323 : {
324 1568 : if (poFile->nLength < nOffset)
325 : {
326 0 : bEOF = TRUE;
327 0 : return 0;
328 : }
329 :
330 1568 : nBytesToRead = (size_t)(poFile->nLength - nOffset);
331 1568 : nCount = nBytesToRead / nSize;
332 1568 : bEOF = TRUE;
333 : }
334 :
335 2776196 : memcpy( pBuffer, poFile->pabyData + nOffset, (size_t)nBytesToRead );
336 2776196 : nOffset += nBytesToRead;
337 :
338 2776196 : return nCount;
339 : }
340 :
341 : /************************************************************************/
342 : /* Write() */
343 : /************************************************************************/
344 :
345 163879 : size_t VSIMemHandle::Write( const void * pBuffer, size_t nSize, size_t nCount )
346 :
347 : {
348 163879 : if( !bUpdate )
349 : {
350 484 : errno = EACCES;
351 484 : return 0;
352 : }
353 :
354 : // FIXME: Integer overflow check should be placed here:
355 163395 : size_t nBytesToWrite = nSize * nCount;
356 :
357 163395 : if( nBytesToWrite + nOffset > poFile->nLength )
358 : {
359 150160 : if( !poFile->SetLength( nBytesToWrite + nOffset ) )
360 0 : return 0;
361 : }
362 :
363 163395 : memcpy( poFile->pabyData + nOffset, pBuffer, nBytesToWrite );
364 163395 : nOffset += nBytesToWrite;
365 :
366 163395 : return nCount;
367 : }
368 :
369 : /************************************************************************/
370 : /* Eof() */
371 : /************************************************************************/
372 :
373 18629 : int VSIMemHandle::Eof()
374 :
375 : {
376 18629 : return bEOF;
377 : }
378 :
379 : /************************************************************************/
380 : /* Truncate() */
381 : /************************************************************************/
382 :
383 100 : int VSIMemHandle::Truncate( vsi_l_offset nNewSize )
384 : {
385 100 : if( !bUpdate )
386 : {
387 0 : errno = EACCES;
388 0 : return -1;
389 : }
390 :
391 100 : if (poFile->SetLength( nNewSize ))
392 100 : return 0;
393 : else
394 0 : return -1;
395 : }
396 :
397 : /************************************************************************/
398 : /* ==================================================================== */
399 : /* VSIMemFilesystemHandler */
400 : /* ==================================================================== */
401 : /************************************************************************/
402 :
403 : /************************************************************************/
404 : /* VSIMemFilesystemHandler() */
405 : /************************************************************************/
406 :
407 712 : VSIMemFilesystemHandler::VSIMemFilesystemHandler()
408 :
409 : {
410 712 : hMutex = NULL;
411 712 : }
412 :
413 : /************************************************************************/
414 : /* ~VSIMemFilesystemHandler() */
415 : /************************************************************************/
416 :
417 687 : VSIMemFilesystemHandler::~VSIMemFilesystemHandler()
418 :
419 : {
420 687 : std::map<CPLString,VSIMemFile*>::const_iterator iter;
421 :
422 715 : for( iter = oFileList.begin(); iter != oFileList.end(); ++iter )
423 : {
424 28 : iter->second->nRefCount--;
425 28 : delete iter->second;
426 : }
427 :
428 687 : if( hMutex != NULL )
429 19 : CPLDestroyMutex( hMutex );
430 687 : hMutex = NULL;
431 687 : }
432 :
433 : /************************************************************************/
434 : /* Open() */
435 : /************************************************************************/
436 :
437 : VSIVirtualHandle *
438 23886 : VSIMemFilesystemHandler::Open( const char *pszFilename,
439 : const char *pszAccess )
440 :
441 : {
442 23886 : CPLMutexHolder oHolder( &hMutex );
443 : VSIMemFile *poFile;
444 23886 : CPLString osFilename = pszFilename;
445 23886 : NormalizePath( osFilename );
446 :
447 : /* -------------------------------------------------------------------- */
448 : /* Get the filename we are opening, create if needed. */
449 : /* -------------------------------------------------------------------- */
450 23886 : if( oFileList.find(osFilename) == oFileList.end() )
451 12774 : poFile = NULL;
452 : else
453 11112 : poFile = oFileList[osFilename];
454 :
455 23886 : if( strstr(pszAccess,"w") == NULL && poFile == NULL )
456 : {
457 8153 : errno = ENOENT;
458 8153 : return NULL;
459 : }
460 :
461 15733 : if( strstr(pszAccess,"w") )
462 : {
463 4679 : if( poFile )
464 58 : poFile->SetLength( 0 );
465 : else
466 : {
467 4621 : poFile = new VSIMemFile;
468 4621 : poFile->osFilename = osFilename;
469 4621 : oFileList[poFile->osFilename] = poFile;
470 4621 : poFile->nRefCount++; // for file list
471 : }
472 : }
473 :
474 15733 : if( poFile->bIsDirectory )
475 : {
476 3 : errno = EISDIR;
477 3 : return NULL;
478 : }
479 :
480 : /* -------------------------------------------------------------------- */
481 : /* Setup the file handle on this file. */
482 : /* -------------------------------------------------------------------- */
483 15730 : VSIMemHandle *poHandle = new VSIMemHandle;
484 :
485 15730 : poHandle->poFile = poFile;
486 15730 : poHandle->nOffset = 0;
487 15730 : poHandle->bEOF = FALSE;
488 38490 : if( strstr(pszAccess,"w") || strstr(pszAccess,"+")
489 : || strstr(pszAccess,"a") )
490 11709 : poHandle->bUpdate = TRUE;
491 : else
492 4021 : poHandle->bUpdate = FALSE;
493 :
494 15730 : poFile->nRefCount++;
495 :
496 15730 : if( strstr(pszAccess,"a") )
497 0 : poHandle->nOffset = poFile->nLength;
498 :
499 15730 : return poHandle;
500 : }
501 :
502 : /************************************************************************/
503 : /* Stat() */
504 : /************************************************************************/
505 :
506 8563 : int VSIMemFilesystemHandler::Stat( const char * pszFilename,
507 : VSIStatBufL * pStatBuf,
508 : int nFlags )
509 :
510 : {
511 : (void) nFlags;
512 :
513 8563 : CPLMutexHolder oHolder( &hMutex );
514 :
515 8563 : CPLString osFilename = pszFilename;
516 8563 : NormalizePath( osFilename );
517 :
518 8563 : memset( pStatBuf, 0, sizeof(VSIStatBufL) );
519 :
520 8563 : if ( osFilename == "/vsimem/" )
521 : {
522 5 : pStatBuf->st_size = 0;
523 5 : pStatBuf->st_mode = S_IFDIR;
524 5 : return 0;
525 : }
526 :
527 8558 : if( oFileList.find(osFilename) == oFileList.end() )
528 : {
529 6172 : errno = ENOENT;
530 6172 : return -1;
531 : }
532 :
533 2386 : VSIMemFile *poFile = oFileList[osFilename];
534 :
535 2386 : memset( pStatBuf, 0, sizeof(VSIStatBufL) );
536 :
537 2386 : if( poFile->bIsDirectory )
538 : {
539 43 : pStatBuf->st_size = 0;
540 43 : pStatBuf->st_mode = S_IFDIR;
541 : }
542 : else
543 : {
544 2343 : pStatBuf->st_size = (long)poFile->nLength;
545 2343 : pStatBuf->st_mode = S_IFREG;
546 : }
547 :
548 2386 : return 0;
549 : }
550 :
551 : /************************************************************************/
552 : /* Unlink() */
553 : /************************************************************************/
554 :
555 7703 : int VSIMemFilesystemHandler::Unlink( const char * pszFilename )
556 :
557 : {
558 7703 : CPLMutexHolder oHolder( &hMutex );
559 :
560 7703 : CPLString osFilename = pszFilename;
561 7703 : NormalizePath( osFilename );
562 :
563 : VSIMemFile *poFile;
564 :
565 7703 : if( oFileList.find(osFilename) == oFileList.end() )
566 : {
567 2771 : errno = ENOENT;
568 2771 : return -1;
569 : }
570 : else
571 : {
572 4932 : poFile = oFileList[osFilename];
573 :
574 4932 : if( --(poFile->nRefCount) == 0 )
575 4906 : delete poFile;
576 :
577 4932 : oFileList.erase( oFileList.find(osFilename) );
578 :
579 4932 : return 0;
580 0 : }
581 : }
582 :
583 : /************************************************************************/
584 : /* Mkdir() */
585 : /************************************************************************/
586 :
587 34 : int VSIMemFilesystemHandler::Mkdir( const char * pszPathname,
588 : long nMode )
589 :
590 : {
591 : (void) nMode;
592 :
593 34 : CPLMutexHolder oHolder( &hMutex );
594 :
595 34 : CPLString osPathname = pszPathname;
596 :
597 34 : NormalizePath( osPathname );
598 :
599 34 : if( oFileList.find(osPathname) != oFileList.end() )
600 : {
601 7 : errno = EEXIST;
602 7 : return -1;
603 : }
604 :
605 27 : VSIMemFile *poFile = new VSIMemFile;
606 :
607 27 : poFile->osFilename = osPathname;
608 27 : poFile->bIsDirectory = TRUE;
609 27 : oFileList[osPathname] = poFile;
610 27 : poFile->nRefCount++; /* referenced by file list */
611 :
612 27 : return 0;
613 : }
614 :
615 : /************************************************************************/
616 : /* Rmdir() */
617 : /************************************************************************/
618 :
619 389 : int VSIMemFilesystemHandler::Rmdir( const char * pszPathname )
620 :
621 : {
622 389 : CPLMutexHolder oHolder( &hMutex );
623 :
624 389 : return Unlink( pszPathname );
625 : }
626 :
627 : /************************************************************************/
628 : /* ReadDir() */
629 : /************************************************************************/
630 :
631 1044 : char **VSIMemFilesystemHandler::ReadDir( const char *pszPath )
632 :
633 : {
634 1044 : CPLMutexHolder oHolder( &hMutex );
635 :
636 1044 : CPLString osPath = pszPath;
637 :
638 1044 : NormalizePath( osPath );
639 :
640 1044 : std::map<CPLString,VSIMemFile*>::const_iterator iter;
641 1044 : char **papszDir = NULL;
642 1044 : int nPathLen = strlen(osPath);
643 :
644 1044 : if( osPath[nPathLen-1] == '/' )
645 3 : nPathLen--;
646 :
647 : /* In case of really big number of files in the directory, CSLAddString */
648 : /* can be slow (see #2158). We then directly build the list. */
649 1044 : int nItems=0;
650 1044 : int nAllocatedItems=0;
651 :
652 35838 : for( iter = oFileList.begin(); iter != oFileList.end(); ++iter )
653 : {
654 34794 : const char *pszFilePath = iter->second->osFilename.c_str();
655 50520 : if( EQUALN(osPath,pszFilePath,nPathLen)
656 15726 : && pszFilePath[nPathLen] == '/'
657 : && strstr(pszFilePath+nPathLen+1,"/") == NULL )
658 : {
659 14134 : if (nItems == 0)
660 : {
661 725 : papszDir = (char**) CPLCalloc(2,sizeof(char*));
662 725 : nAllocatedItems = 1;
663 : }
664 13409 : else if (nItems >= nAllocatedItems)
665 : {
666 1968 : nAllocatedItems = nAllocatedItems * 2;
667 : papszDir = (char**)CPLRealloc(papszDir,
668 1968 : (nAllocatedItems+2)*sizeof(char*));
669 : }
670 :
671 14134 : papszDir[nItems] = CPLStrdup(pszFilePath+nPathLen+1);
672 14134 : papszDir[nItems+1] = NULL;
673 :
674 14134 : nItems++;
675 : }
676 : }
677 :
678 1044 : return papszDir;
679 : }
680 :
681 : /************************************************************************/
682 : /* Rename() */
683 : /************************************************************************/
684 :
685 0 : int VSIMemFilesystemHandler::Rename( const char *pszOldPath,
686 : const char *pszNewPath )
687 :
688 : {
689 0 : CPLMutexHolder oHolder( &hMutex );
690 :
691 0 : CPLString osOldPath = pszOldPath;
692 0 : CPLString osNewPath = pszNewPath;
693 :
694 0 : NormalizePath( osOldPath );
695 0 : NormalizePath( osNewPath );
696 :
697 0 : if ( osOldPath.compare(osNewPath) == 0 )
698 0 : return 0;
699 :
700 0 : if( oFileList.find(osOldPath) == oFileList.end() )
701 : {
702 0 : errno = ENOENT;
703 0 : return -1;
704 : }
705 : else
706 : {
707 0 : VSIMemFile* poFile = oFileList[osOldPath];
708 :
709 0 : oFileList.erase( oFileList.find(osOldPath) );
710 :
711 0 : Unlink(osNewPath);
712 :
713 0 : oFileList[osNewPath] = poFile;
714 0 : poFile->osFilename = osNewPath;
715 :
716 0 : return 0;
717 0 : }
718 : }
719 :
720 : /************************************************************************/
721 : /* NormalizePath() */
722 : /************************************************************************/
723 :
724 41697 : void VSIMemFilesystemHandler::NormalizePath( CPLString &oPath )
725 :
726 : {
727 41697 : int i, nSize = oPath.size();
728 :
729 1327839 : for( i = 0; i < nSize; i++ )
730 : {
731 1286142 : if( oPath[i] == '\\' )
732 4 : oPath[i] = '/';
733 : }
734 :
735 41697 : }
736 :
737 : /************************************************************************/
738 : /* VSIInstallLargeFileHandler() */
739 : /************************************************************************/
740 :
741 : /**
742 : * \brief Install "memory" file system handler.
743 : *
744 : * A special file handler is installed that allows block of memory to be
745 : * treated as files. All portions of the file system underneath the base
746 : * path "/vsimem/" will be handled by this driver.
747 : *
748 : * Normal VSI*L functions can be used freely to create and destroy memory
749 : * arrays treating them as if they were real file system objects. Some
750 : * additional methods exist to efficient create memory file system objects
751 : * without duplicating original copies of the data or to "steal" the block
752 : * of memory associated with a memory file.
753 : *
754 : * At this time the memory handler does not properly handle directory
755 : * semantics for the memory portion of the filesystem. The VSIReadDir()
756 : * function is not supported though this will be corrected in the future.
757 : *
758 : * Calling this function repeatedly should do no harm, though it is not
759 : * necessary. It is already called the first time a virtualizable
760 : * file access function (ie. VSIFOpenL(), VSIMkDir(), etc) is called.
761 : *
762 : * This code example demonstrates using GDAL to translate from one memory
763 : * buffer to another.
764 : *
765 : * \code
766 : * GByte *ConvertBufferFormat( GByte *pabyInData, vsi_l_offset nInDataLength,
767 : * vsi_l_offset *pnOutDataLength )
768 : * {
769 : * // create memory file system object from buffer.
770 : * VSIFCloseL( VSIFileFromMemBuffer( "/vsimem/work.dat", pabyInData,
771 : * nInDataLength, FALSE ) );
772 : *
773 : * // Open memory buffer for read.
774 : * GDALDatasetH hDS = GDALOpen( "/vsimem/work.dat", GA_ReadOnly );
775 : *
776 : * // Get output format driver.
777 : * GDALDriverH hDriver = GDALGetDriverByName( "GTiff" );
778 : * GDALDatasetH hOutDS;
779 : *
780 : * hOutDS = GDALCreateCopy( hDriver, "/vsimem/out.tif", hDS, TRUE, NULL,
781 : * NULL, NULL );
782 : *
783 : * // close source file, and "unlink" it.
784 : * GDALClose( hDS );
785 : * VSIUnlink( "/vsimem/work.dat" );
786 : *
787 : * // seize the buffer associated with the output file.
788 : *
789 : * return VSIGetMemFileBuffer( "/vsimem/out.tif", pnOutDataLength, TRUE );
790 : * }
791 : * \endcode
792 : */
793 :
794 712 : void VSIInstallMemFileHandler()
795 : {
796 712 : VSIFileManager::InstallHandler( "/vsimem/", new VSIMemFilesystemHandler );
797 712 : }
798 :
799 : /************************************************************************/
800 : /* VSIFileFromMemBuffer() */
801 : /************************************************************************/
802 :
803 : /**
804 : * \brief Create memory "file" from a buffer.
805 : *
806 : * A virtual memory file is created from the passed buffer with the indicated
807 : * filename. Under normal conditions the filename would need to be absolute
808 : * and within the /vsimem/ portion of the filesystem.
809 : *
810 : * If bTakeOwnership is TRUE, then the memory file system handler will take
811 : * ownership of the buffer, freeing it when the file is deleted. Otherwise
812 : * it remains the responsibility of the caller, but should not be freed as
813 : * long as it might be accessed as a file. In no circumstances does this
814 : * function take a copy of the pabyData contents.
815 : *
816 : * @param pszFilename the filename to be created.
817 : * @param pabyData the data buffer for the file.
818 : * @param nDataLength the length of buffer in bytes.
819 : * @param bTakeOwnership TRUE to transfer "ownership" of buffer or FALSE.
820 : *
821 : * @return open file handle on created file (see VSIFOpenL()).
822 : */
823 :
824 355 : VSILFILE *VSIFileFromMemBuffer( const char *pszFilename,
825 : GByte *pabyData,
826 : vsi_l_offset nDataLength,
827 : int bTakeOwnership )
828 :
829 : {
830 355 : if( VSIFileManager::GetHandler("")
831 : == VSIFileManager::GetHandler("/vsimem/") )
832 0 : VSIInstallMemFileHandler();
833 :
834 : VSIMemFilesystemHandler *poHandler = (VSIMemFilesystemHandler *)
835 355 : VSIFileManager::GetHandler("/vsimem/");
836 :
837 355 : if (pszFilename == NULL)
838 0 : return NULL;
839 :
840 355 : CPLString osFilename = pszFilename;
841 355 : VSIMemFilesystemHandler::NormalizePath( osFilename );
842 :
843 355 : VSIMemFile *poFile = new VSIMemFile;
844 :
845 355 : poFile->osFilename = osFilename;
846 355 : poFile->bOwnData = bTakeOwnership;
847 355 : poFile->pabyData = pabyData;
848 355 : poFile->nLength = nDataLength;
849 355 : poFile->nAllocLength = nDataLength;
850 :
851 : {
852 355 : CPLMutexHolder oHolder( &poHandler->hMutex );
853 355 : poHandler->Unlink(osFilename);
854 355 : poHandler->oFileList[poFile->osFilename] = poFile;
855 355 : poFile->nRefCount++;
856 : }
857 :
858 355 : return (VSILFILE *) poHandler->Open( osFilename, "r+" );
859 : }
860 :
861 : /************************************************************************/
862 : /* VSIGetMemFileBuffer() */
863 : /************************************************************************/
864 :
865 : /**
866 : * \brief Fetch buffer underlying memory file.
867 : *
868 : * This function returns a pointer to the memory buffer underlying a
869 : * virtual "in memory" file. If bUnlinkAndSeize is TRUE the filesystem
870 : * object will be deleted, and ownership of the buffer will pass to the
871 : * caller otherwise the underlying file will remain in existance.
872 : *
873 : * @param pszFilename the name of the file to grab the buffer of.
874 : * @param pnDataLength (file) length returned in this variable.
875 : * @param bUnlinkAndSeize TRUE to remove the file, or FALSE to leave unaltered.
876 : *
877 : * @return pointer to memory buffer or NULL on failure.
878 : */
879 :
880 112 : GByte *VSIGetMemFileBuffer( const char *pszFilename,
881 : vsi_l_offset *pnDataLength,
882 : int bUnlinkAndSeize )
883 :
884 : {
885 : VSIMemFilesystemHandler *poHandler = (VSIMemFilesystemHandler *)
886 112 : VSIFileManager::GetHandler("/vsimem/");
887 :
888 112 : if (pszFilename == NULL)
889 0 : return NULL;
890 :
891 112 : CPLString osFilename = pszFilename;
892 112 : VSIMemFilesystemHandler::NormalizePath( osFilename );
893 :
894 112 : CPLMutexHolder oHolder( &poHandler->hMutex );
895 :
896 112 : if( poHandler->oFileList.find(osFilename) == poHandler->oFileList.end() )
897 0 : return NULL;
898 :
899 112 : VSIMemFile *poFile = poHandler->oFileList[osFilename];
900 : GByte *pabyData;
901 :
902 112 : pabyData = poFile->pabyData;
903 112 : if( pnDataLength != NULL )
904 112 : *pnDataLength = poFile->nLength;
905 :
906 112 : if( bUnlinkAndSeize )
907 : {
908 43 : if( !poFile->bOwnData )
909 : CPLDebug( "VSIMemFile",
910 0 : "File doesn't own data in VSIGetMemFileBuffer!" );
911 : else
912 43 : poFile->bOwnData = FALSE;
913 :
914 43 : poHandler->oFileList.erase( poHandler->oFileList.find(osFilename) );
915 43 : --(poFile->nRefCount);
916 43 : delete poFile;
917 : }
918 :
919 112 : return pabyData;
920 : }
921 :
|