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