Line data Source code
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 10 : uprv_mapFile(UDataMemory *pData, const char *path) {
195 : int fd;
196 : int length;
197 : struct stat mystat;
198 : void *data;
199 :
200 10 : UDataMemory_init(pData); /* Clear the output struct. */
201 :
202 : /* determine the length of the file */
203 10 : if(stat(path, &mystat)!=0 || mystat.st_size<=0) {
204 7 : return FALSE;
205 : }
206 3 : length=mystat.st_size;
207 :
208 : /* open the file */
209 3 : fd=open(path, O_RDONLY);
210 3 : if(fd==-1) {
211 0 : return FALSE;
212 : }
213 :
214 : /* get a view of the mapping */
215 : #if U_PLATFORM != U_PF_HPUX
216 3 : 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 3 : close(fd); /* no longer needed */
221 3 : if(data==MAP_FAILED) {
222 0 : return FALSE;
223 : }
224 :
225 3 : pData->map = (char *)data + length;
226 3 : pData->pHeader=(const DataHeader *)data;
227 3 : pData->mapAddr = data;
228 : #if U_PLATFORM == U_PF_IPHONE
229 : posix_madvise(data, length, POSIX_MADV_RANDOM);
230 : #endif
231 3 : return TRUE;
232 : }
233 :
234 : U_CFUNC void
235 0 : uprv_unmapFile(UDataMemory *pData) {
236 0 : 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 : }
240 0 : pData->pHeader=NULL;
241 0 : pData->map=0;
242 0 : pData->mapAddr=NULL;
243 : }
244 0 : }
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
|