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