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
83    uprv_mapFile(UDataMemory *pData, const char *path) {
84        UDataMemory_init(pData); /* Clear the output struct. */
85        return FALSE;            /* no file access */
86    }
87
88    U_CFUNC void uprv_unmapFile(UDataMemory *pData) {
89        /* nothing to do */
90    }
91#elif MAP_IMPLEMENTATION==MAP_WIN32
92    U_CFUNC UBool
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
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
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
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
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
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
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
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     *------------------------------------------------------------------------------*/
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
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
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