michael@0: /* michael@0: ****************************************************************************** michael@0: * michael@0: * Copyright (C) 1999-2013, International Business Machines michael@0: * Corporation and others. All Rights Reserved. michael@0: * michael@0: ******************************************************************************/ michael@0: michael@0: michael@0: /*---------------------------------------------------------------------------- michael@0: * michael@0: * Memory mapped file wrappers for use by the ICU Data Implementation michael@0: * All of the platform-specific implementation for mapping data files michael@0: * is here. The rest of the ICU Data implementation uses only the michael@0: * wrapper functions. michael@0: * michael@0: *----------------------------------------------------------------------------*/ michael@0: /* Defines _XOPEN_SOURCE for access to POSIX functions. michael@0: * Must be before any other #includes. */ michael@0: #include "uposixdefs.h" michael@0: michael@0: #include "unicode/putil.h" michael@0: #include "udatamem.h" michael@0: #include "umapfile.h" michael@0: michael@0: /* memory-mapping base definitions ------------------------------------------ */ michael@0: michael@0: #if MAP_IMPLEMENTATION==MAP_WIN32 michael@0: # define WIN32_LEAN_AND_MEAN michael@0: # define VC_EXTRALEAN michael@0: # define NOUSER michael@0: # define NOSERVICE michael@0: # define NOIME michael@0: # define NOMCX michael@0: # include michael@0: # include "cmemory.h" michael@0: michael@0: typedef HANDLE MemoryMap; michael@0: michael@0: # define IS_MAP(map) ((map)!=NULL) michael@0: #elif MAP_IMPLEMENTATION==MAP_POSIX || MAP_IMPLEMENTATION==MAP_390DLL michael@0: typedef size_t MemoryMap; michael@0: michael@0: # define IS_MAP(map) ((map)!=0) michael@0: michael@0: # include michael@0: # include michael@0: # include michael@0: # include michael@0: michael@0: # ifndef MAP_FAILED michael@0: # define MAP_FAILED ((void*)-1) michael@0: # endif michael@0: michael@0: # if MAP_IMPLEMENTATION==MAP_390DLL michael@0: /* No memory mapping for 390 batch mode. Fake it using dll loading. */ michael@0: # include michael@0: # include "cstring.h" michael@0: # include "cmemory.h" michael@0: # include "unicode/udata.h" michael@0: # define LIB_PREFIX "lib" michael@0: # define LIB_SUFFIX ".dll" michael@0: /* This is inconvienient until we figure out what to do with U_ICUDATA_NAME in utypes.h */ michael@0: # define U_ICUDATA_ENTRY_NAME "icudt" U_ICU_VERSION_SHORT U_LIB_SUFFIX_C_NAME_STRING "_dat" michael@0: # endif michael@0: #elif MAP_IMPLEMENTATION==MAP_STDIO michael@0: # include michael@0: # include "cmemory.h" michael@0: michael@0: typedef void *MemoryMap; michael@0: michael@0: # define IS_MAP(map) ((map)!=NULL) michael@0: #endif michael@0: michael@0: /*----------------------------------------------------------------------------* michael@0: * * michael@0: * Memory Mapped File support. Platform dependent implementation of * michael@0: * functions used by the rest of the implementation.* michael@0: * * michael@0: *----------------------------------------------------------------------------*/ michael@0: #if MAP_IMPLEMENTATION==MAP_NONE michael@0: U_CFUNC UBool michael@0: uprv_mapFile(UDataMemory *pData, const char *path) { michael@0: UDataMemory_init(pData); /* Clear the output struct. */ michael@0: return FALSE; /* no file access */ michael@0: } michael@0: michael@0: U_CFUNC void uprv_unmapFile(UDataMemory *pData) { michael@0: /* nothing to do */ michael@0: } michael@0: #elif MAP_IMPLEMENTATION==MAP_WIN32 michael@0: U_CFUNC UBool michael@0: uprv_mapFile( michael@0: UDataMemory *pData, /* Fill in with info on the result doing the mapping. */ michael@0: /* Output only; any original contents are cleared. */ michael@0: const char *path /* File path to be opened/mapped */ michael@0: ) michael@0: { michael@0: HANDLE map; michael@0: HANDLE file; michael@0: SECURITY_ATTRIBUTES mappingAttributes; michael@0: SECURITY_ATTRIBUTES *mappingAttributesPtr = NULL; michael@0: SECURITY_DESCRIPTOR securityDesc; michael@0: michael@0: UDataMemory_init(pData); /* Clear the output struct. */ michael@0: michael@0: /* open the input file */ michael@0: file=CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL, michael@0: OPEN_EXISTING, michael@0: FILE_ATTRIBUTE_NORMAL|FILE_FLAG_RANDOM_ACCESS, NULL); michael@0: if(file==INVALID_HANDLE_VALUE) { michael@0: return FALSE; michael@0: } michael@0: michael@0: /* Declare and initialize a security descriptor. michael@0: This is required for multiuser systems on Windows 2000 SP4 and beyond */ michael@0: if (InitializeSecurityDescriptor(&securityDesc, SECURITY_DESCRIPTOR_REVISION)) { michael@0: /* give the security descriptor a Null Dacl done using the "TRUE, (PACL)NULL" here */ michael@0: if (SetSecurityDescriptorDacl(&securityDesc, TRUE, (PACL)NULL, FALSE)) { michael@0: /* Make the security attributes point to the security descriptor */ michael@0: uprv_memset(&mappingAttributes, 0, sizeof(mappingAttributes)); michael@0: mappingAttributes.nLength = sizeof(mappingAttributes); michael@0: mappingAttributes.lpSecurityDescriptor = &securityDesc; michael@0: mappingAttributes.bInheritHandle = FALSE; /* object uninheritable */ michael@0: mappingAttributesPtr = &mappingAttributes; michael@0: } michael@0: } michael@0: /* else creating security descriptors can fail when we are on Windows 98, michael@0: and mappingAttributesPtr == NULL for that case. */ michael@0: michael@0: /* create an unnamed Windows file-mapping object for the specified file */ michael@0: map=CreateFileMapping(file, mappingAttributesPtr, PAGE_READONLY, 0, 0, NULL); michael@0: CloseHandle(file); michael@0: if(map==NULL) { michael@0: return FALSE; michael@0: } michael@0: michael@0: /* map a view of the file into our address space */ michael@0: pData->pHeader=(const DataHeader *)MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0); michael@0: if(pData->pHeader==NULL) { michael@0: CloseHandle(map); michael@0: return FALSE; michael@0: } michael@0: pData->map=map; michael@0: return TRUE; michael@0: } michael@0: michael@0: U_CFUNC void michael@0: uprv_unmapFile(UDataMemory *pData) { michael@0: if(pData!=NULL && pData->map!=NULL) { michael@0: UnmapViewOfFile(pData->pHeader); michael@0: CloseHandle(pData->map); michael@0: pData->pHeader=NULL; michael@0: pData->map=NULL; michael@0: } michael@0: } michael@0: michael@0: michael@0: michael@0: #elif MAP_IMPLEMENTATION==MAP_POSIX michael@0: U_CFUNC UBool michael@0: uprv_mapFile(UDataMemory *pData, const char *path) { michael@0: int fd; michael@0: int length; michael@0: struct stat mystat; michael@0: void *data; michael@0: michael@0: UDataMemory_init(pData); /* Clear the output struct. */ michael@0: michael@0: /* determine the length of the file */ michael@0: if(stat(path, &mystat)!=0 || mystat.st_size<=0) { michael@0: return FALSE; michael@0: } michael@0: length=mystat.st_size; michael@0: michael@0: /* open the file */ michael@0: fd=open(path, O_RDONLY); michael@0: if(fd==-1) { michael@0: return FALSE; michael@0: } michael@0: michael@0: /* get a view of the mapping */ michael@0: #if U_PLATFORM != U_PF_HPUX michael@0: data=mmap(0, length, PROT_READ, MAP_SHARED, fd, 0); michael@0: #else michael@0: data=mmap(0, length, PROT_READ, MAP_PRIVATE, fd, 0); michael@0: #endif michael@0: close(fd); /* no longer needed */ michael@0: if(data==MAP_FAILED) { michael@0: return FALSE; michael@0: } michael@0: michael@0: pData->map = (char *)data + length; michael@0: pData->pHeader=(const DataHeader *)data; michael@0: pData->mapAddr = data; michael@0: #if U_PLATFORM == U_PF_IPHONE michael@0: posix_madvise(data, length, POSIX_MADV_RANDOM); michael@0: #endif michael@0: return TRUE; michael@0: } michael@0: michael@0: U_CFUNC void michael@0: uprv_unmapFile(UDataMemory *pData) { michael@0: if(pData!=NULL && pData->map!=NULL) { michael@0: size_t dataLen = (char *)pData->map - (char *)pData->mapAddr; michael@0: if(munmap(pData->mapAddr, dataLen)==-1) { michael@0: } michael@0: pData->pHeader=NULL; michael@0: pData->map=0; michael@0: pData->mapAddr=NULL; michael@0: } michael@0: } michael@0: michael@0: michael@0: michael@0: #elif MAP_IMPLEMENTATION==MAP_STDIO michael@0: /* copy of the filestrm.c/T_FileStream_size() implementation */ michael@0: static int32_t michael@0: umap_fsize(FILE *f) { michael@0: int32_t savedPos = ftell(f); michael@0: int32_t size = 0; michael@0: michael@0: /*Changes by Bertrand A. D. doesn't affect the current position michael@0: goes to the end of the file before ftell*/ michael@0: fseek(f, 0, SEEK_END); michael@0: size = (int32_t)ftell(f); michael@0: fseek(f, savedPos, SEEK_SET); michael@0: return size; michael@0: } michael@0: michael@0: U_CFUNC UBool michael@0: uprv_mapFile(UDataMemory *pData, const char *path) { michael@0: FILE *file; michael@0: int32_t fileLength; michael@0: void *p; michael@0: michael@0: UDataMemory_init(pData); /* Clear the output struct. */ michael@0: /* open the input file */ michael@0: file=fopen(path, "rb"); michael@0: if(file==NULL) { michael@0: return FALSE; michael@0: } michael@0: michael@0: /* get the file length */ michael@0: fileLength=umap_fsize(file); michael@0: if(ferror(file) || fileLength<=20) { michael@0: fclose(file); michael@0: return FALSE; michael@0: } michael@0: michael@0: /* allocate the memory to hold the file data */ michael@0: p=uprv_malloc(fileLength); michael@0: if(p==NULL) { michael@0: fclose(file); michael@0: return FALSE; michael@0: } michael@0: michael@0: /* read the file */ michael@0: if(fileLength!=fread(p, 1, fileLength, file)) { michael@0: uprv_free(p); michael@0: fclose(file); michael@0: return FALSE; michael@0: } michael@0: michael@0: fclose(file); michael@0: pData->map=p; michael@0: pData->pHeader=(const DataHeader *)p; michael@0: pData->mapAddr=p; michael@0: return TRUE; michael@0: } michael@0: michael@0: U_CFUNC void michael@0: uprv_unmapFile(UDataMemory *pData) { michael@0: if(pData!=NULL && pData->map!=NULL) { michael@0: uprv_free(pData->map); michael@0: pData->map = NULL; michael@0: pData->mapAddr = NULL; michael@0: pData->pHeader = NULL; michael@0: } michael@0: } michael@0: michael@0: michael@0: #elif MAP_IMPLEMENTATION==MAP_390DLL michael@0: /* 390 specific Library Loading. michael@0: * This is the only platform left that dynamically loads an ICU Data Library. michael@0: * All other platforms use .data files when dynamic loading is required, but michael@0: * this turn out to be awkward to support in 390 batch mode. michael@0: * michael@0: * The idea here is to hide the fact that 390 is using dll loading from the michael@0: * rest of ICU, and make it look like there is file loading happening. michael@0: * michael@0: */ michael@0: michael@0: static char *strcpy_returnEnd(char *dest, const char *src) michael@0: { michael@0: while((*dest=*src)!=0) { michael@0: ++dest; michael@0: ++src; michael@0: } michael@0: return dest; michael@0: } michael@0: michael@0: /*------------------------------------------------------------------------------ michael@0: * michael@0: * computeDirPath given a user-supplied path of an item to be opened, michael@0: * compute and return michael@0: * - the full directory path to be used michael@0: * when opening the file. michael@0: * - Pointer to null at end of above returned path michael@0: * michael@0: * Parameters: michael@0: * path: input path. Buffer is not altered. michael@0: * pathBuffer: Output buffer. Any contents are overwritten. michael@0: * michael@0: * Returns: michael@0: * Pointer to null termination in returned pathBuffer. michael@0: * michael@0: * TODO: This works the way ICU historically has, but the michael@0: * whole data fallback search path is so complicated that michael@0: * proabably almost no one will ever really understand it, michael@0: * the potential for confusion is large. (It's not just michael@0: * this one function, but the whole scheme.) michael@0: * michael@0: *------------------------------------------------------------------------------*/ michael@0: static char *uprv_computeDirPath(const char *path, char *pathBuffer) michael@0: { michael@0: char *finalSlash; /* Ptr to last dir separator in input path, or null if none. */ michael@0: int32_t pathLen; /* Length of the returned directory path */ michael@0: michael@0: finalSlash = 0; michael@0: if (path != 0) { michael@0: finalSlash = uprv_strrchr(path, U_FILE_SEP_CHAR); michael@0: } michael@0: michael@0: *pathBuffer = 0; michael@0: if (finalSlash == 0) { michael@0: /* No user-supplied path. michael@0: * Copy the ICU_DATA path to the path buffer and return that*/ michael@0: const char *icuDataDir; michael@0: icuDataDir=u_getDataDirectory(); michael@0: if(icuDataDir!=NULL && *icuDataDir!=0) { michael@0: return strcpy_returnEnd(pathBuffer, icuDataDir); michael@0: } else { michael@0: /* there is no icuDataDir either. Just return the empty pathBuffer. */ michael@0: return pathBuffer; michael@0: } michael@0: } michael@0: michael@0: /* User supplied path did contain a directory portion. michael@0: * Copy it to the output path buffer */ michael@0: pathLen = (int32_t)(finalSlash - path + 1); michael@0: uprv_memcpy(pathBuffer, path, pathLen); michael@0: *(pathBuffer+pathLen) = 0; michael@0: return pathBuffer+pathLen; michael@0: } michael@0: michael@0: michael@0: # define DATA_TYPE "dat" michael@0: michael@0: U_CFUNC UBool uprv_mapFile(UDataMemory *pData, const char *path) { michael@0: const char *inBasename; michael@0: char *basename; michael@0: char pathBuffer[1024]; michael@0: const DataHeader *pHeader; michael@0: dllhandle *handle; michael@0: void *val=0; michael@0: michael@0: inBasename=uprv_strrchr(path, U_FILE_SEP_CHAR); michael@0: if(inBasename==NULL) { michael@0: inBasename = path; michael@0: } else { michael@0: inBasename++; michael@0: } michael@0: basename=uprv_computeDirPath(path, pathBuffer); michael@0: if(uprv_strcmp(inBasename, U_ICUDATA_NAME".dat") != 0) { michael@0: /* must mmap file... for build */ michael@0: int fd; michael@0: int length; michael@0: struct stat mystat; michael@0: void *data; michael@0: UDataMemory_init(pData); /* Clear the output struct. */ michael@0: michael@0: /* determine the length of the file */ michael@0: if(stat(path, &mystat)!=0 || mystat.st_size<=0) { michael@0: return FALSE; michael@0: } michael@0: length=mystat.st_size; michael@0: michael@0: /* open the file */ michael@0: fd=open(path, O_RDONLY); michael@0: if(fd==-1) { michael@0: return FALSE; michael@0: } michael@0: michael@0: /* get a view of the mapping */ michael@0: data=mmap(0, length, PROT_READ, MAP_PRIVATE, fd, 0); michael@0: close(fd); /* no longer needed */ michael@0: if(data==MAP_FAILED) { michael@0: return FALSE; michael@0: } michael@0: pData->map = (char *)data + length; michael@0: pData->pHeader=(const DataHeader *)data; michael@0: pData->mapAddr = data; michael@0: return TRUE; michael@0: } michael@0: michael@0: # ifdef OS390BATCH michael@0: /* ### hack: we still need to get u_getDataDirectory() fixed michael@0: for OS/390 (batch mode - always return "//"? ) michael@0: and this here straightened out with LIB_PREFIX and LIB_SUFFIX (both empty?!) michael@0: This is probably due to the strange file system on OS/390. It's more like michael@0: a database with short entry names than a typical file system. */ michael@0: /* U_ICUDATA_NAME should always have the correct name */ michael@0: /* BUT FOR BATCH MODE IT IS AN EXCEPTION BECAUSE */ michael@0: /* THE FIRST THREE LETTERS ARE PREASSIGNED TO THE */ michael@0: /* PROJECT!!!!! */ michael@0: uprv_strcpy(pathBuffer, "IXMI" U_ICU_VERSION_SHORT "DA"); michael@0: # else michael@0: /* set up the library name */ michael@0: uprv_strcpy(basename, LIB_PREFIX U_LIBICUDATA_NAME U_ICU_VERSION_SHORT LIB_SUFFIX); michael@0: # endif michael@0: michael@0: # ifdef UDATA_DEBUG michael@0: fprintf(stderr, "dllload: %s ", pathBuffer); michael@0: # endif michael@0: michael@0: handle=dllload(pathBuffer); michael@0: michael@0: # ifdef UDATA_DEBUG michael@0: fprintf(stderr, " -> %08X\n", handle ); michael@0: # endif michael@0: michael@0: if(handle != NULL) { michael@0: /* we have a data DLL - what kind of lookup do we need here? */ michael@0: /* try to find the Table of Contents */ michael@0: UDataMemory_init(pData); /* Clear the output struct. */ michael@0: val=dllqueryvar((dllhandle*)handle, U_ICUDATA_ENTRY_NAME); michael@0: if(val == 0) { michael@0: /* failed... so keep looking */ michael@0: return FALSE; michael@0: } michael@0: # ifdef UDATA_DEBUG michael@0: fprintf(stderr, "dllqueryvar(%08X, %s) -> %08X\n", handle, U_ICUDATA_ENTRY_NAME, val); michael@0: # endif michael@0: michael@0: pData->pHeader=(const DataHeader *)val; michael@0: return TRUE; michael@0: } else { michael@0: return FALSE; /* no handle */ michael@0: } michael@0: } michael@0: michael@0: U_CFUNC void uprv_unmapFile(UDataMemory *pData) { michael@0: if(pData!=NULL && pData->map!=NULL) { michael@0: uprv_free(pData->map); michael@0: pData->map = NULL; michael@0: pData->mapAddr = NULL; michael@0: pData->pHeader = NULL; michael@0: } michael@0: } michael@0: michael@0: #else michael@0: # error MAP_IMPLEMENTATION is set incorrectly michael@0: #endif