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