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