Home | History | Annotate | Download | only in common
      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