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