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