This source file includes following definitions.
- zend_win_error_message
- create_name_with_username
- get_mmap_base_file
- zend_shared_alloc_create_lock
- zend_shared_alloc_lock_win32
- zend_shared_alloc_unlock_win32
- zend_shared_alloc_reattach
- create_segments
- detach_segment
- segment_type_size
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 #include "ZendAccelerator.h"
23 #include "zend_shared_alloc.h"
24 #include "zend_accelerator_util_funcs.h"
25 #include <winbase.h>
26 #include <process.h>
27 #include <LMCONS.H>
28
29 #define ACCEL_FILEMAP_NAME "ZendOPcache.SharedMemoryArea"
30 #define ACCEL_MUTEX_NAME "ZendOPcache.SharedMemoryMutex"
31 #define ACCEL_FILEMAP_BASE_DEFAULT 0x01000000
32 #define ACCEL_FILEMAP_BASE "ZendOPcache.MemoryBase"
33 #define ACCEL_EVENT_SOURCE "Zend OPcache"
34
35 static HANDLE memfile = NULL, memory_mutex = NULL;
36 static void *mapping_base;
37
38 #define MAX_MAP_RETRIES 25
39
40 static void zend_win_error_message(int type, char *msg, int err)
41 {
42 LPVOID lpMsgBuf;
43 HANDLE h;
44 char *ev_msgs[2];
45
46 FormatMessage(
47 FORMAT_MESSAGE_ALLOCATE_BUFFER |
48 FORMAT_MESSAGE_FROM_SYSTEM |
49 FORMAT_MESSAGE_IGNORE_INSERTS,
50 NULL,
51 err,
52 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
53 (LPTSTR) &lpMsgBuf,
54 0,
55 NULL
56 );
57
58 h = RegisterEventSource(NULL, TEXT(ACCEL_EVENT_SOURCE));
59 ev_msgs[0] = msg;
60 ev_msgs[1] = lpMsgBuf;
61 ReportEvent(h,
62 EVENTLOG_ERROR_TYPE,
63 0,
64 err,
65 NULL,
66 2,
67 0,
68 ev_msgs,
69 NULL);
70 DeregisterEventSource(h);
71
72 LocalFree( lpMsgBuf );
73
74 zend_accel_error(type, msg);
75 }
76
77 static char *create_name_with_username(char *name)
78 {
79 static char newname[MAXPATHLEN + UNLEN + 4 + 1 + 32];
80 char uname[UNLEN + 1];
81 DWORD unsize = UNLEN;
82
83 GetUserName(uname, &unsize);
84 snprintf(newname, sizeof(newname) - 1, "%s@%s@%.32s", name, uname, ZCG(system_id));
85 return newname;
86 }
87
88 static char *get_mmap_base_file(void)
89 {
90 static char windir[MAXPATHLEN+UNLEN + 3 + sizeof("\\\\@") + 1 + 32];
91 char uname[UNLEN + 1];
92 DWORD unsize = UNLEN;
93 int l;
94
95 GetTempPath(MAXPATHLEN, windir);
96 GetUserName(uname, &unsize);
97 l = strlen(windir);
98 snprintf(windir + l, sizeof(windir) - l - 1, "\\%s@%s@%.32s", ACCEL_FILEMAP_BASE, uname, ZCG(system_id));
99 return windir;
100 }
101
102 void zend_shared_alloc_create_lock(void)
103 {
104 memory_mutex = CreateMutex(NULL, FALSE, create_name_with_username(ACCEL_MUTEX_NAME));
105 if (!memory_mutex) {
106 zend_accel_error(ACCEL_LOG_FATAL, "Cannot create mutex");
107 return;
108 }
109 ReleaseMutex(memory_mutex);
110 }
111
112 void zend_shared_alloc_lock_win32(void)
113 {
114 DWORD waitRes = WaitForSingleObject(memory_mutex, INFINITE);
115
116 if (waitRes == WAIT_FAILED) {
117 zend_accel_error(ACCEL_LOG_ERROR, "Cannot lock mutex");
118 }
119 }
120
121 void zend_shared_alloc_unlock_win32(void)
122 {
123 ReleaseMutex(memory_mutex);
124 }
125
126 static int zend_shared_alloc_reattach(size_t requested_size, char **error_in)
127 {
128 int err;
129 void *wanted_mapping_base;
130 char *mmap_base_file = get_mmap_base_file();
131 FILE *fp = fopen(mmap_base_file, "r");
132 MEMORY_BASIC_INFORMATION info;
133
134 err = GetLastError();
135 if (!fp) {
136 zend_win_error_message(ACCEL_LOG_WARNING, mmap_base_file, err);
137 zend_win_error_message(ACCEL_LOG_FATAL, "Unable to open base address file", err);
138 *error_in="fopen";
139 return ALLOC_FAILURE;
140 }
141 if (!fscanf(fp, "%p", &wanted_mapping_base)) {
142 err = GetLastError();
143 zend_win_error_message(ACCEL_LOG_FATAL, "Unable to read base address", err);
144 *error_in="read mapping base";
145 fclose(fp);
146 return ALLOC_FAILURE;
147 }
148 fclose(fp);
149
150 if (VirtualQuery(wanted_mapping_base, &info, sizeof(info)) == 0 ||
151 info.State != MEM_FREE ||
152 info.RegionSize < requested_size) {
153 #if ENABLE_FILE_CACHE_FALLBACK
154 if (ZCG(accel_directives).file_cache && ZCG(accel_directives).file_cache_fallback) {
155 size_t pre_size, wanted_mb_save;
156
157 wanted_mb_save = (size_t)wanted_mapping_base;
158
159 err = ERROR_INVALID_ADDRESS;
160 zend_win_error_message(ACCEL_LOG_WARNING, "Base address marks unusable memory region (fall-back to file cache)", err);
161
162 pre_size = ZEND_ALIGNED_SIZE(sizeof(zend_smm_shared_globals)) + ZEND_ALIGNED_SIZE(sizeof(zend_shared_segment)) + ZEND_ALIGNED_SIZE(sizeof(void *)) + ZEND_ALIGNED_SIZE(sizeof(int));
163
164 mapping_base = MapViewOfFileEx(memfile, FILE_MAP_ALL_ACCESS, 0, 0, pre_size + ZEND_ALIGNED_SIZE(sizeof(zend_accel_shared_globals)), NULL);
165 if (mapping_base == NULL) {
166 err = GetLastError();
167 zend_win_error_message(ACCEL_LOG_FATAL, "Unable to reattach to opcache shared globals", err);
168 return ALLOC_FAILURE;
169 }
170 accel_shared_globals = (zend_accel_shared_globals *)((char *)((zend_smm_shared_globals *)mapping_base)->app_shared_globals + ((char *)mapping_base - (char *)wanted_mb_save));
171
172
173 ZCG(accel_directives).file_cache_only = 1;
174
175 return ALLOC_FALLBACK;
176 }
177 #endif
178 err = ERROR_INVALID_ADDRESS;
179 zend_win_error_message(ACCEL_LOG_FATAL, "Base address marks unusable memory region. Please setup opcache.file_cache and opcache.file_cache_callback directives for more convenient Opcache usage", err);
180 return ALLOC_FAILURE;
181 }
182
183 mapping_base = MapViewOfFileEx(memfile, FILE_MAP_ALL_ACCESS, 0, 0, 0, wanted_mapping_base);
184 err = GetLastError();
185
186 if (mapping_base == NULL) {
187 if (err == ERROR_INVALID_ADDRESS) {
188 zend_win_error_message(ACCEL_LOG_FATAL, "Unable to reattach to base address", err);
189 return ALLOC_FAILURE;
190 }
191 return ALLOC_FAIL_MAPPING;
192 }
193 smm_shared_globals = (zend_smm_shared_globals *) mapping_base;
194
195 return SUCCESSFULLY_REATTACHED;
196 }
197
198 static int create_segments(size_t requested_size, zend_shared_segment ***shared_segments_p, int *shared_segments_count, char **error_in)
199 {
200 int err, ret;
201 zend_shared_segment *shared_segment;
202 int map_retries = 0;
203 void *default_mapping_base_set[] = { 0, 0 };
204
205
206
207
208
209 #if defined(_WIN64)
210 void *vista_mapping_base_set[] = { (void *) 0x0000100000000000, (void *) 0x0000200000000000, (void *) 0x0000300000000000, (void *) 0x0000700000000000, 0 };
211 #else
212 void *vista_mapping_base_set[] = { (void *) 0x20000000, (void *) 0x21000000, (void *) 0x30000000, (void *) 0x31000000, (void *) 0x50000000, 0 };
213 #endif
214 void **wanted_mapping_base = default_mapping_base_set;
215
216 zend_shared_alloc_lock_win32();
217
218
219
220 do {
221 memfile = OpenFileMapping(FILE_MAP_WRITE, 0, create_name_with_username(ACCEL_FILEMAP_NAME));
222 err = GetLastError();
223 if (memfile == NULL) {
224 break;
225 }
226
227 ret = zend_shared_alloc_reattach(requested_size, error_in);
228 err = GetLastError();
229 if (ret == ALLOC_FAIL_MAPPING) {
230
231 CloseHandle(memfile);
232 memfile = NULL;
233 if (++map_retries >= MAX_MAP_RETRIES) {
234 break;
235 }
236 zend_shared_alloc_unlock_win32();
237 Sleep(1000 * (map_retries + 1));
238 zend_shared_alloc_lock_win32();
239 } else {
240 zend_shared_alloc_unlock_win32();
241 return ret;
242 }
243 } while (1);
244
245 if (map_retries == MAX_MAP_RETRIES) {
246 zend_shared_alloc_unlock_win32();
247 zend_win_error_message(ACCEL_LOG_FATAL, "Unable to open file mapping", err);
248 *error_in = "OpenFileMapping";
249 return ALLOC_FAILURE;
250 }
251
252
253 *shared_segments_count = 1;
254 *shared_segments_p = (zend_shared_segment **) calloc(1, sizeof(zend_shared_segment)+sizeof(void *));
255 if (!*shared_segments_p) {
256 zend_shared_alloc_unlock_win32();
257 zend_win_error_message(ACCEL_LOG_FATAL, "calloc() failed", GetLastError());
258 *error_in = "calloc";
259 return ALLOC_FAILURE;
260 }
261 shared_segment = (zend_shared_segment *)((char *)(*shared_segments_p) + sizeof(void *));
262 (*shared_segments_p)[0] = shared_segment;
263
264 memfile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, requested_size,
265 create_name_with_username(ACCEL_FILEMAP_NAME));
266 err = GetLastError();
267 if (memfile == NULL) {
268 zend_shared_alloc_unlock_win32();
269 zend_win_error_message(ACCEL_LOG_FATAL, "Unable to create file mapping", err);
270 *error_in = "CreateFileMapping";
271 return ALLOC_FAILURE;
272 }
273
274
275
276
277 if (!ZCG(accel_directives).mmap_base || !*ZCG(accel_directives).mmap_base) {
278 wanted_mapping_base = vista_mapping_base_set;
279 } else {
280 char *s = ZCG(accel_directives).mmap_base;
281
282
283 if (*s == '0' && *(s + 1) == 'x') {
284 s += 2;
285 }
286 if (sscanf(s, "%p", &default_mapping_base_set[0]) != 1) {
287 zend_shared_alloc_unlock_win32();
288 zend_win_error_message(ACCEL_LOG_FATAL, "Bad mapping address specified in opcache.mmap_base", err);
289 return ALLOC_FAILURE;
290 }
291 }
292
293 do {
294 shared_segment->p = mapping_base = MapViewOfFileEx(memfile, FILE_MAP_ALL_ACCESS, 0, 0, 0, *wanted_mapping_base);
295 if (*wanted_mapping_base == NULL) {
296 break;
297 }
298 wanted_mapping_base++;
299 } while (!mapping_base);
300
301 err = GetLastError();
302 if (mapping_base == NULL) {
303 zend_shared_alloc_unlock_win32();
304 zend_win_error_message(ACCEL_LOG_FATAL, "Unable to create view for file mapping", err);
305 *error_in = "MapViewOfFile";
306 return ALLOC_FAILURE;
307 } else {
308 char *mmap_base_file = get_mmap_base_file();
309 FILE *fp = fopen(mmap_base_file, "w");
310 err = GetLastError();
311 if (!fp) {
312 zend_shared_alloc_unlock_win32();
313 zend_win_error_message(ACCEL_LOG_WARNING, mmap_base_file, err);
314 zend_win_error_message(ACCEL_LOG_FATAL, "Unable to write base address", err);
315 return ALLOC_FAILURE;
316 }
317 fprintf(fp, "%p\n", mapping_base);
318 fclose(fp);
319 }
320
321 shared_segment->pos = 0;
322 shared_segment->size = requested_size;
323
324 zend_shared_alloc_unlock_win32();
325
326 return ALLOC_SUCCESS;
327 }
328
329 static int detach_segment(zend_shared_segment *shared_segment)
330 {
331 zend_shared_alloc_lock_win32();
332 if (mapping_base) {
333 UnmapViewOfFile(mapping_base);
334 mapping_base = NULL;
335 }
336 CloseHandle(memfile);
337 memfile = NULL;
338 zend_shared_alloc_unlock_win32();
339 CloseHandle(memory_mutex);
340 memory_mutex = NULL;
341 return 0;
342 }
343
344 static size_t segment_type_size(void)
345 {
346 return sizeof(zend_shared_segment);
347 }
348
349 zend_shared_memory_handlers zend_alloc_win32_handlers = {
350 create_segments,
351 detach_segment,
352 segment_type_size
353 };