2 * See the file LICENSE for redistribution information.
4 * Copyright (c) 1996, 1997
5 * Sleepycat Software. All rights reserved.
10 static const char sccsid[] = "@(#)mp_fopen.c 10.25 (Sleepycat) 8/27/97";
13 #ifndef NO_SYSTEM_INCLUDES
14 #include <sys/types.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 *, DB_MPOOLFILE *,
32 int, int, size_t, int, DBT *, u_int8_t *, int, MPOOLFILE **));
36 * Open a backing file for the memory pool.
39 memp_fopen(dbmp, path, ftype,
40 flags, mode, pagesize, lsn_offset, pgcookie, fileid, retp)
43 int ftype, flags, mode, lsn_offset;
51 /* Validate arguments. */
52 if ((ret = __db_fchk(dbmp->dbenv,
53 "memp_fopen", flags, DB_CREATE | DB_NOMMAP | DB_RDONLY)) != 0)
56 return (__memp_fopen(dbmp, path, ftype,
57 flags, mode, pagesize, lsn_offset, pgcookie, fileid, 1, retp));
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 **));
68 __memp_fopen(dbmp, path,
69 ftype, flags, mode, pagesize, lsn_offset, pgcookie, fileid, needlock, retp)
72 int ftype, flags, mode, lsn_offset, needlock;
87 /* Require a non-zero pagesize. */
89 __db_err(dbenv, "memp_fopen: pagesize not specified");
93 /* Allocate and initialize the per-process structure. */
95 (DB_MPOOLFILE *)calloc(1, sizeof(DB_MPOOLFILE))) == NULL) {
96 __db_err(dbenv, "%s: %s",
97 path == NULL ? TEMPORARY : path, strerror(ENOMEM));
100 LOCKINIT(dbmp, &dbmfp->mutex);
103 if (LF_ISSET(DB_RDONLY))
104 F_SET(dbmfp, MP_READONLY);
107 if (LF_ISSET(DB_RDONLY)) {
109 "memp_fopen: temporary files can't be readonly");
113 dbmfp->path = (char *)TEMPORARY;
114 F_SET(dbmfp, MP_PATH_TEMP);
116 /* Calculate the real name for this file. */
117 if ((ret = __db_appname(dbenv,
118 DB_APP_DATA, NULL, path, NULL, &dbmfp->path)) != 0)
120 F_SET(dbmfp, MP_PATH_ALLOC);
124 if ((ret = __db_fdopen(dbmfp->path,
125 LF_ISSET(DB_CREATE | DB_RDONLY), DB_CREATE | DB_RDONLY,
126 mode, &dbmfp->fd)) != 0) {
127 __db_err(dbenv, "%s: %s", dbmfp->path, strerror(ret));
131 /* Don't permit files that aren't a multiple of the pagesize. */
132 if ((ret = __db_stat(dbenv,
133 dbmfp->path, dbmfp->fd, &size, NULL)) != 0)
135 if (size % pagesize) {
137 "%s: file size not a multiple of the pagesize",
144 /* Find/allocate the shared file object. */
147 ret = __memp_mf_open(dbmp, dbmfp, ftype,
148 F_ISSET(dbmfp, MP_READONLY), pagesize,
149 lsn_offset, pgcookie, fileid, F_ISSET(dbmfp, MP_PATH_TEMP), &mfp);
161 * + doesn't require any pgin/pgout support
162 * + is less than mp_mmapsize bytes in size.
163 * + and the DB_NOMMAP flag wasn't set
165 * we can mmap it instead of reading/writing buffers. Don't do error
166 * checking based on the mmap call failure. We want to do normal I/O
167 * on the file if the reason we failed was because the file was on an
168 * NFS mounted partition, and we can fail in buffer I/O just as easily
172 * We'd like to test to see if the file is too big to mmap. Since we
173 * don't know what size or type off_t's or size_t's are, or the largest
174 * unsigned integral type is, or what random insanity the local C
175 * compiler will perpetrate, doing the comparison in a portable way is
176 * flatly impossible. Hope that mmap fails if the file is too large.
178 #define DB_MAXMMAPSIZE (10 * 1024 * 1024) /* 10 Mb. */
180 mfp->can_mmap = F_ISSET(dbmfp, MP_READONLY) &&
181 ftype == 0 && !LF_ISSET(DB_NOMMAP) && path != NULL &&
182 size <= (dbenv == NULL || dbenv->mp_mmapsize == 0 ?
183 DB_MAXMMAPSIZE : (off_t)dbenv->mp_mmapsize);
186 if (__db_mmap(dbmfp->fd, dbmfp->len, 1, 1, &dbmfp->addr) != 0) {
192 LOCKHANDLE(dbmp, &dbmp->mutex);
193 TAILQ_INSERT_TAIL(&dbmp->dbmfq, dbmfp, q);
194 UNLOCKHANDLE(dbmp, &dbmp->mutex);
199 err: if (F_ISSET(dbmfp, MP_PATH_ALLOC))
202 (void)__db_close(dbmfp->fd);
204 FREE(dbmfp, sizeof(DB_MPOOLFILE));
213 __memp_mf_open(dbmp, dbmfp,
214 ftype, readonly, pagesize, lsn_offset, pgcookie, fileid, istemp, retp)
217 int ftype, readonly, lsn_offset, istemp;
225 u_int8_t idbuf[DB_FILE_ID_LEN];
228 /* Temporary files can't match previous files. */
233 * Get the file id if we weren't give one. Generated file id's don't
234 * use timestamps, otherwise there'd be no chance of anyone joining
237 if (fileid == NULL) {
239 __db_fileid(dbmp->dbenv, dbmfp->path, 0, idbuf)) != 0)
244 /* Walk the list of MPOOLFILE's, looking for a matching file. */
245 for (mfp = SH_TAILQ_FIRST(&dbmp->mp->mpfq, __mpoolfile);
246 mfp != NULL; mfp = SH_TAILQ_NEXT(mfp, q, __mpoolfile))
248 ADDR(dbmp, mfp->fileid_off), DB_FILE_ID_LEN)) {
249 if (ftype != mfp->ftype ||
250 pagesize != mfp->stat.st_pagesize) {
251 __db_err(dbmp->dbenv,
252 "%s: ftype or pagesize changed",
259 * Found it: increment the reference count and update
260 * the mmap-able status.
268 /* Allocate a new MPOOLFILE. */
269 alloc: if ((ret = __memp_ralloc(dbmp, sizeof(MPOOLFILE), NULL, &mfp)) != 0)
272 /* Initialize the structure. */
273 memset(mfp, 0, sizeof(MPOOLFILE));
276 mfp->lsn_off = lsn_offset;
277 mfp->stat.st_pagesize = pagesize;
279 /* Copy the file path into shared memory. */
280 if ((ret = __memp_ralloc(dbmp,
281 strlen(dbmfp->path) + 1, &mfp->path_off, &p)) != 0)
283 memcpy(p, dbmfp->path, strlen(dbmfp->path) + 1);
285 /* Copy the file identification string into shared memory. */
289 if ((ret = __memp_ralloc(dbmp,
290 DB_FILE_ID_LEN, &mfp->fileid_off, &p)) != 0)
292 memcpy(p, fileid, DB_FILE_ID_LEN);
295 /* Copy the page cookie into shared memory. */
296 if (pgcookie == NULL || pgcookie->size == 0) {
297 mfp->pgcookie_len = 0;
298 mfp->pgcookie_off = 0;
300 if ((ret = __memp_ralloc(dbmp,
301 pgcookie->size, &mfp->pgcookie_off, &p)) != 0)
303 memcpy(p, pgcookie->data, pgcookie->size);
304 mfp->pgcookie_len = pgcookie->size;
307 /* Prepend the MPOOLFILE to the list of MPOOLFILE's. */
308 SH_TAILQ_INSERT_HEAD(&dbmp->mp->mpfq, mfp, q, __mpoolfile);
311 err: if (mfp->path_off != 0)
312 __db_shalloc_free(dbmp->addr,
313 ADDR(dbmp, mfp->path_off));
315 __db_shalloc_free(dbmp->addr,
316 ADDR(dbmp, mfp->fileid_off));
318 __db_shalloc_free(dbmp->addr, mfp);
328 * Close a backing file for the memory pool.
340 /* Complain if pinned blocks never returned. */
341 if (dbmfp->pinref != 0)
342 __db_err(dbmp->dbenv, "%s: close: %lu blocks left pinned",
343 dbmfp->path, (u_long)dbmfp->pinref);
345 /* Remove the DB_MPOOLFILE structure from the list. */
346 LOCKHANDLE(dbmp, &dbmp->mutex);
347 TAILQ_REMOVE(&dbmp->dbmfq, dbmfp, q);
348 UNLOCKHANDLE(dbmp, &dbmp->mutex);
350 /* Close the underlying MPOOLFILE. */
351 (void)__memp_mf_close(dbmp, dbmfp);
353 /* Discard any mmap information. */
354 if (dbmfp->addr != NULL &&
355 (ret = __db_munmap(dbmfp->addr, dbmfp->len)) != 0)
356 __db_err(dbmp->dbenv, "%s: %s", dbmfp->path, strerror(ret));
358 /* Close the file; temporary files may not yet have been created. */
359 if (dbmfp->fd != -1 && (t_ret = __db_close(dbmfp->fd)) != 0) {
360 __db_err(dbmp->dbenv, "%s: %s", dbmfp->path, strerror(t_ret));
365 /* Potentially allocated path. */
366 if (F_ISSET(dbmfp, MP_PATH_ALLOC))
369 /* Free the DB_MPOOLFILE structure. */
370 FREE(dbmfp, sizeof(DB_MPOOLFILE));
377 * Close down an MPOOLFILE.
380 __memp_mf_close(dbmp, dbmfp)
394 /* If more than a single reference, simply decrement. */
401 * Move any BH's held by the file to the free list. We don't free the
402 * memory itself because we may be discarding the memory pool, and it's
403 * fairly expensive to reintegrate the buffers back into the region for
406 mf_offset = OFFSET(dbmp, mfp);
407 for (bhp = SH_TAILQ_FIRST(&mp->bhq, __bh); bhp != NULL; bhp = nbhp) {
408 nbhp = SH_TAILQ_NEXT(bhp, q, __bh);
410 #ifdef DEBUG_NO_DIRTY
411 /* Complain if we find any blocks that were left dirty. */
412 if (F_ISSET(bhp, BH_DIRTY))
413 __db_err(dbmp->dbenv,
414 "%s: close: pgno %lu left dirty; ref %lu",
415 dbmfp->path, (u_long)bhp->pgno, (u_long)bhp->ref);
418 if (bhp->mf_offset == mf_offset) {
419 __memp_bhfree(dbmp, mfp, bhp, 0);
420 SH_TAILQ_INSERT_HEAD(&mp->bhfq, bhp, q, __bh);
424 /* Delete from the list of MPOOLFILEs. */
425 SH_TAILQ_REMOVE(&mp->mpfq, mfp, q, __mpoolfile);
427 /* Free the space. */
428 __db_shalloc_free(dbmp->addr, mfp);
429 __db_shalloc_free(dbmp->addr, ADDR(dbmp, mfp->path_off));
430 if (mfp->fileid_off != 0)
431 __db_shalloc_free(dbmp->addr, ADDR(dbmp, mfp->fileid_off));
432 if (mfp->pgcookie_off != 0)
433 __db_shalloc_free(dbmp->addr, ADDR(dbmp, mfp->pgcookie_off));
435 ret1: UNLOCKREGION(dbmp);