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