1 : /******************************************************************************
2 : * $Id: cpl_vsi_mem.cpp 23506 2011-12-10 13:43:59Z 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 23506 2011-12-10 13:43:59Z 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 37970 : {
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 3764 : VSIMemFile::VSIMemFile()
162 :
163 : {
164 3764 : nRefCount = 0;
165 3764 : bIsDirectory = FALSE;
166 3764 : bOwnData = TRUE;
167 3764 : pabyData = NULL;
168 3764 : nLength = 0;
169 3764 : nAllocLength = 0;
170 3764 : bEOF = FALSE;
171 3764 : }
172 :
173 : /************************************************************************/
174 : /* ~VSIMemFile() */
175 : /************************************************************************/
176 :
177 3764 : VSIMemFile::~VSIMemFile()
178 :
179 : {
180 3764 : if( nRefCount != 0 )
181 : CPLDebug( "VSIMemFile", "Memory file %s deleted with %d references.",
182 0 : osFilename.c_str(), nRefCount );
183 :
184 3764 : if( bOwnData && pabyData )
185 3568 : CPLFree( pabyData );
186 3764 : }
187 :
188 : /************************************************************************/
189 : /* SetLength() */
190 : /************************************************************************/
191 :
192 85529 : bool VSIMemFile::SetLength( vsi_l_offset nNewLength )
193 :
194 : {
195 : /* -------------------------------------------------------------------- */
196 : /* Grow underlying array if needed. */
197 : /* -------------------------------------------------------------------- */
198 85529 : 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 5676 : 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 5676 : vsi_l_offset nNewAlloc = (nNewLength + nNewLength / 10) + 5000;
213 :
214 5676 : pabyNewData = (GByte *) VSIRealloc(pabyData, (size_t)nNewAlloc);
215 5676 : if( pabyNewData == NULL )
216 0 : return false;
217 :
218 : /* Clear the new allocated part of the buffer */
219 : memset(pabyNewData + nAllocLength, 0,
220 5676 : (size_t) (nNewAlloc - nAllocLength));
221 :
222 5676 : pabyData = pabyNewData;
223 5676 : nAllocLength = nNewAlloc;
224 : }
225 :
226 85529 : nLength = nNewLength;
227 :
228 85529 : return true;
229 : }
230 :
231 : /************************************************************************/
232 : /* ==================================================================== */
233 : /* VSIMemHandle */
234 : /* ==================================================================== */
235 : /************************************************************************/
236 :
237 :
238 : /************************************************************************/
239 : /* Close() */
240 : /************************************************************************/
241 :
242 18985 : int VSIMemHandle::Close()
243 :
244 : {
245 18985 : if( --(poFile->nRefCount) == 0 )
246 8 : delete poFile;
247 :
248 18985 : poFile = NULL;
249 :
250 18985 : return 0;
251 : }
252 :
253 : /************************************************************************/
254 : /* Seek() */
255 : /************************************************************************/
256 :
257 70393 : int VSIMemHandle::Seek( vsi_l_offset nOffset, int nWhence )
258 :
259 : {
260 70393 : if( nWhence == SEEK_CUR )
261 100 : this->nOffset += nOffset;
262 70293 : else if( nWhence == SEEK_SET )
263 52389 : this->nOffset = nOffset;
264 17904 : else if( nWhence == SEEK_END )
265 17904 : this->nOffset = poFile->nLength + nOffset;
266 : else
267 : {
268 0 : errno = EINVAL;
269 0 : return -1;
270 : }
271 :
272 70393 : bEOF = FALSE;
273 :
274 70393 : if( this->nOffset > poFile->nLength )
275 : {
276 356 : if( !bUpdate ) // Read-only files cannot be extended by seek.
277 : {
278 : CPLDebug( "VSIMemHandle",
279 : "Attempt to extend read-only file '%s' to length %d from %d, .",
280 : poFile->osFilename.c_str(),
281 1 : (int) this->nOffset, (int) poFile->nLength );
282 :
283 1 : this->nOffset = poFile->nLength;
284 1 : errno = EACCES;
285 1 : return -1;
286 : }
287 : else // Writeable files are zero-extended by seek past end.
288 : {
289 355 : if( !poFile->SetLength( this->nOffset ) )
290 0 : return -1;
291 : }
292 : }
293 :
294 70392 : return 0;
295 : }
296 :
297 : /************************************************************************/
298 : /* Tell() */
299 : /************************************************************************/
300 :
301 26657 : vsi_l_offset VSIMemHandle::Tell()
302 :
303 : {
304 26657 : return nOffset;
305 : }
306 :
307 : /************************************************************************/
308 : /* Read() */
309 : /************************************************************************/
310 :
311 258783 : size_t VSIMemHandle::Read( void * pBuffer, size_t nSize, size_t nCount )
312 :
313 : {
314 : // FIXME: Integer overflow check should be placed here:
315 258783 : size_t nBytesToRead = nSize * nCount;
316 :
317 258783 : if( nBytesToRead + nOffset > poFile->nLength )
318 : {
319 899 : nBytesToRead = (size_t)(poFile->nLength - nOffset);
320 899 : nCount = nBytesToRead / nSize;
321 899 : bEOF = TRUE;
322 : }
323 :
324 258783 : memcpy( pBuffer, poFile->pabyData + nOffset, (size_t)nBytesToRead );
325 258783 : nOffset += nBytesToRead;
326 :
327 258783 : return nCount;
328 : }
329 :
330 : /************************************************************************/
331 : /* Write() */
332 : /************************************************************************/
333 :
334 96256 : size_t VSIMemHandle::Write( const void * pBuffer, size_t nSize, size_t nCount )
335 :
336 : {
337 96256 : if( !bUpdate )
338 : {
339 0 : errno = EACCES;
340 0 : return 0;
341 : }
342 :
343 : // FIXME: Integer overflow check should be placed here:
344 96256 : size_t nBytesToWrite = nSize * nCount;
345 :
346 96256 : if( nBytesToWrite + nOffset > poFile->nLength )
347 : {
348 85084 : if( !poFile->SetLength( nBytesToWrite + nOffset ) )
349 0 : return 0;
350 : }
351 :
352 96256 : memcpy( poFile->pabyData + nOffset, pBuffer, nBytesToWrite );
353 96256 : nOffset += nBytesToWrite;
354 :
355 96256 : return nCount;
356 : }
357 :
358 : /************************************************************************/
359 : /* Eof() */
360 : /************************************************************************/
361 :
362 7060 : int VSIMemHandle::Eof()
363 :
364 : {
365 7060 : return bEOF;
366 : }
367 :
368 : /************************************************************************/
369 : /* Truncate() */
370 : /************************************************************************/
371 :
372 47 : int VSIMemHandle::Truncate( vsi_l_offset nNewSize )
373 : {
374 47 : if( !bUpdate )
375 : {
376 0 : errno = EACCES;
377 0 : return -1;
378 : }
379 :
380 47 : if (poFile->SetLength( nNewSize ))
381 47 : return 0;
382 : else
383 0 : return -1;
384 : }
385 :
386 : /************************************************************************/
387 : /* ==================================================================== */
388 : /* VSIMemFilesystemHandler */
389 : /* ==================================================================== */
390 : /************************************************************************/
391 :
392 : /************************************************************************/
393 : /* VSIMemFilesystemHandler() */
394 : /************************************************************************/
395 :
396 647 : VSIMemFilesystemHandler::VSIMemFilesystemHandler()
397 :
398 : {
399 647 : hMutex = NULL;
400 647 : }
401 :
402 : /************************************************************************/
403 : /* ~VSIMemFilesystemHandler() */
404 : /************************************************************************/
405 :
406 628 : VSIMemFilesystemHandler::~VSIMemFilesystemHandler()
407 :
408 : {
409 628 : std::map<CPLString,VSIMemFile*>::const_iterator iter;
410 :
411 638 : for( iter = oFileList.begin(); iter != oFileList.end(); ++iter )
412 : {
413 10 : iter->second->nRefCount--;
414 10 : delete iter->second;
415 : }
416 :
417 628 : if( hMutex != NULL )
418 11 : CPLDestroyMutex( hMutex );
419 628 : hMutex = NULL;
420 628 : }
421 :
422 : /************************************************************************/
423 : /* Open() */
424 : /************************************************************************/
425 :
426 : VSIVirtualHandle *
427 29542 : VSIMemFilesystemHandler::Open( const char *pszFilename,
428 : const char *pszAccess )
429 :
430 : {
431 29542 : CPLMutexHolder oHolder( &hMutex );
432 : VSIMemFile *poFile;
433 29542 : CPLString osFilename = pszFilename;
434 29542 : NormalizePath( osFilename );
435 :
436 : /* -------------------------------------------------------------------- */
437 : /* Get the filename we are opening, create if needed. */
438 : /* -------------------------------------------------------------------- */
439 29542 : if( oFileList.find(osFilename) == oFileList.end() )
440 14127 : poFile = NULL;
441 : else
442 15415 : poFile = oFileList[osFilename];
443 :
444 29542 : if( strstr(pszAccess,"w") == NULL && poFile == NULL )
445 : {
446 10554 : errno = ENOENT;
447 10554 : return NULL;
448 : }
449 :
450 18988 : if( strstr(pszAccess,"w") )
451 : {
452 3616 : if( poFile )
453 43 : poFile->SetLength( 0 );
454 : else
455 : {
456 3573 : poFile = new VSIMemFile;
457 3573 : poFile->osFilename = osFilename;
458 3573 : oFileList[poFile->osFilename] = poFile;
459 3573 : poFile->nRefCount++; // for file list
460 : }
461 : }
462 :
463 18988 : if( poFile->bIsDirectory )
464 : {
465 3 : errno = EISDIR;
466 3 : return NULL;
467 : }
468 :
469 : /* -------------------------------------------------------------------- */
470 : /* Setup the file handle on this file. */
471 : /* -------------------------------------------------------------------- */
472 18985 : VSIMemHandle *poHandle = new VSIMemHandle;
473 :
474 18985 : poHandle->poFile = poFile;
475 18985 : poHandle->nOffset = 0;
476 18985 : poHandle->bEOF = FALSE;
477 45881 : if( strstr(pszAccess,"w") || strstr(pszAccess,"+")
478 : || strstr(pszAccess,"a") )
479 11527 : poHandle->bUpdate = TRUE;
480 : else
481 7458 : poHandle->bUpdate = FALSE;
482 :
483 18985 : poFile->nRefCount++;
484 :
485 18985 : if( strstr(pszAccess,"a") )
486 0 : poHandle->nOffset = poFile->nLength;
487 :
488 18985 : return poHandle;
489 : }
490 :
491 : /************************************************************************/
492 : /* Stat() */
493 : /************************************************************************/
494 :
495 3661 : int VSIMemFilesystemHandler::Stat( const char * pszFilename,
496 : VSIStatBufL * pStatBuf,
497 : int nFlags )
498 :
499 : {
500 : (void) nFlags;
501 :
502 3661 : CPLMutexHolder oHolder( &hMutex );
503 :
504 3661 : CPLString osFilename = pszFilename;
505 3661 : NormalizePath( osFilename );
506 :
507 3661 : memset( pStatBuf, 0, sizeof(VSIStatBufL) );
508 :
509 3661 : if ( osFilename == "/vsimem/" )
510 : {
511 1 : pStatBuf->st_size = 0;
512 1 : pStatBuf->st_mode = S_IFDIR;
513 1 : return 0;
514 : }
515 :
516 3660 : if( oFileList.find(osFilename) == oFileList.end() )
517 : {
518 2573 : errno = ENOENT;
519 2573 : return -1;
520 : }
521 :
522 1087 : VSIMemFile *poFile = oFileList[osFilename];
523 :
524 1087 : memset( pStatBuf, 0, sizeof(VSIStatBufL) );
525 :
526 1087 : if( poFile->bIsDirectory )
527 : {
528 25 : pStatBuf->st_size = 0;
529 25 : pStatBuf->st_mode = S_IFDIR;
530 : }
531 : else
532 : {
533 1062 : pStatBuf->st_size = (long)poFile->nLength;
534 1062 : pStatBuf->st_mode = S_IFREG;
535 : }
536 :
537 1087 : return 0;
538 : }
539 :
540 : /************************************************************************/
541 : /* Unlink() */
542 : /************************************************************************/
543 :
544 6041 : int VSIMemFilesystemHandler::Unlink( const char * pszFilename )
545 :
546 : {
547 6041 : CPLMutexHolder oHolder( &hMutex );
548 :
549 6041 : CPLString osFilename = pszFilename;
550 6041 : NormalizePath( osFilename );
551 :
552 : VSIMemFile *poFile;
553 :
554 6041 : if( oFileList.find(osFilename) == oFileList.end() )
555 : {
556 2319 : errno = ENOENT;
557 2319 : return -1;
558 : }
559 : else
560 : {
561 3722 : poFile = oFileList[osFilename];
562 :
563 3722 : if( --(poFile->nRefCount) == 0 )
564 3714 : delete poFile;
565 :
566 3722 : oFileList.erase( oFileList.find(osFilename) );
567 :
568 3722 : return 0;
569 0 : }
570 : }
571 :
572 : /************************************************************************/
573 : /* Mkdir() */
574 : /************************************************************************/
575 :
576 28 : int VSIMemFilesystemHandler::Mkdir( const char * pszPathname,
577 : long nMode )
578 :
579 : {
580 : (void) nMode;
581 :
582 28 : CPLMutexHolder oHolder( &hMutex );
583 :
584 28 : CPLString osPathname = pszPathname;
585 :
586 28 : NormalizePath( osPathname );
587 :
588 28 : if( oFileList.find(osPathname) != oFileList.end() )
589 : {
590 5 : errno = EEXIST;
591 5 : return -1;
592 : }
593 :
594 23 : VSIMemFile *poFile = new VSIMemFile;
595 :
596 23 : poFile->osFilename = osPathname;
597 23 : poFile->bIsDirectory = TRUE;
598 23 : oFileList[osPathname] = poFile;
599 23 : poFile->nRefCount++; /* referenced by file list */
600 :
601 23 : return 0;
602 : }
603 :
604 : /************************************************************************/
605 : /* Rmdir() */
606 : /************************************************************************/
607 :
608 129 : int VSIMemFilesystemHandler::Rmdir( const char * pszPathname )
609 :
610 : {
611 129 : CPLMutexHolder oHolder( &hMutex );
612 :
613 129 : return Unlink( pszPathname );
614 : }
615 :
616 : /************************************************************************/
617 : /* ReadDir() */
618 : /************************************************************************/
619 :
620 541 : char **VSIMemFilesystemHandler::ReadDir( const char *pszPath )
621 :
622 : {
623 541 : CPLMutexHolder oHolder( &hMutex );
624 :
625 541 : CPLString osPath = pszPath;
626 :
627 541 : NormalizePath( osPath );
628 :
629 541 : std::map<CPLString,VSIMemFile*>::const_iterator iter;
630 541 : char **papszDir = NULL;
631 541 : int nPathLen = strlen(osPath);
632 :
633 541 : if( osPath[nPathLen-1] == '/' )
634 1 : nPathLen--;
635 :
636 : /* In case of really big number of files in the directory, CSLAddString */
637 : /* can be slow (see #2158). We then directly build the list. */
638 541 : int nItems=0;
639 541 : int nAllocatedItems=0;
640 :
641 20655 : for( iter = oFileList.begin(); iter != oFileList.end(); ++iter )
642 : {
643 20114 : const char *pszFilePath = iter->second->osFilename.c_str();
644 31868 : if( EQUALN(osPath,pszFilePath,nPathLen)
645 11754 : && pszFilePath[nPathLen] == '/'
646 : && strstr(pszFilePath+nPathLen+1,"/") == NULL )
647 : {
648 11032 : if (nItems == 0)
649 : {
650 455 : papszDir = (char**) CPLCalloc(2,sizeof(char*));
651 455 : nAllocatedItems = 1;
652 : }
653 10577 : else if (nItems >= nAllocatedItems)
654 : {
655 943 : nAllocatedItems = nAllocatedItems * 2;
656 : papszDir = (char**)CPLRealloc(papszDir,
657 943 : (nAllocatedItems+2)*sizeof(char*));
658 : }
659 :
660 11032 : papszDir[nItems] = CPLStrdup(pszFilePath+nPathLen+1);
661 11032 : papszDir[nItems+1] = NULL;
662 :
663 11032 : nItems++;
664 : }
665 : }
666 :
667 541 : return papszDir;
668 : }
669 :
670 : /************************************************************************/
671 : /* Rename() */
672 : /************************************************************************/
673 :
674 0 : int VSIMemFilesystemHandler::Rename( const char *pszOldPath,
675 : const char *pszNewPath )
676 :
677 : {
678 0 : CPLMutexHolder oHolder( &hMutex );
679 :
680 0 : CPLString osOldPath = pszOldPath;
681 0 : CPLString osNewPath = pszNewPath;
682 :
683 0 : NormalizePath( osOldPath );
684 0 : NormalizePath( osNewPath );
685 :
686 0 : if ( osOldPath.compare(osNewPath) == 0 )
687 0 : return 0;
688 :
689 0 : if( oFileList.find(osOldPath) == oFileList.end() )
690 : {
691 0 : errno = ENOENT;
692 0 : return -1;
693 : }
694 : else
695 : {
696 0 : VSIMemFile* poFile = oFileList[osOldPath];
697 :
698 0 : oFileList.erase( oFileList.find(osOldPath) );
699 :
700 0 : Unlink(osNewPath);
701 :
702 0 : oFileList[osNewPath] = poFile;
703 0 : poFile->osFilename = osNewPath;
704 :
705 0 : return 0;
706 0 : }
707 : }
708 :
709 : /************************************************************************/
710 : /* NormalizePath() */
711 : /************************************************************************/
712 :
713 40033 : void VSIMemFilesystemHandler::NormalizePath( CPLString &oPath )
714 :
715 : {
716 40033 : int i, nSize = oPath.size();
717 :
718 1271165 : for( i = 0; i < nSize; i++ )
719 : {
720 1231132 : if( oPath[i] == '\\' )
721 3 : oPath[i] = '/';
722 : }
723 :
724 40033 : }
725 :
726 : /************************************************************************/
727 : /* VSIInstallLargeFileHandler() */
728 : /************************************************************************/
729 :
730 : /**
731 : * \brief Install "memory" file system handler.
732 : *
733 : * A special file handler is installed that allows block of memory to be
734 : * treated as files. All portions of the file system underneath the base
735 : * path "/vsimem/" will be handled by this driver.
736 : *
737 : * Normal VSI*L functions can be used freely to create and destroy memory
738 : * arrays treating them as if they were real file system objects. Some
739 : * additional methods exist to efficient create memory file system objects
740 : * without duplicating original copies of the data or to "steal" the block
741 : * of memory associated with a memory file.
742 : *
743 : * At this time the memory handler does not properly handle directory
744 : * semantics for the memory portion of the filesystem. The VSIReadDir()
745 : * function is not supported though this will be corrected in the future.
746 : *
747 : * Calling this function repeatedly should do no harm, though it is not
748 : * necessary. It is already called the first time a virtualizable
749 : * file access function (ie. VSIFOpenL(), VSIMkDir(), etc) is called.
750 : *
751 : * This code example demonstrates using GDAL to translate from one memory
752 : * buffer to another.
753 : *
754 : * \code
755 : * GByte *ConvertBufferFormat( GByte *pabyInData, vsi_l_offset nInDataLength,
756 : * vsi_l_offset *pnOutDataLength )
757 : * {
758 : * // create memory file system object from buffer.
759 : * VSIFCloseL( VSIFileFromMemBuffer( "/vsimem/work.dat", pabyInData,
760 : * nInDataLength, FALSE ) );
761 : *
762 : * // Open memory buffer for read.
763 : * GDALDatasetH hDS = GDALOpen( "/vsimem/work.dat", GA_ReadOnly );
764 : *
765 : * // Get output format driver.
766 : * GDALDriverH hDriver = GDALGetDriverByName( "GTiff" );
767 : * GDALDatasetH hOutDS;
768 : *
769 : * hOutDS = GDALCreateCopy( hDriver, "/vsimem/out.tif", hDS, TRUE, NULL,
770 : * NULL, NULL );
771 : *
772 : * // close source file, and "unlink" it.
773 : * GDALClose( hDS );
774 : * VSIUnlink( "/vsimem/work.dat" );
775 : *
776 : * // seize the buffer associated with the output file.
777 : *
778 : * return VSIGetMemFileBuffer( "/vsimem/out.tif", pnOutDataLength, TRUE );
779 : * }
780 : * \endcode
781 : */
782 :
783 647 : void VSIInstallMemFileHandler()
784 : {
785 647 : VSIFileManager::InstallHandler( "/vsimem/", new VSIMemFilesystemHandler );
786 647 : }
787 :
788 : /************************************************************************/
789 : /* VSIFileFromMemBuffer() */
790 : /************************************************************************/
791 :
792 : /**
793 : * \brief Create memory "file" from a buffer.
794 : *
795 : * A virtual memory file is created from the passed buffer with the indicated
796 : * filename. Under normal conditions the filename would need to be absolute
797 : * and within the /vsimem/ portion of the filesystem.
798 : *
799 : * If bTakeOwnership is TRUE, then the memory file system handler will take
800 : * ownership of the buffer, freeing it when the file is deleted. Otherwise
801 : * it remains the responsibility of the caller, but should not be freed as
802 : * long as it might be accessed as a file. In no circumstances does this
803 : * function take a copy of the pabyData contents.
804 : *
805 : * @param pszFilename the filename to be created.
806 : * @param pabyData the data buffer for the file.
807 : * @param nDataLength the length of buffer in bytes.
808 : * @param bTakeOwnership TRUE to transfer "ownership" of buffer or FALSE.
809 : *
810 : * @return open file handle on created file (see VSIFOpenL()).
811 : */
812 :
813 168 : VSILFILE *VSIFileFromMemBuffer( const char *pszFilename,
814 : GByte *pabyData,
815 : vsi_l_offset nDataLength,
816 : int bTakeOwnership )
817 :
818 : {
819 168 : if( VSIFileManager::GetHandler("")
820 : == VSIFileManager::GetHandler("/vsimem/") )
821 0 : VSIInstallMemFileHandler();
822 :
823 : VSIMemFilesystemHandler *poHandler = (VSIMemFilesystemHandler *)
824 168 : VSIFileManager::GetHandler("/vsimem/");
825 :
826 168 : if (pszFilename == NULL)
827 0 : return NULL;
828 :
829 168 : CPLString osFilename = pszFilename;
830 168 : VSIMemFilesystemHandler::NormalizePath( osFilename );
831 :
832 168 : VSIMemFile *poFile = new VSIMemFile;
833 :
834 168 : poFile->osFilename = osFilename;
835 168 : poFile->bOwnData = bTakeOwnership;
836 168 : poFile->pabyData = pabyData;
837 168 : poFile->nLength = nDataLength;
838 168 : poFile->nAllocLength = nDataLength;
839 :
840 : {
841 168 : CPLMutexHolder oHolder( &poHandler->hMutex );
842 168 : poHandler->Unlink(osFilename);
843 168 : poHandler->oFileList[poFile->osFilename] = poFile;
844 168 : poFile->nRefCount++;
845 : }
846 :
847 168 : return (VSILFILE *) poHandler->Open( osFilename, "r+" );
848 : }
849 :
850 : /************************************************************************/
851 : /* VSIGetMemFileBuffer() */
852 : /************************************************************************/
853 :
854 : /**
855 : * \brief Fetch buffer underlying memory file.
856 : *
857 : * This function returns a pointer to the memory buffer underlying a
858 : * virtual "in memory" file. If bUnlinkAndSeize is TRUE the filesystem
859 : * object will be deleted, and ownership of the buffer will pass to the
860 : * caller otherwise the underlying file will remain in existance.
861 : *
862 : * @param pszFilename the name of the file to grab the buffer of.
863 : * @param pnDataLength (file) length returned in this variable.
864 : * @param bUnlinkAndSeize TRUE to remove the file, or FALSE to leave unaltered.
865 : *
866 : * @return pointer to memory buffer or NULL on failure.
867 : */
868 :
869 52 : GByte *VSIGetMemFileBuffer( const char *pszFilename,
870 : vsi_l_offset *pnDataLength,
871 : int bUnlinkAndSeize )
872 :
873 : {
874 : VSIMemFilesystemHandler *poHandler = (VSIMemFilesystemHandler *)
875 52 : VSIFileManager::GetHandler("/vsimem/");
876 :
877 52 : if (pszFilename == NULL)
878 0 : return NULL;
879 :
880 52 : CPLString osFilename = pszFilename;
881 52 : VSIMemFilesystemHandler::NormalizePath( osFilename );
882 :
883 52 : CPLMutexHolder oHolder( &poHandler->hMutex );
884 :
885 52 : if( poHandler->oFileList.find(osFilename) == poHandler->oFileList.end() )
886 0 : return NULL;
887 :
888 52 : VSIMemFile *poFile = poHandler->oFileList[osFilename];
889 : GByte *pabyData;
890 :
891 52 : pabyData = poFile->pabyData;
892 52 : if( pnDataLength != NULL )
893 52 : *pnDataLength = poFile->nLength;
894 :
895 52 : if( bUnlinkAndSeize )
896 : {
897 32 : if( !poFile->bOwnData )
898 : CPLDebug( "VSIMemFile",
899 0 : "File doesn't own data in VSIGetMemFileBuffer!" );
900 : else
901 32 : poFile->bOwnData = FALSE;
902 :
903 32 : poHandler->oFileList.erase( poHandler->oFileList.find(osFilename) );
904 32 : --(poFile->nRefCount);
905 32 : delete poFile;
906 : }
907 :
908 52 : return pabyData;
909 : }
910 :
|