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