• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 ******************************************************************************
5 *
6 *   Copyright (C) 1999-2013, International Business Machines
7 *   Corporation and others.  All Rights Reserved.
8 *
9 ******************************************************************************/
10 
11 
12 /*----------------------------------------------------------------------------
13  *
14  *       Memory mapped file wrappers for use by the ICU Data Implementation
15  *       All of the platform-specific implementation for mapping data files
16  *         is here.  The rest of the ICU Data implementation uses only the
17  *         wrapper functions.
18  *
19  *----------------------------------------------------------------------------*/
20 /* Defines _XOPEN_SOURCE for access to POSIX functions.
21  * Must be before any other #includes. */
22 #include "uposixdefs.h"
23 
24 #include "unicode/putil.h"
25 #include "unicode/ustring.h"
26 #include "udatamem.h"
27 #include "umapfile.h"
28 
29 /* memory-mapping base definitions ------------------------------------------ */
30 
31 #if MAP_IMPLEMENTATION==MAP_WIN32
32 #ifndef WIN32_LEAN_AND_MEAN
33 #   define WIN32_LEAN_AND_MEAN
34 #endif
35 #   define VC_EXTRALEAN
36 #   define NOUSER
37 #   define NOSERVICE
38 #   define NOIME
39 #   define NOMCX
40 #   include <windows.h>
41 #   include "cmemory.h"
42 
43     typedef HANDLE MemoryMap;
44 
45 #   define IS_MAP(map) ((map)!=NULL)
46 #elif MAP_IMPLEMENTATION==MAP_POSIX || MAP_IMPLEMENTATION==MAP_390DLL
47     typedef size_t MemoryMap;
48 
49 #   define IS_MAP(map) ((map)!=0)
50 
51 #   include <unistd.h>
52 #   include <sys/mman.h>
53 #   include <sys/stat.h>
54 #   include <fcntl.h>
55 
56 #   ifndef MAP_FAILED
57 #       define MAP_FAILED ((void*)-1)
58 #   endif
59 
60 #   if MAP_IMPLEMENTATION==MAP_390DLL
61         /*   No memory mapping for 390 batch mode.  Fake it using dll loading.  */
62 #       include <dll.h>
63 #       include "cstring.h"
64 #       include "cmemory.h"
65 #       include "unicode/udata.h"
66 #       define LIB_PREFIX "lib"
67 #       define LIB_SUFFIX ".dll"
68         /* This is inconvenient until we figure out what to do with U_ICUDATA_NAME in utypes.h */
69 #       define U_ICUDATA_ENTRY_NAME "icudt" U_ICU_VERSION_SHORT U_LIB_SUFFIX_C_NAME_STRING "_dat"
70 #   endif
71 #elif MAP_IMPLEMENTATION==MAP_STDIO
72 #   include <stdio.h>
73 #   include "cmemory.h"
74 
75     typedef void *MemoryMap;
76 
77 #   define IS_MAP(map) ((map)!=NULL)
78 #endif
79 
80 /*----------------------------------------------------------------------------*
81  *                                                                            *
82  *   Memory Mapped File support.  Platform dependent implementation of        *
83  *                           functions used by the rest of the implementation.*
84  *                                                                            *
85  *----------------------------------------------------------------------------*/
86 #if MAP_IMPLEMENTATION==MAP_NONE
87     U_CFUNC UBool
uprv_mapFile(UDataMemory * pData,const char * path,UErrorCode * status)88     uprv_mapFile(UDataMemory *pData, const char *path, UErrorCode *status) {
89         if (U_FAILURE(*status)) {
90             return FALSE;
91         }
92         UDataMemory_init(pData); /* Clear the output struct. */
93         return FALSE;            /* no file access */
94     }
95 
uprv_unmapFile(UDataMemory * pData)96     U_CFUNC void uprv_unmapFile(UDataMemory *pData) {
97         /* nothing to do */
98     }
99 #elif MAP_IMPLEMENTATION==MAP_WIN32
100     U_CFUNC UBool
uprv_mapFile(UDataMemory * pData,const char * path,UErrorCode * status)101     uprv_mapFile(
102          UDataMemory *pData,    /* Fill in with info on the result doing the mapping. */
103                                 /*   Output only; any original contents are cleared.  */
104          const char *path,      /* File path to be opened/mapped.                     */
105          UErrorCode *status     /* Error status, used to report out-of-memory errors. */
106          )
107     {
108         HANDLE map;
109         HANDLE file;
110 
111         if (U_FAILURE(*status)) {
112             return FALSE;
113         }
114 
115         UDataMemory_init(pData); /* Clear the output struct.        */
116 
117         /* open the input file */
118 #if U_PLATFORM_HAS_WINUWP_API == 0
119         file=CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL,
120             OPEN_EXISTING,
121             FILE_ATTRIBUTE_NORMAL|FILE_FLAG_RANDOM_ACCESS, NULL);
122 #else
123         // Convert from UTF-8 string to UTF-16 string.
124         wchar_t utf16Path[MAX_PATH];
125         int32_t pathUtf16Len = 0;
126         u_strFromUTF8(reinterpret_cast<UChar*>(utf16Path), static_cast<int32_t>(UPRV_LENGTHOF(utf16Path)), &pathUtf16Len, path, -1, status);
127 
128         if (U_FAILURE(*status)) {
129             return FALSE;
130         }
131         if (*status == U_STRING_NOT_TERMINATED_WARNING) {
132             // Report back an error instead of a warning.
133             *status = U_BUFFER_OVERFLOW_ERROR;
134             return FALSE;
135         }
136 
137         // TODO: Is it worth setting extended parameters to specify random access?
138         file = CreateFile2(utf16Path, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, NULL);
139 #endif
140         if (file == INVALID_HANDLE_VALUE) {
141             // If we failed to open the file due to an out-of-memory error, then we want
142             // to report that error back to the caller.
143             if (HRESULT_FROM_WIN32(GetLastError()) == E_OUTOFMEMORY) {
144                 *status = U_MEMORY_ALLOCATION_ERROR;
145             }
146             return FALSE;
147         }
148 
149         /* Declare and initialize a security descriptor.
150            This is required for multiuser systems on Windows 2000 SP4 and beyond */
151         // TODO: UWP does not have this function and I do not think it is required?
152 #if U_PLATFORM_HAS_WINUWP_API == 0
153 
154         SECURITY_ATTRIBUTES mappingAttributes;
155         SECURITY_ATTRIBUTES *mappingAttributesPtr = NULL;
156         SECURITY_DESCRIPTOR securityDesc;
157 
158         if (InitializeSecurityDescriptor(&securityDesc, SECURITY_DESCRIPTOR_REVISION)) {
159             /* give the security descriptor a Null Dacl done using the  "TRUE, (PACL)NULL" here */
160             if (SetSecurityDescriptorDacl(&securityDesc, TRUE, (PACL)NULL, FALSE)) {
161                 /* Make the security attributes point to the security descriptor */
162                 uprv_memset(&mappingAttributes, 0, sizeof(mappingAttributes));
163                 mappingAttributes.nLength = sizeof(mappingAttributes);
164                 mappingAttributes.lpSecurityDescriptor = &securityDesc;
165                 mappingAttributes.bInheritHandle = FALSE; /* object uninheritable */
166                 mappingAttributesPtr = &mappingAttributes;
167             }
168         }
169         /* else creating security descriptors can fail when we are on Windows 98,
170            and mappingAttributesPtr == NULL for that case. */
171 
172         /* create an unnamed Windows file-mapping object for the specified file */
173         map=CreateFileMapping(file, mappingAttributesPtr, PAGE_READONLY, 0, 0, NULL);
174 #else
175         map = CreateFileMappingFromApp(file, NULL, PAGE_READONLY, 0, NULL);
176 #endif
177         CloseHandle(file);
178         if (map == NULL) {
179             // If we failed to create the mapping due to an out-of-memory error, then
180             // we want to report that error back to the caller.
181             if (HRESULT_FROM_WIN32(GetLastError()) == E_OUTOFMEMORY) {
182                 *status = U_MEMORY_ALLOCATION_ERROR;
183             }
184             return FALSE;
185         }
186 
187         /* map a view of the file into our address space */
188         pData->pHeader=(const DataHeader *)MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
189         if(pData->pHeader==NULL) {
190             CloseHandle(map);
191             return FALSE;
192         }
193         pData->map=map;
194         return TRUE;
195     }
196 
197     U_CFUNC void
uprv_unmapFile(UDataMemory * pData)198     uprv_unmapFile(UDataMemory *pData) {
199         if(pData!=NULL && pData->map!=NULL) {
200             UnmapViewOfFile(pData->pHeader);
201             CloseHandle(pData->map);
202             pData->pHeader=NULL;
203             pData->map=NULL;
204         }
205     }
206 
207 
208 
209 #elif MAP_IMPLEMENTATION==MAP_POSIX
210     U_CFUNC UBool
uprv_mapFile(UDataMemory * pData,const char * path,UErrorCode * status)211     uprv_mapFile(UDataMemory *pData, const char *path, UErrorCode *status) {
212         int fd;
213         int length;
214         struct stat mystat;
215         void *data;
216 
217         if (U_FAILURE(*status)) {
218             return FALSE;
219         }
220 
221         UDataMemory_init(pData); /* Clear the output struct.        */
222 
223         /* determine the length of the file */
224         if(stat(path, &mystat)!=0 || mystat.st_size<=0) {
225             return FALSE;
226         }
227         length=mystat.st_size;
228 
229         /* open the file */
230         fd=open(path, O_RDONLY);
231         if(fd==-1) {
232             return FALSE;
233         }
234 
235         /* get a view of the mapping */
236 #if U_PLATFORM != U_PF_HPUX
237         data=mmap(0, length, PROT_READ, MAP_SHARED,  fd, 0);
238 #else
239         data=mmap(0, length, PROT_READ, MAP_PRIVATE, fd, 0);
240 #endif
241         close(fd); /* no longer needed */
242         if(data==MAP_FAILED) {
243             // Possibly check the errno value for ENOMEM, and report U_MEMORY_ALLOCATION_ERROR?
244             return FALSE;
245         }
246 
247         pData->map = (char *)data + length;
248         pData->pHeader=(const DataHeader *)data;
249         pData->mapAddr = data;
250 #if U_PLATFORM == U_PF_IPHONE
251         posix_madvise(data, length, POSIX_MADV_RANDOM);
252 #endif
253         return TRUE;
254     }
255 
256     U_CFUNC void
uprv_unmapFile(UDataMemory * pData)257     uprv_unmapFile(UDataMemory *pData) {
258         if(pData!=NULL && pData->map!=NULL) {
259             size_t dataLen = (char *)pData->map - (char *)pData->mapAddr;
260             if(munmap(pData->mapAddr, dataLen)==-1) {
261             }
262             pData->pHeader=NULL;
263             pData->map=0;
264             pData->mapAddr=NULL;
265         }
266     }
267 
268 
269 
270 #elif MAP_IMPLEMENTATION==MAP_STDIO
271     /* copy of the filestrm.c/T_FileStream_size() implementation */
272     static int32_t
umap_fsize(FILE * f)273     umap_fsize(FILE *f) {
274         int32_t savedPos = ftell(f);
275         int32_t size = 0;
276 
277         /*Changes by Bertrand A. D. doesn't affect the current position
278         goes to the end of the file before ftell*/
279         fseek(f, 0, SEEK_END);
280         size = (int32_t)ftell(f);
281         fseek(f, savedPos, SEEK_SET);
282         return size;
283     }
284 
285     U_CFUNC UBool
uprv_mapFile(UDataMemory * pData,const char * path,UErrorCode * status)286     uprv_mapFile(UDataMemory *pData, const char *path, UErrorCode *status) {
287         FILE *file;
288         int32_t fileLength;
289         void *p;
290 
291         if (U_FAILURE(*status)) {
292             return FALSE;
293         }
294 
295         UDataMemory_init(pData); /* Clear the output struct.        */
296         /* open the input file */
297         file=fopen(path, "rb");
298         if(file==NULL) {
299             return FALSE;
300         }
301 
302         /* get the file length */
303         fileLength=umap_fsize(file);
304         if(ferror(file) || fileLength<=20) {
305             fclose(file);
306             return FALSE;
307         }
308 
309         /* allocate the memory to hold the file data */
310         p=uprv_malloc(fileLength);
311         if(p==NULL) {
312             fclose(file);
313             *status = U_MEMORY_ALLOCATION_ERROR;
314             return FALSE;
315         }
316 
317         /* read the file */
318         if(fileLength!=fread(p, 1, fileLength, file)) {
319             uprv_free(p);
320             fclose(file);
321             return FALSE;
322         }
323 
324         fclose(file);
325         pData->map=p;
326         pData->pHeader=(const DataHeader *)p;
327         pData->mapAddr=p;
328         return TRUE;
329     }
330 
331     U_CFUNC void
uprv_unmapFile(UDataMemory * pData)332     uprv_unmapFile(UDataMemory *pData) {
333         if(pData!=NULL && pData->map!=NULL) {
334             uprv_free(pData->map);
335             pData->map     = NULL;
336             pData->mapAddr = NULL;
337             pData->pHeader = NULL;
338         }
339     }
340 
341 
342 #elif MAP_IMPLEMENTATION==MAP_390DLL
343     /*  390 specific Library Loading.
344      *  This is the only platform left that dynamically loads an ICU Data Library.
345      *  All other platforms use .data files when dynamic loading is required, but
346      *  this turn out to be awkward to support in 390 batch mode.
347      *
348      *  The idea here is to hide the fact that 390 is using dll loading from the
349      *   rest of ICU, and make it look like there is file loading happening.
350      *
351      */
352 
strcpy_returnEnd(char * dest,const char * src)353     static char *strcpy_returnEnd(char *dest, const char *src)
354     {
355         while((*dest=*src)!=0) {
356             ++dest;
357             ++src;
358         }
359         return dest;
360     }
361 
362     /*------------------------------------------------------------------------------
363      *
364      *  computeDirPath   given a user-supplied path of an item to be opened,
365      *                         compute and return
366      *                            - the full directory path to be used
367      *                              when opening the file.
368      *                            - Pointer to null at end of above returned path
369      *
370      *                       Parameters:
371      *                          path:        input path.  Buffer is not altered.
372      *                          pathBuffer:  Output buffer.  Any contents are overwritten.
373      *
374      *                       Returns:
375      *                          Pointer to null termination in returned pathBuffer.
376      *
377      *                    TODO:  This works the way ICU historically has, but the
378      *                           whole data fallback search path is so complicated that
379      *                           probably almost no one will ever really understand it,
380      *                           the potential for confusion is large.  (It's not just
381      *                           this one function, but the whole scheme.)
382      *
383      *------------------------------------------------------------------------------*/
uprv_computeDirPath(const char * path,char * pathBuffer)384     static char *uprv_computeDirPath(const char *path, char *pathBuffer)
385     {
386         char   *finalSlash;       /* Ptr to last dir separator in input path, or null if none. */
387         int32_t pathLen;          /* Length of the returned directory path                     */
388 
389         finalSlash = 0;
390         if (path != 0) {
391             finalSlash = uprv_strrchr(path, U_FILE_SEP_CHAR);
392         }
393 
394         *pathBuffer = 0;
395         if (finalSlash == 0) {
396         /* No user-supplied path.
397             * Copy the ICU_DATA path to the path buffer and return that*/
398             const char *icuDataDir;
399             icuDataDir=u_getDataDirectory();
400             if(icuDataDir!=NULL && *icuDataDir!=0) {
401                 return strcpy_returnEnd(pathBuffer, icuDataDir);
402             } else {
403                 /* there is no icuDataDir either.  Just return the empty pathBuffer. */
404                 return pathBuffer;
405             }
406         }
407 
408         /* User supplied path did contain a directory portion.
409         * Copy it to the output path buffer */
410         pathLen = (int32_t)(finalSlash - path + 1);
411         uprv_memcpy(pathBuffer, path, pathLen);
412         *(pathBuffer+pathLen) = 0;
413         return pathBuffer+pathLen;
414     }
415 
416 
417 #   define DATA_TYPE "dat"
418 
uprv_mapFile(UDataMemory * pData,const char * path,UErrorCode * status)419     U_CFUNC UBool uprv_mapFile(UDataMemory *pData, const char *path, UErrorCode *status) {
420         const char *inBasename;
421         char *basename;
422         char pathBuffer[1024];
423         const DataHeader *pHeader;
424         dllhandle *handle;
425         void *val=0;
426 
427         if (U_FAILURE(*status)) {
428             return FALSE;
429         }
430 
431         inBasename=uprv_strrchr(path, U_FILE_SEP_CHAR);
432         if(inBasename==NULL) {
433             inBasename = path;
434         } else {
435             inBasename++;
436         }
437         basename=uprv_computeDirPath(path, pathBuffer);
438         if(uprv_strcmp(inBasename, U_ICUDATA_NAME".dat") != 0) {
439             /* must mmap file... for build */
440             int fd;
441             int length;
442             struct stat mystat;
443             void *data;
444             UDataMemory_init(pData); /* Clear the output struct. */
445 
446             /* determine the length of the file */
447             if(stat(path, &mystat)!=0 || mystat.st_size<=0) {
448                 return FALSE;
449             }
450             length=mystat.st_size;
451 
452             /* open the file */
453             fd=open(path, O_RDONLY);
454             if(fd==-1) {
455                 return FALSE;
456             }
457 
458             /* get a view of the mapping */
459             data=mmap(0, length, PROT_READ, MAP_PRIVATE, fd, 0);
460             close(fd); /* no longer needed */
461             if(data==MAP_FAILED) {
462                 // Possibly check the errorno value for ENOMEM, and report U_MEMORY_ALLOCATION_ERROR?
463                 return FALSE;
464             }
465             pData->map = (char *)data + length;
466             pData->pHeader=(const DataHeader *)data;
467             pData->mapAddr = data;
468             return TRUE;
469         }
470 
471 #       ifdef OS390BATCH
472             /* ### hack: we still need to get u_getDataDirectory() fixed
473             for OS/390 (batch mode - always return "//"? )
474             and this here straightened out with LIB_PREFIX and LIB_SUFFIX (both empty?!)
475             This is probably due to the strange file system on OS/390.  It's more like
476             a database with short entry names than a typical file system. */
477             /* U_ICUDATA_NAME should always have the correct name */
478             /* BUT FOR BATCH MODE IT IS AN EXCEPTION BECAUSE */
479             /* THE FIRST THREE LETTERS ARE PREASSIGNED TO THE */
480             /* PROJECT!!!!! */
481             uprv_strcpy(pathBuffer, "IXMI" U_ICU_VERSION_SHORT "DA");
482 #       else
483             /* set up the library name */
484             uprv_strcpy(basename, LIB_PREFIX U_LIBICUDATA_NAME U_ICU_VERSION_SHORT LIB_SUFFIX);
485 #       endif
486 
487 #       ifdef UDATA_DEBUG
488              fprintf(stderr, "dllload: %s ", pathBuffer);
489 #       endif
490 
491         handle=dllload(pathBuffer);
492 
493 #       ifdef UDATA_DEBUG
494                fprintf(stderr, " -> %08X\n", handle );
495 #       endif
496 
497         if(handle != NULL) {
498                /* we have a data DLL - what kind of lookup do we need here? */
499                /* try to find the Table of Contents */
500                UDataMemory_init(pData); /* Clear the output struct.        */
501                val=dllqueryvar((dllhandle*)handle, U_ICUDATA_ENTRY_NAME);
502                if(val == 0) {
503                     /* failed... so keep looking */
504                     return FALSE;
505                }
506 #              ifdef UDATA_DEBUG
507                     fprintf(stderr, "dllqueryvar(%08X, %s) -> %08X\n", handle, U_ICUDATA_ENTRY_NAME, val);
508 #              endif
509 
510                pData->pHeader=(const DataHeader *)val;
511                return TRUE;
512          } else {
513                return FALSE; /* no handle */
514          }
515     }
516 
uprv_unmapFile(UDataMemory * pData)517     U_CFUNC void uprv_unmapFile(UDataMemory *pData) {
518         if(pData!=NULL && pData->map!=NULL) {
519             uprv_free(pData->map);
520             pData->map     = NULL;
521             pData->mapAddr = NULL;
522             pData->pHeader = NULL;
523         }
524     }
525 
526 #else
527 #   error MAP_IMPLEMENTATION is set incorrectly
528 #endif
529