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