1 | /*- |
---|
2 | * See the file LICENSE for redistribution information. |
---|
3 | * |
---|
4 | * Copyright (c) 1996-2002 |
---|
5 | * Sleepycat Software. All rights reserved. |
---|
6 | */ |
---|
7 | |
---|
8 | #include "db_config.h" |
---|
9 | |
---|
10 | #ifndef lint |
---|
11 | static const char revid[] = "Id: os_map.c,v 11.38 2002/09/10 02:35:48 bostic Exp "; |
---|
12 | #endif /* not lint */ |
---|
13 | |
---|
14 | #include "db_int.h" |
---|
15 | |
---|
16 | static int __os_map |
---|
17 | __P((DB_ENV *, char *, REGINFO *, DB_FH *, size_t, int, int, int, void **)); |
---|
18 | static int __os_unique_name __P((char *, HANDLE, char *, size_t)); |
---|
19 | |
---|
20 | /* |
---|
21 | * __os_r_sysattach -- |
---|
22 | * Create/join a shared memory region. |
---|
23 | */ |
---|
24 | int |
---|
25 | __os_r_sysattach(dbenv, infop, rp) |
---|
26 | DB_ENV *dbenv; |
---|
27 | REGINFO *infop; |
---|
28 | REGION *rp; |
---|
29 | { |
---|
30 | DB_FH fh; |
---|
31 | int is_system, ret; |
---|
32 | |
---|
33 | /* |
---|
34 | * Try to open/create the file. We DO NOT need to ensure that multiple |
---|
35 | * threads/processes attempting to simultaneously create the region are |
---|
36 | * properly ordered, our caller has already taken care of that. |
---|
37 | */ |
---|
38 | if ((ret = __os_open(dbenv, infop->name, |
---|
39 | DB_OSO_DIRECT | |
---|
40 | F_ISSET(infop, REGION_CREATE_OK) ? DB_OSO_CREATE: 0, |
---|
41 | infop->mode, &fh)) != 0) { |
---|
42 | __db_err(dbenv, "%s: %s", infop->name, db_strerror(ret)); |
---|
43 | return (ret); |
---|
44 | } |
---|
45 | |
---|
46 | /* |
---|
47 | * On Windows/9X, files that are opened by multiple processes do not |
---|
48 | * share data correctly. For this reason, the DB_SYSTEM_MEM flag is |
---|
49 | * implied for any application that does not specify the DB_PRIVATE |
---|
50 | * flag. |
---|
51 | */ |
---|
52 | is_system = F_ISSET(dbenv, DB_ENV_SYSTEM_MEM) || |
---|
53 | (!F_ISSET(dbenv, DB_ENV_PRIVATE) && __os_is_winnt() == 0); |
---|
54 | |
---|
55 | /* |
---|
56 | * Map the file in. If we're creating an in-system-memory region, |
---|
57 | * specify a segment ID (which is never used again) so that the |
---|
58 | * calling code writes out the REGENV_REF structure to the primary |
---|
59 | * environment file. |
---|
60 | */ |
---|
61 | ret = __os_map(dbenv, infop->name, infop, &fh, rp->size, |
---|
62 | 1, is_system, 0, &infop->addr); |
---|
63 | if (ret == 0 && is_system == 1) |
---|
64 | rp->segid = 1; |
---|
65 | |
---|
66 | (void)__os_closehandle(dbenv, &fh); |
---|
67 | |
---|
68 | return (ret); |
---|
69 | } |
---|
70 | |
---|
71 | /* |
---|
72 | * __os_r_sysdetach -- |
---|
73 | * Detach from a shared memory region. |
---|
74 | */ |
---|
75 | int |
---|
76 | __os_r_sysdetach(dbenv, infop, destroy) |
---|
77 | DB_ENV *dbenv; |
---|
78 | REGINFO *infop; |
---|
79 | int destroy; |
---|
80 | { |
---|
81 | int ret, t_ret; |
---|
82 | |
---|
83 | if (infop->wnt_handle != NULL) { |
---|
84 | (void)CloseHandle(*((HANDLE*)(infop->wnt_handle))); |
---|
85 | __os_free(dbenv, infop->wnt_handle); |
---|
86 | } |
---|
87 | |
---|
88 | ret = !UnmapViewOfFile(infop->addr) ? __os_win32_errno() : 0; |
---|
89 | if (ret != 0) |
---|
90 | __db_err(dbenv, "UnmapViewOfFile: %s", strerror(ret)); |
---|
91 | |
---|
92 | if (!F_ISSET(dbenv, DB_ENV_SYSTEM_MEM) && destroy) { |
---|
93 | if (F_ISSET(dbenv, DB_ENV_OVERWRITE)) |
---|
94 | (void)__db_overwrite(dbenv, infop->name); |
---|
95 | if ((t_ret = __os_unlink(dbenv, infop->name)) != 0 && ret == 0) |
---|
96 | ret = t_ret; |
---|
97 | } |
---|
98 | |
---|
99 | return (ret); |
---|
100 | } |
---|
101 | |
---|
102 | /* |
---|
103 | * __os_mapfile -- |
---|
104 | * Map in a shared memory file. |
---|
105 | */ |
---|
106 | int |
---|
107 | __os_mapfile(dbenv, path, fhp, len, is_rdonly, addr) |
---|
108 | DB_ENV *dbenv; |
---|
109 | char *path; |
---|
110 | DB_FH *fhp; |
---|
111 | int is_rdonly; |
---|
112 | size_t len; |
---|
113 | void **addr; |
---|
114 | { |
---|
115 | /* If the user replaced the map call, call through their interface. */ |
---|
116 | if (DB_GLOBAL(j_map) != NULL) |
---|
117 | return (DB_GLOBAL(j_map)(path, len, 0, is_rdonly, addr)); |
---|
118 | |
---|
119 | return (__os_map(dbenv, path, NULL, fhp, len, 0, 0, is_rdonly, addr)); |
---|
120 | } |
---|
121 | |
---|
122 | /* |
---|
123 | * __os_unmapfile -- |
---|
124 | * Unmap the shared memory file. |
---|
125 | */ |
---|
126 | int |
---|
127 | __os_unmapfile(dbenv, addr, len) |
---|
128 | DB_ENV *dbenv; |
---|
129 | void *addr; |
---|
130 | size_t len; |
---|
131 | { |
---|
132 | /* If the user replaced the map call, call through their interface. */ |
---|
133 | if (DB_GLOBAL(j_unmap) != NULL) |
---|
134 | return (DB_GLOBAL(j_unmap)(addr, len)); |
---|
135 | |
---|
136 | return (!UnmapViewOfFile(addr) ? __os_win32_errno() : 0); |
---|
137 | } |
---|
138 | |
---|
139 | /* |
---|
140 | * __os_unique_name -- |
---|
141 | * Create a unique identifying name from a pathname (may be absolute or |
---|
142 | * relative) and/or a file descriptor. |
---|
143 | * |
---|
144 | * The name returned must be unique (different files map to different |
---|
145 | * names), and repeatable (same files, map to same names). It's not |
---|
146 | * so easy to do by name. Should handle not only: |
---|
147 | * |
---|
148 | * foo.bar == ./foo.bar == c:/whatever_path/foo.bar |
---|
149 | * |
---|
150 | * but also understand that: |
---|
151 | * |
---|
152 | * foo.bar == Foo.Bar (FAT file system) |
---|
153 | * foo.bar != Foo.Bar (NTFS) |
---|
154 | * |
---|
155 | * The best solution is to use the file index, found in the file |
---|
156 | * information structure (similar to UNIX inode #). |
---|
157 | * |
---|
158 | * When a file is deleted, its file index may be reused, |
---|
159 | * but if the unique name has not gone from its namespace, |
---|
160 | * we may get a conflict. So to ensure some tie in to the |
---|
161 | * original pathname, we also use the creation time and the |
---|
162 | * file basename. This is not a perfect system, but it |
---|
163 | * should work for all but anamolous test cases. |
---|
164 | * |
---|
165 | */ |
---|
166 | static int |
---|
167 | __os_unique_name(orig_path, hfile, result_path, result_path_len) |
---|
168 | char *orig_path, *result_path; |
---|
169 | HANDLE hfile; |
---|
170 | size_t result_path_len; |
---|
171 | { |
---|
172 | BY_HANDLE_FILE_INFORMATION fileinfo; |
---|
173 | char *basename, *p; |
---|
174 | |
---|
175 | /* |
---|
176 | * In Windows, pathname components are delimited by '/' or '\', and |
---|
177 | * if neither is present, we need to strip off leading drive letter |
---|
178 | * (e.g. c:foo.txt). |
---|
179 | */ |
---|
180 | basename = strrchr(orig_path, '/'); |
---|
181 | p = strrchr(orig_path, '\\'); |
---|
182 | if (basename == NULL || (p != NULL && p > basename)) |
---|
183 | basename = p; |
---|
184 | if (basename == NULL) |
---|
185 | basename = strrchr(orig_path, ':'); |
---|
186 | |
---|
187 | if (basename == NULL) |
---|
188 | basename = orig_path; |
---|
189 | else |
---|
190 | basename++; |
---|
191 | |
---|
192 | if (!GetFileInformationByHandle(hfile, &fileinfo)) |
---|
193 | return (__os_win32_errno()); |
---|
194 | |
---|
195 | (void)snprintf(result_path, result_path_len, |
---|
196 | "__db_shmem.%8.8lx.%8.8lx.%8.8lx.%8.8lx.%8.8lx.%s", |
---|
197 | fileinfo.dwVolumeSerialNumber, |
---|
198 | fileinfo.nFileIndexHigh, |
---|
199 | fileinfo.nFileIndexLow, |
---|
200 | fileinfo.ftCreationTime.dwHighDateTime, |
---|
201 | fileinfo.ftCreationTime.dwHighDateTime, |
---|
202 | basename); |
---|
203 | |
---|
204 | return (0); |
---|
205 | } |
---|
206 | |
---|
207 | /* |
---|
208 | * __os_map -- |
---|
209 | * The mmap(2) function for Windows. |
---|
210 | */ |
---|
211 | static int |
---|
212 | __os_map(dbenv, path, infop, fhp, len, is_region, is_system, is_rdonly, addr) |
---|
213 | DB_ENV *dbenv; |
---|
214 | REGINFO *infop; |
---|
215 | char *path; |
---|
216 | DB_FH *fhp; |
---|
217 | int is_region, is_system, is_rdonly; |
---|
218 | size_t len; |
---|
219 | void **addr; |
---|
220 | { |
---|
221 | HANDLE hMemory; |
---|
222 | REGENV *renv; |
---|
223 | int ret, use_pagefile; |
---|
224 | char shmem_name[MAXPATHLEN]; |
---|
225 | void *pMemory; |
---|
226 | |
---|
227 | ret = 0; |
---|
228 | if (infop != NULL) |
---|
229 | infop->wnt_handle = NULL; |
---|
230 | |
---|
231 | use_pagefile = is_region && is_system; |
---|
232 | |
---|
233 | /* |
---|
234 | * If creating a region in system space, get a matching name in the |
---|
235 | * paging file namespace. |
---|
236 | */ |
---|
237 | if (use_pagefile && (ret = __os_unique_name( |
---|
238 | path, fhp->handle, shmem_name, sizeof(shmem_name))) != 0) |
---|
239 | return (ret); |
---|
240 | |
---|
241 | /* |
---|
242 | * XXX |
---|
243 | * DB: We have not implemented copy-on-write here. |
---|
244 | * |
---|
245 | * XXX |
---|
246 | * DB: This code will fail if the library is ever compiled on a 64-bit |
---|
247 | * machine. |
---|
248 | * |
---|
249 | * XXX |
---|
250 | * If this is an region in system memory, let's try opening using the |
---|
251 | * OpenFileMapping() first. Why, oh why are we doing this? |
---|
252 | * |
---|
253 | * Well, we might be asking the OS for a handle to a pre-existing |
---|
254 | * memory section, or we might be the first to get here and want the |
---|
255 | * section created. CreateFileMapping() sounds like it will do both |
---|
256 | * jobs. But, not so. It seems to mess up making the commit charge to |
---|
257 | * the process. It thinks, incorrectly, that when we want to join a |
---|
258 | * previously existing section, that it should make a commit charge |
---|
259 | * for the whole section. In fact, there is no new committed memory |
---|
260 | * whatever. The call can fail if there is insufficient memory free |
---|
261 | * to handle the erroneous commit charge. So, we find that the bogus |
---|
262 | * commit is not made if we call OpenFileMapping(). So we do that |
---|
263 | * first, and only call CreateFileMapping() if we're really creating |
---|
264 | * the section. |
---|
265 | */ |
---|
266 | hMemory = NULL; |
---|
267 | if (use_pagefile) |
---|
268 | hMemory = OpenFileMapping( |
---|
269 | is_rdonly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS, |
---|
270 | 0, |
---|
271 | shmem_name); |
---|
272 | |
---|
273 | if (hMemory == NULL) |
---|
274 | hMemory = CreateFileMapping( |
---|
275 | use_pagefile ? (HANDLE)-1 : fhp->handle, |
---|
276 | 0, |
---|
277 | is_rdonly ? PAGE_READONLY : PAGE_READWRITE, |
---|
278 | 0, (DWORD)len, |
---|
279 | use_pagefile ? shmem_name : NULL); |
---|
280 | if (hMemory == NULL) { |
---|
281 | ret = __os_win32_errno(); |
---|
282 | __db_err(dbenv, "OpenFileMapping: %s", strerror(ret)); |
---|
283 | return (ret); |
---|
284 | } |
---|
285 | |
---|
286 | pMemory = MapViewOfFile(hMemory, |
---|
287 | (is_rdonly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS), 0, 0, len); |
---|
288 | if (pMemory == NULL) { |
---|
289 | ret = __os_win32_errno(); |
---|
290 | __db_err(dbenv, "MapViewOfFile: %s", strerror(ret)); |
---|
291 | return (ret); |
---|
292 | } |
---|
293 | |
---|
294 | /* |
---|
295 | * XXX |
---|
296 | * It turns out that the kernel object underlying the named section |
---|
297 | * is reference counted, but that the call to MapViewOfFile() above |
---|
298 | * does NOT increment the reference count! So, if we close the handle |
---|
299 | * here, the kernel deletes the object from the kernel namespace. |
---|
300 | * When a second process comes along to join the region, the kernel |
---|
301 | * happily creates a new object with the same name, but completely |
---|
302 | * different identity. The two processes then have distinct isolated |
---|
303 | * mapped sections, not at all what was wanted. Not closing the handle |
---|
304 | * here fixes this problem. We carry the handle around in the region |
---|
305 | * structure so we can close it when unmap is called. Ignore malloc |
---|
306 | * errors, it just means we leak the memory. |
---|
307 | */ |
---|
308 | if (use_pagefile && infop != NULL) { |
---|
309 | if (__os_malloc(dbenv, |
---|
310 | sizeof(HANDLE), &infop->wnt_handle) == 0) |
---|
311 | memcpy(infop->wnt_handle, &hMemory, sizeof(HANDLE)); |
---|
312 | } else |
---|
313 | CloseHandle(hMemory); |
---|
314 | |
---|
315 | if (is_region) { |
---|
316 | /* |
---|
317 | * XXX |
---|
318 | * Windows/95 zeroes anonymous memory regions at last close. |
---|
319 | * This means that the backing file can exist and reference |
---|
320 | * the region, but the region itself is no longer initialized. |
---|
321 | * If the caller is capable of creating the region, update |
---|
322 | * the REGINFO structure so that they do so. |
---|
323 | */ |
---|
324 | renv = (REGENV *)pMemory; |
---|
325 | if (renv->magic == 0) { |
---|
326 | if (F_ISSET(infop, REGION_CREATE_OK)) |
---|
327 | F_SET(infop, REGION_CREATE); |
---|
328 | else { |
---|
329 | (void)UnmapViewOfFile(pMemory); |
---|
330 | pMemory = NULL; |
---|
331 | ret = EAGAIN; |
---|
332 | } |
---|
333 | } |
---|
334 | } |
---|
335 | |
---|
336 | *addr = pMemory; |
---|
337 | return (ret); |
---|
338 | } |
---|