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 uprv_mapFile(UDataMemory * pData,const char * path)87 uprv_mapFile(UDataMemory *pData, const char *path) { 88 UDataMemory_init(pData); /* Clear the output struct. */ 89 return FALSE; /* no file access */ 90 } 91 uprv_unmapFile(UDataMemory * pData)92 U_CFUNC void uprv_unmapFile(UDataMemory *pData) { 93 /* nothing to do */ 94 } 95 #elif MAP_IMPLEMENTATION==MAP_WIN32 96 U_CFUNC UBool uprv_mapFile(UDataMemory * pData,const char * path)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 uprv_unmapFile(UDataMemory * pData)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 uprv_mapFile(UDataMemory * pData,const char * path)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 uprv_unmapFile(UDataMemory * pData)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 umap_fsize(FILE * f)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 uprv_mapFile(UDataMemory * pData,const char * path)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 uprv_unmapFile(UDataMemory * pData)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 strcpy_returnEnd(char * dest,const char * src)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 *------------------------------------------------------------------------------*/ uprv_computeDirPath(const char * path,char * pathBuffer)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 uprv_mapFile(UDataMemory * pData,const char * path)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 uprv_unmapFile(UDataMemory * pData)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