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