Update.
[glibc.git] / db2 / os / os_map.c
blob5f0fd790e626e8947c015b2a24478b2623ff5607
1 /*-
2 * See the file LICENSE for redistribution information.
4 * Copyright (c) 1996, 1997, 1998
5 * Sleepycat Software. All rights reserved.
6 */
8 #include "config.h"
10 #ifndef lint
11 static const char sccsid[] = "@(#)os_map.c 10.19 (Sleepycat) 5/3/98";
12 #endif /* not lint */
14 #ifndef NO_SYSTEM_INCLUDES
15 #include <sys/types.h>
16 #ifdef HAVE_MMAP
17 #include <sys/mman.h>
18 #endif
20 #ifdef HAVE_SHMGET
21 #include <sys/ipc.h>
22 #include <sys/shm.h>
23 #endif
25 #include <errno.h>
26 #include <string.h>
27 #endif
29 #include "db_int.h"
30 #include "common_ext.h"
32 #ifdef HAVE_MMAP
33 static int __os_map __P((char *, int, size_t, int, int, int, void **));
34 #endif
35 #ifdef HAVE_SHMGET
36 static int __os_shmget __P((char *, REGINFO *));
37 #endif
40 * __db_mapanon_ok --
41 * Return if this OS can support anonymous memory regions.
43 * PUBLIC: int __db_mapanon_ok __P((int));
45 int
46 __db_mapanon_ok(need_names)
47 int need_names;
49 int ret;
51 ret = EINVAL;
54 * If we don't have spinlocks, we have to have a file descriptor
55 * for fcntl(2) locking, which implies using mmap(2) to map in a
56 * regular file. Theoretically, we could probably find ways to
57 * get a file descriptor to lock other types of shared regions,
58 * but I don't see any reason to do so.
60 * If need_names is set, the application wants to share anonymous
61 * memory among multiple processes, so we have to have a way to
62 * name it. This requires shmget(2), on UNIX systems.
64 #ifdef HAVE_SPINLOCKS
65 #ifdef HAVE_SHMGET
66 ret = 0;
67 #endif
68 #ifdef HAVE_MMAP
69 #ifdef MAP_ANON
70 if (!need_names)
71 ret = 0;
72 #endif
73 #ifdef MAP_ANONYMOUS
74 if (!need_names)
75 ret = 0;
76 #endif
77 #else
78 COMPQUIET(need_names, 0);
79 #endif /* HAVE_MMAP */
80 #endif /* HAVE_SPINLOCKS */
82 return (ret);
86 * __db_mapinit --
87 * Return if shared regions need to be initialized.
89 * PUBLIC: int __db_mapinit __P((void));
91 int
92 __db_mapinit()
95 * Historically, some systems required that all of the bytes of the
96 * region be written before it could be mmapped and accessed randomly.
97 * We have the option of setting REGION_INIT_NEEDED at configuration
98 * time if we're running on one of those systems.
100 #ifdef REGION_INIT_NEEDED
101 return (1);
102 #else
103 return (0);
104 #endif
108 * __db_mapregion --
109 * Attach to a shared memory region.
111 * PUBLIC: int __db_mapregion __P((char *, REGINFO *));
114 __db_mapregion(path, infop)
115 char *path;
116 REGINFO *infop;
118 int called, ret;
120 called = 0;
121 ret = EINVAL;
123 /* If the user replaces the map call, call through their interface. */
124 if (__db_jump.j_map != NULL) {
125 F_SET(infop, REGION_HOLDINGSYS);
126 return (__db_jump.j_map(path, infop->fd, infop->size,
127 1, F_ISSET(infop, REGION_ANONYMOUS), 0, &infop->addr));
130 if (F_ISSET(infop, REGION_ANONYMOUS)) {
132 * !!!
133 * If we're creating anonymous regions:
135 * If it's private, we use mmap(2). The problem with using
136 * shmget(2) is that we may be creating a region of which the
137 * application isn't aware, and if the application crashes
138 * we'll have no way to remove the system resources for the
139 * region.
141 * If it's not private, we use the shmget(2) interface if it's
142 * available, because it allows us to name anonymous memory.
143 * If shmget(2) isn't available, use the mmap(2) calls.
145 * In the case of anonymous memory, using mmap(2) means the
146 * memory isn't named and only the single process and its
147 * threads can access the region.
149 #ifdef HAVE_MMAP
150 #ifdef MAP_ANON
151 #define HAVE_MMAP_ANONYMOUS 1
152 #else
153 #ifdef MAP_ANONYMOUS
154 #define HAVE_MMAP_ANONYMOUS 1
155 #endif
156 #endif
157 #endif
158 #ifdef HAVE_MMAP_ANONYMOUS
159 if (!called && F_ISSET(infop, REGION_PRIVATE)) {
160 called = 1;
161 ret = __os_map(path,
162 infop->fd, infop->size, 1, 1, 0, &infop->addr);
164 #endif
165 #ifdef HAVE_SHMGET
166 if (!called) {
167 called = 1;
168 ret = __os_shmget(path, infop);
170 #endif
171 #ifdef HAVE_MMAP
173 * If we're trying to join an unnamed anonymous region, fail --
174 * that's not possible.
176 if (!called) {
177 called = 1;
179 if (!F_ISSET(infop, REGION_CREATED)) {
180 __db_err(infop->dbenv,
181 "cannot join region in unnamed anonymous memory");
182 return (EINVAL);
185 ret = __os_map(path,
186 infop->fd, infop->size, 1, 1, 0, &infop->addr);
188 #endif
189 } else {
191 * !!!
192 * If we're creating normal regions, we use the mmap(2)
193 * interface if it's available because it's POSIX 1003.1
194 * standard and we trust it more than we do shmget(2).
196 #ifdef HAVE_MMAP
197 if (!called) {
198 called = 1;
200 /* Mmap(2) regions that aren't anonymous can grow. */
201 F_SET(infop, REGION_CANGROW);
203 ret = __os_map(path,
204 infop->fd, infop->size, 1, 0, 0, &infop->addr);
206 #endif
207 #ifdef HAVE_SHMGET
208 if (!called) {
209 called = 1;
210 ret = __os_shmget(path, infop);
212 #endif
214 return (ret);
218 * __db_unmapregion --
219 * Detach from the shared memory region.
221 * PUBLIC: int __db_unmapregion __P((REGINFO *));
224 __db_unmapregion(infop)
225 REGINFO *infop;
227 int called, ret;
229 called = 0;
230 ret = EINVAL;
232 if (__db_jump.j_unmap != NULL)
233 return (__db_jump.j_unmap(infop->addr, infop->size));
235 #ifdef HAVE_SHMGET
236 if (infop->segid != INVALID_SEGID) {
237 called = 1;
238 ret = shmdt(infop->addr) ? errno : 0;
240 #endif
241 #ifdef HAVE_MMAP
242 if (!called) {
243 called = 1;
244 ret = munmap(infop->addr, infop->size) ? errno : 0;
246 #endif
247 return (ret);
251 * __db_unlinkregion --
252 * Remove the shared memory region.
254 * PUBLIC: int __db_unlinkregion __P((char *, REGINFO *));
257 __db_unlinkregion(name, infop)
258 char *name;
259 REGINFO *infop;
261 int called, ret;
263 called = 0;
264 ret = EINVAL;
266 if (__db_jump.j_runlink != NULL)
267 return (__db_jump.j_runlink(name));
269 #ifdef HAVE_SHMGET
270 if (infop->segid != INVALID_SEGID) {
271 called = 1;
272 ret = shmctl(infop->segid, IPC_RMID, NULL) ? errno : 0;
274 #else
275 COMPQUIET(infop, NULL);
276 #endif
277 #ifdef HAVE_MMAP
278 if (!called) {
279 called = 1;
280 ret = 0;
282 #endif
283 return (ret);
287 * __db_mapfile --
288 * Map in a shared memory file.
290 * PUBLIC: int __db_mapfile __P((char *, int, size_t, int, void **));
293 __db_mapfile(path, fd, len, is_rdonly, addr)
294 char *path;
295 int fd, is_rdonly;
296 size_t len;
297 void **addr;
299 if (__db_jump.j_map != NULL)
300 return (__db_jump.j_map(path, fd, len, 0, 0, is_rdonly, addr));
302 #ifdef HAVE_MMAP
303 return (__os_map(path, fd, len, 0, 0, is_rdonly, addr));
304 #else
305 return (EINVAL);
306 #endif
310 * __db_unmapfile --
311 * Unmap the shared memory file.
313 * PUBLIC: int __db_unmapfile __P((void *, size_t));
316 __db_unmapfile(addr, len)
317 void *addr;
318 size_t len;
320 if (__db_jump.j_unmap != NULL)
321 return (__db_jump.j_unmap(addr, len));
323 #ifdef HAVE_MMAP
324 return (munmap(addr, len) ? errno : 0);
325 #else
326 return (EINVAL);
327 #endif
330 #ifdef HAVE_MMAP
332 * __os_map --
333 * Call the mmap(2) function.
335 static int
336 __os_map(path, fd, len, is_region, is_anonymous, is_rdonly, addr)
337 char *path;
338 int fd, is_region, is_anonymous, is_rdonly;
339 size_t len;
340 void **addr;
342 void *p;
343 int flags, prot;
345 COMPQUIET(path, NULL);
348 * If it's read-only, it's private, and if it's not, it's shared.
349 * Don't bother with an additional parameter.
351 flags = is_rdonly ? MAP_PRIVATE : MAP_SHARED;
353 if (is_region && is_anonymous) {
355 * BSD derived systems use MAP_ANON; Digital Unix and HP/UX
356 * use MAP_ANONYMOUS.
358 #ifdef MAP_ANON
359 flags |= MAP_ANON;
360 #endif
361 #ifdef MAP_ANONYMOUS
362 flags |= MAP_ANONYMOUS;
363 #endif
364 fd = -1;
366 #ifdef MAP_FILE
367 if (!is_region || !is_anonymous) {
369 * Historically, MAP_FILE was required for mapping regular
370 * files, even though it was the default. Some systems have
371 * it, some don't, some that have it set it to 0.
373 flags |= MAP_FILE;
375 #endif
378 * I know of no systems that implement the flag to tell the system
379 * that the region contains semaphores, but it's not an unreasonable
380 * thing to do, and has been part of the design since forever. I
381 * don't think anyone will object, but don't set it for read-only
382 * files, it doesn't make sense.
384 #ifdef MAP_HASSEMAPHORE
385 if (!is_rdonly)
386 flags |= MAP_HASSEMAPHORE;
387 #endif
389 prot = PROT_READ | (is_rdonly ? 0 : PROT_WRITE);
391 /* MAP_FAILED was not defined in early mmap implementations. */
392 #ifndef MAP_FAILED
393 #define MAP_FAILED -1
394 #endif
395 if ((p =
396 mmap(NULL, len, prot, flags, fd, (off_t)0)) == (void *)MAP_FAILED)
397 return (errno);
399 *addr = p;
400 return (0);
402 #endif
404 #ifdef HAVE_SHMGET
406 * __os_shmget --
407 * Call the shmget(2) family of functions.
409 static int
410 __os_shmget(path, infop)
411 REGINFO *infop;
412 char *path;
414 key_t key;
415 int shmflg;
417 if (F_ISSET(infop, REGION_CREATED)) {
419 * The return key from ftok(3) is not guaranteed to be unique.
420 * The nice thing about the shmget(2) interface is that it
421 * allows you to name anonymous pieces of memory. The evil
422 * thing about it is that the name space is separate from the
423 * filesystem.
425 #ifdef __hp3000s900
426 {char mpe_path[MAXPATHLEN];
428 * MPE ftok() is broken as of 5.5pp4. If the file path does
429 * not start with '/' or '.', then ftok() tries to interpret
430 * the file path in MPE syntax instead of POSIX HFS syntax.
431 * The workaround is to prepend "./" to these paths. See HP
432 * SR 5003416081 for details.
434 if (*path != '/' && *path != '.') {
435 if (strlen(path) + strlen("./") + 1 > sizeof(mpe_path))
436 return (ENAMETOOLONG);
437 mpe_path[0] = '.';
438 mpe_path[1] = '/';
439 (void)strcpy(mpe_path + 2, path);
440 path = mpe_path;
443 #endif
444 if ((key = ftok(path, 1)) == (key_t)-1)
445 return (errno);
447 shmflg = IPC_CREAT | 0600;
448 if ((infop->segid = shmget(key, infop->size, shmflg)) == -1)
449 return (errno);
452 if ((infop->addr = shmat(infop->segid, NULL, 0)) == (void *)-1) {
454 * If we're trying to join the region and failing, assume
455 * that there was a reboot and the region no longer exists.
457 if (!F_ISSET(infop, REGION_CREATED))
458 errno = EAGAIN;
459 return (errno);
462 F_SET(infop, REGION_HOLDINGSYS);
463 return (0);
465 #endif