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