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