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.27 (Sleepycat) 9/23/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
*,
32 DB_MPOOLFILE
*, 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
));
102 if (LF_ISSET(DB_RDONLY
))
103 F_SET(dbmfp
, MP_READONLY
);
106 if (LF_ISSET(DB_RDONLY
)) {
108 "memp_fopen: temporary files can't be readonly");
112 dbmfp
->path
= (char *)TEMPORARY
;
113 F_SET(dbmfp
, MP_PATH_TEMP
);
115 /* Calculate the real name for this file. */
116 if ((ret
= __db_appname(dbenv
,
117 DB_APP_DATA
, NULL
, path
, NULL
, &dbmfp
->path
)) != 0)
119 F_SET(dbmfp
, MP_PATH_ALLOC
);
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
));
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)
134 if (size
% pagesize
) {
136 "%s: file size not a multiple of the pagesize",
144 * Find/allocate the shared file objects. This includes allocating
145 * space for the per-process thread lock.
149 ret
= __memp_mf_open(dbmp
, dbmfp
, ftype
, pagesize
,
150 lsn_offset
, pgcookie
, fileid
, F_ISSET(dbmfp
, MP_PATH_TEMP
), &mfp
);
152 F_ISSET(dbmp
, MP_LOCKHANDLE
) && (ret
=
153 __memp_ralloc(dbmp
, sizeof(db_mutex_t
), NULL
, &dbmfp
->mutexp
)) == 0)
154 LOCKINIT(dbmp
, dbmfp
->mutexp
);
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
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. */
186 if (!F_ISSET(dbmfp
, MP_READONLY
))
192 if (LF_ISSET(DB_NOMMAP
))
194 if (size
> (dbenv
== NULL
|| dbenv
->mp_mmapsize
== 0 ?
195 DB_MAXMMAPSIZE
: (off_t
)dbenv
->mp_mmapsize
))
201 if (__db_mmap(dbmfp
->fd
, dbmfp
->len
, 1, 1, &dbmfp
->addr
) != 0) {
207 LOCKHANDLE(dbmp
, dbmp
->mutexp
);
208 TAILQ_INSERT_TAIL(&dbmp
->dbmfq
, dbmfp
, q
);
209 UNLOCKHANDLE(dbmp
, dbmp
->mutexp
);
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
))
221 (void)__db_close(dbmfp
->fd
);
223 FREE(dbmfp
, sizeof(DB_MPOOLFILE
));
232 __memp_mf_open(dbmp
, dbmfp
,
233 ftype
, pagesize
, lsn_offset
, pgcookie
, fileid
, istemp
, retp
)
236 int ftype
, lsn_offset
, istemp
;
244 u_int8_t idbuf
[DB_FILE_ID_LEN
];
247 /* Temporary files can't match previous files. */
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
256 if (fileid
== NULL
) {
258 __db_fileid(dbmp
->dbenv
, dbmfp
->path
, 0, idbuf
)) != 0)
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
))
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",
277 /* Found it: increment the reference count. */
282 /* Allocate a new MPOOLFILE. */
283 alloc
: if ((ret
= __memp_ralloc(dbmp
, sizeof(MPOOLFILE
), NULL
, &mfp
)) != 0)
286 /* Initialize the structure. */
287 memset(mfp
, 0, sizeof(MPOOLFILE
));
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)
298 memcpy(p
, dbmfp
->path
, strlen(dbmfp
->path
) + 1);
300 /* Copy the file identification string into shared memory. */
304 if ((ret
= __memp_ralloc(dbmp
,
305 DB_FILE_ID_LEN
, &mfp
->fileid_off
, &p
)) != 0)
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;
315 if ((ret
= __memp_ralloc(dbmp
,
316 pgcookie
->size
, &mfp
->pgcookie_off
, &p
)) != 0)
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
);
326 err
: if (mfp
->path_off
!= 0)
327 __db_shalloc_free(dbmp
->addr
,
328 ADDR(dbmp
, mfp
->path_off
));
330 __db_shalloc_free(dbmp
->addr
,
331 ADDR(dbmp
, mfp
->fileid_off
));
333 __db_shalloc_free(dbmp
->addr
, mfp
);
343 * Close a backing file for the memory pool.
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
));
381 if (F_ISSET(dbmfp
, MP_PATH_ALLOC
))
383 if (dbmfp
->mutexp
!= NULL
) {
385 __db_shalloc_free(dbmp
->addr
, dbmfp
->mutexp
);
389 /* Discard the DB_MPOOLFILE structure. */
390 FREE(dbmfp
, sizeof(DB_MPOOLFILE
));
397 * Close down an MPOOLFILE.
400 __memp_mf_close(dbmp
, dbmfp
)
414 /* If more than a single reference, simply decrement. */
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
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
);
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
);