Update from db-2.3.12.
[kopensolaris-gnu/glibc.git] / db2 / mp / mp_fopen.c
1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 1996, 1997
5  *      Sleepycat Software.  All rights reserved.
6  */
7 #include "config.h"
8
9 #ifndef lint
10 static const char sccsid[] = "@(#)mp_fopen.c    10.30 (Sleepycat) 10/25/97";
11 #endif /* not lint */
12
13 #ifndef NO_SYSTEM_INCLUDES
14 #include <sys/types.h>
15 #include <sys/mman.h>
16 #include <sys/stat.h>
17
18 #include <errno.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <unistd.h>
22 #endif
23
24 #include "db_int.h"
25 #include "shqueue.h"
26 #include "db_shash.h"
27 #include "mp.h"
28 #include "common_ext.h"
29
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 **));
33
34 /*
35  * memp_fopen --
36  *      Open a backing file for the memory pool.
37  */
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;
48 {
49         int ret;
50
51         /* Validate arguments. */
52         if ((ret = __db_fchk(dbmp->dbenv,
53             "memp_fopen", flags, DB_CREATE | DB_NOMMAP | DB_RDONLY)) != 0)
54                 return (ret);
55
56         return (__memp_fopen(dbmp, path, ftype,
57             flags, mode, pagesize, lsn_offset, pgcookie, fileid, 1, retp));
58 }
59
60 /*
61  * __memp_fopen --
62  *      Open a backing file for the memory pool; internal version.
63  *
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 **));
66  */
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;
77 {
78         DB_ENV *dbenv;
79         DB_MPOOLFILE *dbmfp;
80         MPOOLFILE *mfp;
81         off_t size;
82         int ret;
83
84         dbenv = dbmp->dbenv;
85         ret = 0;
86
87         /* Require a non-zero pagesize. */
88         if (pagesize == 0) {
89                 __db_err(dbenv, "memp_fopen: pagesize not specified");
90                 return (EINVAL);
91         }
92
93         /* Allocate and initialize the per-process structure. */
94         if ((dbmfp =
95             (DB_MPOOLFILE *)__db_calloc(1, sizeof(DB_MPOOLFILE))) == NULL) {
96                 __db_err(dbenv, "%s: %s",
97                     path == NULL ? TEMPORARY : path, strerror(ENOMEM));
98                 return (ENOMEM);
99         }
100         dbmfp->dbmp = dbmp;
101         dbmfp->fd = -1;
102         if (LF_ISSET(DB_RDONLY))
103                 F_SET(dbmfp, MP_READONLY);
104
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;
111                 }
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);
120
121
122                 /* Open the file. */
123                 if ((ret = __db_open(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;
128                 }
129
130                 /* Don't permit files that aren't a multiple of the pagesize. */
131                 if ((ret =
132                     __db_ioinfo(dbmfp->path, dbmfp->fd, &size, NULL)) != 0) {
133                         __db_err(dbenv, "%s: %s", dbmfp->path, strerror(ret));
134                         goto err;
135                 }
136                 if (size % pagesize) {
137                         __db_err(dbenv,
138                             "%s: file size not a multiple of the pagesize",
139                             dbmfp->path);
140                         ret = EINVAL;
141                         goto err;
142                 }
143         }
144
145         /*
146          * Find/allocate the shared file objects.  This includes allocating
147          * space for the per-process thread lock.
148          */
149         if (needlock)
150                 LOCKREGION(dbmp);
151         ret = __memp_mf_open(dbmp, dbmfp, ftype, pagesize,
152             lsn_offset, pgcookie, fileid, F_ISSET(dbmfp, MP_PATH_TEMP), &mfp);
153         if (ret == 0 &&
154             F_ISSET(dbmp, MP_LOCKHANDLE) && (ret =
155             __memp_ralloc(dbmp, sizeof(db_mutex_t), NULL, &dbmfp->mutexp)) == 0)
156                 LOCKINIT(dbmp, dbmfp->mutexp);
157         if (needlock)
158                 UNLOCKREGION(dbmp);
159
160         if (ret != 0)
161                 goto err;
162
163         dbmfp->mfp = mfp;
164
165         /*
166          * If a file:
167          *      + is read-only
168          *      + isn't temporary
169          *      + doesn't require any pgin/pgout support
170          *      + the DB_NOMMAP flag wasn't set
171          *      + and is less than mp_mmapsize bytes in size
172          *
173          * we can mmap it instead of reading/writing buffers.  Don't do error
174          * checking based on the mmap call failure.  We want to do normal I/O
175          * on the file if the reason we failed was because the file was on an
176          * NFS mounted partition, and we can fail in buffer I/O just as easily
177          * as here.
178          *
179          * XXX
180          * We'd like to test to see if the file is too big to mmap.  Since we
181          * don't know what size or type off_t's or size_t's are, or the largest
182          * unsigned integral type is, or what random insanity the local C
183          * compiler will perpetrate, doing the comparison in a portable way is
184          * flatly impossible.  Hope that mmap fails if the file is too large.
185          */
186 #define DB_MAXMMAPSIZE  (10 * 1024 * 1024)      /* 10 Mb. */
187         if (mfp->can_mmap) {
188                 if (!F_ISSET(dbmfp, MP_READONLY))
189                         mfp->can_mmap = 0;
190                 if (path == NULL)
191                         mfp->can_mmap = 0;
192                 if (ftype != 0)
193                         mfp->can_mmap = 0;
194                 if (LF_ISSET(DB_NOMMAP))
195                         mfp->can_mmap = 0;
196                 if (size > (dbenv == NULL || dbenv->mp_mmapsize == 0 ?
197                     DB_MAXMMAPSIZE : (off_t)dbenv->mp_mmapsize))
198                         mfp->can_mmap = 0;
199         }
200         dbmfp->addr = NULL;
201         if (mfp->can_mmap) {
202                 dbmfp->len = size;
203                 if (__db_map(dbmfp->fd, dbmfp->len, 1, 1, &dbmfp->addr) != 0) {
204                         mfp->can_mmap = 0;
205                         dbmfp->addr = NULL;
206                 }
207         }
208
209         LOCKHANDLE(dbmp, dbmp->mutexp);
210         TAILQ_INSERT_TAIL(&dbmp->dbmfq, dbmfp, q);
211         UNLOCKHANDLE(dbmp, dbmp->mutexp);
212
213         *retp = dbmfp;
214         return (0);
215
216 err:    /*
217          * Note that we do not have to free the thread mutex, because we
218          * never get to here after we have successfully allocated it.
219          */
220         if (F_ISSET(dbmfp, MP_PATH_ALLOC))
221                 FREES(dbmfp->path);
222         if (dbmfp->fd != -1)
223                 (void)__db_close(dbmfp->fd);
224         if (dbmfp != NULL)
225                 FREE(dbmfp, sizeof(DB_MPOOLFILE));
226         return (ret);
227 }
228
229 /*
230  * __memp_mf_open --
231  *      Open an MPOOLFILE.
232  */
233 static int
234 __memp_mf_open(dbmp, dbmfp,
235     ftype, pagesize, lsn_offset, pgcookie, fileid, istemp, retp)
236         DB_MPOOL *dbmp;
237         DB_MPOOLFILE *dbmfp;
238         int ftype, lsn_offset, istemp;
239         size_t pagesize;
240         DBT *pgcookie;
241         u_int8_t *fileid;
242         MPOOLFILE **retp;
243 {
244         MPOOLFILE *mfp;
245         int ret;
246         u_int8_t idbuf[DB_FILE_ID_LEN];
247         void *p;
248
249         /* Temporary files can't match previous files. */
250         if (istemp)
251                 goto alloc;
252
253         /*
254          * Get the file id if we weren't give one.  Generated file id's don't
255          * use timestamps, otherwise there'd be no chance of anyone joining
256          * the party.
257          */
258         if (fileid == NULL) {
259                 if ((ret =
260                     __db_fileid(dbmp->dbenv, dbmfp->path, 0, idbuf)) != 0)
261                         return (ret);
262                 fileid = idbuf;
263         }
264
265         /* Walk the list of MPOOLFILE's, looking for a matching file. */
266         for (mfp = SH_TAILQ_FIRST(&dbmp->mp->mpfq, __mpoolfile);
267             mfp != NULL; mfp = SH_TAILQ_NEXT(mfp, q, __mpoolfile))
268                 if (!memcmp(fileid,
269                     R_ADDR(dbmp, mfp->fileid_off), DB_FILE_ID_LEN)) {
270                         if (ftype != mfp->ftype ||
271                             pagesize != mfp->stat.st_pagesize) {
272                                 __db_err(dbmp->dbenv,
273                                     "%s: ftype or pagesize changed",
274                                     dbmfp->path);
275                                 ret = EINVAL;
276                                 mfp = NULL;
277                                 goto ret1;
278                         }
279                         /* Found it: increment the reference count. */
280                         ++mfp->ref;
281                         goto ret1;
282                 }
283
284         /* Allocate a new MPOOLFILE. */
285 alloc:  if ((ret = __memp_ralloc(dbmp, sizeof(MPOOLFILE), NULL, &mfp)) != 0)
286                 goto ret1;
287
288         /* Initialize the structure. */
289         memset(mfp, 0, sizeof(MPOOLFILE));
290         mfp->ref = 1;
291         mfp->ftype = ftype;
292         mfp->can_mmap = 1;
293         mfp->lsn_off = lsn_offset;
294         mfp->stat.st_pagesize = pagesize;
295
296         /* Copy the file path into shared memory. */
297         if ((ret = __memp_ralloc(dbmp,
298             strlen(dbmfp->path) + 1, &mfp->path_off, &p)) != 0)
299                 goto err;
300         memcpy(p, dbmfp->path, strlen(dbmfp->path) + 1);
301
302         /* Copy the file identification string into shared memory. */
303         if (istemp)
304                 mfp->fileid_off = 0;
305         else {
306                 if ((ret = __memp_ralloc(dbmp,
307                     DB_FILE_ID_LEN, &mfp->fileid_off, &p)) != 0)
308                         goto err;
309                 memcpy(p, fileid, DB_FILE_ID_LEN);
310         }
311
312         /* Copy the page cookie into shared memory. */
313         if (pgcookie == NULL || pgcookie->size == 0) {
314                 mfp->pgcookie_len = 0;
315                 mfp->pgcookie_off = 0;
316         } else {
317                 if ((ret = __memp_ralloc(dbmp,
318                     pgcookie->size, &mfp->pgcookie_off, &p)) != 0)
319                         goto err;
320                 memcpy(p, pgcookie->data, pgcookie->size);
321                 mfp->pgcookie_len = pgcookie->size;
322         }
323
324         /* Prepend the MPOOLFILE to the list of MPOOLFILE's. */
325         SH_TAILQ_INSERT_HEAD(&dbmp->mp->mpfq, mfp, q, __mpoolfile);
326
327         if (0) {
328 err:            if (mfp->path_off != 0)
329                         __db_shalloc_free(dbmp->addr,
330                             R_ADDR(dbmp, mfp->path_off));
331                 if (!istemp)
332                         __db_shalloc_free(dbmp->addr,
333                             R_ADDR(dbmp, mfp->fileid_off));
334                 if (mfp != NULL)
335                         __db_shalloc_free(dbmp->addr, mfp);
336                 mfp = NULL;
337         }
338
339 ret1:   *retp = mfp;
340         return (0);
341 }
342
343 /*
344  * memp_fclose --
345  *      Close a backing file for the memory pool.
346  */
347 int
348 memp_fclose(dbmfp)
349         DB_MPOOLFILE *dbmfp;
350 {
351         DB_MPOOL *dbmp;
352         int ret, t_ret;
353
354         dbmp = dbmfp->dbmp;
355         ret = 0;
356
357         /* Complain if pinned blocks never returned. */
358         if (dbmfp->pinref != 0)
359                 __db_err(dbmp->dbenv, "%s: close: %lu blocks left pinned",
360                     dbmfp->path, (u_long)dbmfp->pinref);
361
362         /* Remove the DB_MPOOLFILE structure from the list. */
363         LOCKHANDLE(dbmp, dbmp->mutexp);
364         TAILQ_REMOVE(&dbmp->dbmfq, dbmfp, q);
365         UNLOCKHANDLE(dbmp, dbmp->mutexp);
366
367         /* Close the underlying MPOOLFILE. */
368         (void)__memp_mf_close(dbmp, dbmfp);
369
370         /* Discard any mmap information. */
371         if (dbmfp->addr != NULL &&
372             (ret = __db_unmap(dbmfp->addr, dbmfp->len)) != 0)
373                 __db_err(dbmp->dbenv, "%s: %s", dbmfp->path, strerror(ret));
374
375         /* Close the file; temporary files may not yet have been created. */
376         if (dbmfp->fd != -1 && (t_ret = __db_close(dbmfp->fd)) != 0) {
377                 __db_err(dbmp->dbenv, "%s: %s", dbmfp->path, strerror(t_ret));
378                 if (ret != 0)
379                         t_ret = ret;
380         }
381
382         /* Free memory. */
383         if (F_ISSET(dbmfp, MP_PATH_ALLOC))
384                 FREES(dbmfp->path);
385         if (dbmfp->mutexp != NULL) {
386                 LOCKREGION(dbmp);
387                 __db_shalloc_free(dbmp->addr, dbmfp->mutexp);
388                 UNLOCKREGION(dbmp);
389         }
390
391         /* Discard the DB_MPOOLFILE structure. */
392         FREE(dbmfp, sizeof(DB_MPOOLFILE));
393
394         return (ret);
395 }
396
397 /*
398  * __memp_mf_close --
399  *      Close down an MPOOLFILE.
400  */
401 static int
402 __memp_mf_close(dbmp, dbmfp)
403         DB_MPOOL *dbmp;
404         DB_MPOOLFILE *dbmfp;
405 {
406         BH *bhp, *nbhp;
407         MPOOL *mp;
408         MPOOLFILE *mfp;
409         size_t mf_offset;
410
411         mp = dbmp->mp;
412         mfp = dbmfp->mfp;
413
414         LOCKREGION(dbmp);
415
416         /* If more than a single reference, simply decrement. */
417         if (mfp->ref > 1) {
418                 --mfp->ref;
419                 goto ret1;
420         }
421
422         /*
423          * Move any BH's held by the file to the free list.  We don't free the
424          * memory itself because we may be discarding the memory pool, and it's
425          * fairly expensive to reintegrate the buffers back into the region for
426          * no purpose.
427          */
428         mf_offset = R_OFFSET(dbmp, mfp);
429         for (bhp = SH_TAILQ_FIRST(&mp->bhq, __bh); bhp != NULL; bhp = nbhp) {
430                 nbhp = SH_TAILQ_NEXT(bhp, q, __bh);
431
432 #ifdef DEBUG_NO_DIRTY
433                 /* Complain if we find any blocks that were left dirty. */
434                 if (F_ISSET(bhp, BH_DIRTY))
435                         __db_err(dbmp->dbenv,
436                             "%s: close: pgno %lu left dirty; ref %lu",
437                             dbmfp->path, (u_long)bhp->pgno, (u_long)bhp->ref);
438 #endif
439
440                 if (bhp->mf_offset == mf_offset) {
441                         if (F_ISSET(bhp, BH_DIRTY)) {
442                                 ++mp->stat.st_page_clean;
443                                 --mp->stat.st_page_dirty;
444                         }
445                         __memp_bhfree(dbmp, mfp, bhp, 0);
446                         SH_TAILQ_INSERT_HEAD(&mp->bhfq, bhp, q, __bh);
447                 }
448         }
449
450         /* Delete from the list of MPOOLFILEs. */
451         SH_TAILQ_REMOVE(&mp->mpfq, mfp, q, __mpoolfile);
452
453         /* Free the space. */
454         __db_shalloc_free(dbmp->addr, mfp);
455         __db_shalloc_free(dbmp->addr, R_ADDR(dbmp, mfp->path_off));
456         if (mfp->fileid_off != 0)
457                 __db_shalloc_free(dbmp->addr, R_ADDR(dbmp, mfp->fileid_off));
458         if (mfp->pgcookie_off != 0)
459                 __db_shalloc_free(dbmp->addr, R_ADDR(dbmp, mfp->pgcookie_off));
460
461 ret1:   UNLOCKREGION(dbmp);
462         return (0);
463 }