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