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