source: trunk/third/rpm/db/os_win32/os_map.c @ 19079

Revision 19079, 9.3 KB checked in by ghudson, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r19078, which included commits to RCS files with non-trunk default branches.
Line 
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
11static 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
16static int __os_map
17  __P((DB_ENV *, char *, REGINFO *, DB_FH *, size_t, int, int, int, void **));
18static int __os_unique_name __P((char *, HANDLE, char *, size_t));
19
20/*
21 * __os_r_sysattach --
22 *      Create/join a shared memory region.
23 */
24int
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 */
75int
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 */
106int
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 */
126int
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 */
166static 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 */
211static 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}
Note: See TracBrowser for help on using the repository browser.