Update.
[glibc.git] / db2 / mp / mp_fopen.c
blob5ab807701c77fa670f4dff8b6959c728220bf4e5
1 /*-
2 * See the file LICENSE for redistribution information.
4 * Copyright (c) 1996, 1997
5 * Sleepycat Software. All rights reserved.
6 */
7 #include "config.h"
9 #ifndef lint
10 static const char sccsid[] = "@(#)mp_fopen.c 10.27 (Sleepycat) 9/23/97";
11 #endif /* not lint */
13 #ifndef NO_SYSTEM_INCLUDES
14 #include <sys/types.h>
15 #include <sys/mman.h>
16 #include <sys/stat.h>
18 #include <errno.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <unistd.h>
22 #endif
24 #include "db_int.h"
25 #include "shqueue.h"
26 #include "db_shash.h"
27 #include "mp.h"
28 #include "common_ext.h"
30 static int __memp_mf_close __P((DB_MPOOL *, DB_MPOOLFILE *));
31 static int __memp_mf_open __P((DB_MPOOL *,
32 DB_MPOOLFILE *, int, size_t, int, DBT *, u_int8_t *, int, MPOOLFILE **));
35 * memp_fopen --
36 * Open a backing file for the memory pool.
38 int
39 memp_fopen(dbmp, path, ftype,
40 flags, mode, pagesize, lsn_offset, pgcookie, fileid, retp)
41 DB_MPOOL *dbmp;
42 const char *path;
43 int ftype, flags, mode, lsn_offset;
44 size_t pagesize;
45 DBT *pgcookie;
46 u_int8_t *fileid;
47 DB_MPOOLFILE **retp;
49 int ret;
51 /* Validate arguments. */
52 if ((ret = __db_fchk(dbmp->dbenv,
53 "memp_fopen", flags, DB_CREATE | DB_NOMMAP | DB_RDONLY)) != 0)
54 return (ret);
56 return (__memp_fopen(dbmp, path, ftype,
57 flags, mode, pagesize, lsn_offset, pgcookie, fileid, 1, retp));
61 * __memp_fopen --
62 * Open a backing file for the memory pool; internal version.
64 * PUBLIC: int __memp_fopen __P((DB_MPOOL *, const char *, int, int,
65 * PUBLIC: int, size_t, int, DBT *, u_int8_t *, int, DB_MPOOLFILE **));
67 int
68 __memp_fopen(dbmp, path,
69 ftype, flags, mode, pagesize, lsn_offset, pgcookie, fileid, needlock, retp)
70 DB_MPOOL *dbmp;
71 const char *path;
72 int ftype, flags, mode, lsn_offset, needlock;
73 size_t pagesize;
74 DBT *pgcookie;
75 u_int8_t *fileid;
76 DB_MPOOLFILE **retp;
78 DB_ENV *dbenv;
79 DB_MPOOLFILE *dbmfp;
80 MPOOLFILE *mfp;
81 off_t size;
82 int ret;
84 dbenv = dbmp->dbenv;
85 ret = 0;
87 /* Require a non-zero pagesize. */
88 if (pagesize == 0) {
89 __db_err(dbenv, "memp_fopen: pagesize not specified");
90 return (EINVAL);
93 /* Allocate and initialize the per-process structure. */
94 if ((dbmfp =
95 (DB_MPOOLFILE *)calloc(1, sizeof(DB_MPOOLFILE))) == NULL) {
96 __db_err(dbenv, "%s: %s",
97 path == NULL ? TEMPORARY : path, strerror(ENOMEM));
98 return (ENOMEM);
100 dbmfp->dbmp = dbmp;
101 dbmfp->fd = -1;
102 if (LF_ISSET(DB_RDONLY))
103 F_SET(dbmfp, MP_READONLY);
105 if (path == NULL) {
106 if (LF_ISSET(DB_RDONLY)) {
107 __db_err(dbenv,
108 "memp_fopen: temporary files can't be readonly");
109 ret = EINVAL;
110 goto err;
112 dbmfp->path = (char *)TEMPORARY;
113 F_SET(dbmfp, MP_PATH_TEMP);
114 } else {
115 /* Calculate the real name for this file. */
116 if ((ret = __db_appname(dbenv,
117 DB_APP_DATA, NULL, path, NULL, &dbmfp->path)) != 0)
118 goto err;
119 F_SET(dbmfp, MP_PATH_ALLOC);
122 /* Open the file. */
123 if ((ret = __db_fdopen(dbmfp->path,
124 LF_ISSET(DB_CREATE | DB_RDONLY), DB_CREATE | DB_RDONLY,
125 mode, &dbmfp->fd)) != 0) {
126 __db_err(dbenv, "%s: %s", dbmfp->path, strerror(ret));
127 goto err;
130 /* Don't permit files that aren't a multiple of the pagesize. */
131 if ((ret = __db_stat(dbenv,
132 dbmfp->path, dbmfp->fd, &size, NULL)) != 0)
133 goto err;
134 if (size % pagesize) {
135 __db_err(dbenv,
136 "%s: file size not a multiple of the pagesize",
137 dbmfp->path);
138 ret = EINVAL;
139 goto err;
144 * Find/allocate the shared file objects. This includes allocating
145 * space for the per-process thread lock.
147 if (needlock)
148 LOCKREGION(dbmp);
149 ret = __memp_mf_open(dbmp, dbmfp, ftype, pagesize,
150 lsn_offset, pgcookie, fileid, F_ISSET(dbmfp, MP_PATH_TEMP), &mfp);
151 if (ret == 0 &&
152 F_ISSET(dbmp, MP_LOCKHANDLE) && (ret =
153 __memp_ralloc(dbmp, sizeof(db_mutex_t), NULL, &dbmfp->mutexp)) == 0)
154 LOCKINIT(dbmp, dbmfp->mutexp);
155 if (needlock)
156 UNLOCKREGION(dbmp);
158 if (ret != 0)
159 goto err;
161 dbmfp->mfp = mfp;
164 * If a file:
165 * + is read-only
166 * + isn't temporary
167 * + doesn't require any pgin/pgout support
168 * + the DB_NOMMAP flag wasn't set
169 * + and is less than mp_mmapsize bytes in size
171 * we can mmap it instead of reading/writing buffers. Don't do error
172 * checking based on the mmap call failure. We want to do normal I/O
173 * on the file if the reason we failed was because the file was on an
174 * NFS mounted partition, and we can fail in buffer I/O just as easily
175 * as here.
177 * XXX
178 * We'd like to test to see if the file is too big to mmap. Since we
179 * don't know what size or type off_t's or size_t's are, or the largest
180 * unsigned integral type is, or what random insanity the local C
181 * compiler will perpetrate, doing the comparison in a portable way is
182 * flatly impossible. Hope that mmap fails if the file is too large.
184 #define DB_MAXMMAPSIZE (10 * 1024 * 1024) /* 10 Mb. */
185 if (mfp->can_mmap) {
186 if (!F_ISSET(dbmfp, MP_READONLY))
187 mfp->can_mmap = 0;
188 if (path == NULL)
189 mfp->can_mmap = 0;
190 if (ftype != 0)
191 mfp->can_mmap = 0;
192 if (LF_ISSET(DB_NOMMAP))
193 mfp->can_mmap = 0;
194 if (size > (dbenv == NULL || dbenv->mp_mmapsize == 0 ?
195 DB_MAXMMAPSIZE : (off_t)dbenv->mp_mmapsize))
196 mfp->can_mmap = 0;
198 dbmfp->addr = NULL;
199 if (mfp->can_mmap) {
200 dbmfp->len = size;
201 if (__db_mmap(dbmfp->fd, dbmfp->len, 1, 1, &dbmfp->addr) != 0) {
202 mfp->can_mmap = 0;
203 dbmfp->addr = NULL;
207 LOCKHANDLE(dbmp, dbmp->mutexp);
208 TAILQ_INSERT_TAIL(&dbmp->dbmfq, dbmfp, q);
209 UNLOCKHANDLE(dbmp, dbmp->mutexp);
211 *retp = dbmfp;
212 return (0);
214 err: /*
215 * Note that we do not have to free the thread mutex, because we
216 * never get to here after we have successfully allocated it.
218 if (F_ISSET(dbmfp, MP_PATH_ALLOC))
219 FREES(dbmfp->path);
220 if (dbmfp->fd != -1)
221 (void)__db_close(dbmfp->fd);
222 if (dbmfp != NULL)
223 FREE(dbmfp, sizeof(DB_MPOOLFILE));
224 return (ret);
228 * __memp_mf_open --
229 * Open an MPOOLFILE.
231 static int
232 __memp_mf_open(dbmp, dbmfp,
233 ftype, pagesize, lsn_offset, pgcookie, fileid, istemp, retp)
234 DB_MPOOL *dbmp;
235 DB_MPOOLFILE *dbmfp;
236 int ftype, lsn_offset, istemp;
237 size_t pagesize;
238 DBT *pgcookie;
239 u_int8_t *fileid;
240 MPOOLFILE **retp;
242 MPOOLFILE *mfp;
243 int ret;
244 u_int8_t idbuf[DB_FILE_ID_LEN];
245 void *p;
247 /* Temporary files can't match previous files. */
248 if (istemp)
249 goto alloc;
252 * Get the file id if we weren't give one. Generated file id's don't
253 * use timestamps, otherwise there'd be no chance of anyone joining
254 * the party.
256 if (fileid == NULL) {
257 if ((ret =
258 __db_fileid(dbmp->dbenv, dbmfp->path, 0, idbuf)) != 0)
259 return (ret);
260 fileid = idbuf;
263 /* Walk the list of MPOOLFILE's, looking for a matching file. */
264 for (mfp = SH_TAILQ_FIRST(&dbmp->mp->mpfq, __mpoolfile);
265 mfp != NULL; mfp = SH_TAILQ_NEXT(mfp, q, __mpoolfile))
266 if (!memcmp(fileid,
267 ADDR(dbmp, mfp->fileid_off), DB_FILE_ID_LEN)) {
268 if (ftype != mfp->ftype ||
269 pagesize != mfp->stat.st_pagesize) {
270 __db_err(dbmp->dbenv,
271 "%s: ftype or pagesize changed",
272 dbmfp->path);
273 ret = EINVAL;
274 mfp = NULL;
275 goto ret1;
277 /* Found it: increment the reference count. */
278 ++mfp->ref;
279 goto ret1;
282 /* Allocate a new MPOOLFILE. */
283 alloc: if ((ret = __memp_ralloc(dbmp, sizeof(MPOOLFILE), NULL, &mfp)) != 0)
284 goto ret1;
286 /* Initialize the structure. */
287 memset(mfp, 0, sizeof(MPOOLFILE));
288 mfp->ref = 1;
289 mfp->ftype = ftype;
290 mfp->can_mmap = 1;
291 mfp->lsn_off = lsn_offset;
292 mfp->stat.st_pagesize = pagesize;
294 /* Copy the file path into shared memory. */
295 if ((ret = __memp_ralloc(dbmp,
296 strlen(dbmfp->path) + 1, &mfp->path_off, &p)) != 0)
297 goto err;
298 memcpy(p, dbmfp->path, strlen(dbmfp->path) + 1);
300 /* Copy the file identification string into shared memory. */
301 if (istemp)
302 mfp->fileid_off = 0;
303 else {
304 if ((ret = __memp_ralloc(dbmp,
305 DB_FILE_ID_LEN, &mfp->fileid_off, &p)) != 0)
306 goto err;
307 memcpy(p, fileid, DB_FILE_ID_LEN);
310 /* Copy the page cookie into shared memory. */
311 if (pgcookie == NULL || pgcookie->size == 0) {
312 mfp->pgcookie_len = 0;
313 mfp->pgcookie_off = 0;
314 } else {
315 if ((ret = __memp_ralloc(dbmp,
316 pgcookie->size, &mfp->pgcookie_off, &p)) != 0)
317 goto err;
318 memcpy(p, pgcookie->data, pgcookie->size);
319 mfp->pgcookie_len = pgcookie->size;
322 /* Prepend the MPOOLFILE to the list of MPOOLFILE's. */
323 SH_TAILQ_INSERT_HEAD(&dbmp->mp->mpfq, mfp, q, __mpoolfile);
325 if (0) {
326 err: if (mfp->path_off != 0)
327 __db_shalloc_free(dbmp->addr,
328 ADDR(dbmp, mfp->path_off));
329 if (!istemp)
330 __db_shalloc_free(dbmp->addr,
331 ADDR(dbmp, mfp->fileid_off));
332 if (mfp != NULL)
333 __db_shalloc_free(dbmp->addr, mfp);
334 mfp = NULL;
337 ret1: *retp = mfp;
338 return (0);
342 * memp_fclose --
343 * Close a backing file for the memory pool.
346 memp_fclose(dbmfp)
347 DB_MPOOLFILE *dbmfp;
349 DB_MPOOL *dbmp;
350 int ret, t_ret;
352 dbmp = dbmfp->dbmp;
353 ret = 0;
355 /* Complain if pinned blocks never returned. */
356 if (dbmfp->pinref != 0)
357 __db_err(dbmp->dbenv, "%s: close: %lu blocks left pinned",
358 dbmfp->path, (u_long)dbmfp->pinref);
360 /* Remove the DB_MPOOLFILE structure from the list. */
361 LOCKHANDLE(dbmp, dbmp->mutexp);
362 TAILQ_REMOVE(&dbmp->dbmfq, dbmfp, q);
363 UNLOCKHANDLE(dbmp, dbmp->mutexp);
365 /* Close the underlying MPOOLFILE. */
366 (void)__memp_mf_close(dbmp, dbmfp);
368 /* Discard any mmap information. */
369 if (dbmfp->addr != NULL &&
370 (ret = __db_munmap(dbmfp->addr, dbmfp->len)) != 0)
371 __db_err(dbmp->dbenv, "%s: %s", dbmfp->path, strerror(ret));
373 /* Close the file; temporary files may not yet have been created. */
374 if (dbmfp->fd != -1 && (t_ret = __db_close(dbmfp->fd)) != 0) {
375 __db_err(dbmp->dbenv, "%s: %s", dbmfp->path, strerror(t_ret));
376 if (ret != 0)
377 t_ret = ret;
380 /* Free memory. */
381 if (F_ISSET(dbmfp, MP_PATH_ALLOC))
382 FREES(dbmfp->path);
383 if (dbmfp->mutexp != NULL) {
384 LOCKREGION(dbmp);
385 __db_shalloc_free(dbmp->addr, dbmfp->mutexp);
386 UNLOCKREGION(dbmp);
389 /* Discard the DB_MPOOLFILE structure. */
390 FREE(dbmfp, sizeof(DB_MPOOLFILE));
392 return (ret);
396 * __memp_mf_close --
397 * Close down an MPOOLFILE.
399 static int
400 __memp_mf_close(dbmp, dbmfp)
401 DB_MPOOL *dbmp;
402 DB_MPOOLFILE *dbmfp;
404 BH *bhp, *nbhp;
405 MPOOL *mp;
406 MPOOLFILE *mfp;
407 size_t mf_offset;
409 mp = dbmp->mp;
410 mfp = dbmfp->mfp;
412 LOCKREGION(dbmp);
414 /* If more than a single reference, simply decrement. */
415 if (mfp->ref > 1) {
416 --mfp->ref;
417 goto ret1;
421 * Move any BH's held by the file to the free list. We don't free the
422 * memory itself because we may be discarding the memory pool, and it's
423 * fairly expensive to reintegrate the buffers back into the region for
424 * no purpose.
426 mf_offset = OFFSET(dbmp, mfp);
427 for (bhp = SH_TAILQ_FIRST(&mp->bhq, __bh); bhp != NULL; bhp = nbhp) {
428 nbhp = SH_TAILQ_NEXT(bhp, q, __bh);
430 #ifdef DEBUG_NO_DIRTY
431 /* Complain if we find any blocks that were left dirty. */
432 if (F_ISSET(bhp, BH_DIRTY))
433 __db_err(dbmp->dbenv,
434 "%s: close: pgno %lu left dirty; ref %lu",
435 dbmfp->path, (u_long)bhp->pgno, (u_long)bhp->ref);
436 #endif
438 if (bhp->mf_offset == mf_offset) {
439 __memp_bhfree(dbmp, mfp, bhp, 0);
440 SH_TAILQ_INSERT_HEAD(&mp->bhfq, bhp, q, __bh);
444 /* Delete from the list of MPOOLFILEs. */
445 SH_TAILQ_REMOVE(&mp->mpfq, mfp, q, __mpoolfile);
447 /* Free the space. */
448 __db_shalloc_free(dbmp->addr, mfp);
449 __db_shalloc_free(dbmp->addr, ADDR(dbmp, mfp->path_off));
450 if (mfp->fileid_off != 0)
451 __db_shalloc_free(dbmp->addr, ADDR(dbmp, mfp->fileid_off));
452 if (mfp->pgcookie_off != 0)
453 __db_shalloc_free(dbmp->addr, ADDR(dbmp, mfp->pgcookie_off));
455 ret1: UNLOCKREGION(dbmp);
456 return (0);