Update to db 2.3.10.
[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.27 (Sleepycat) 9/23/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 *)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_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;
128                 }
129
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;
140                 }
141         }
142
143         /*
144          * Find/allocate the shared file objects.  This includes allocating
145          * space for the per-process thread lock.
146          */
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);
157
158         if (ret != 0)
159                 goto err;
160
161         dbmfp->mfp = mfp;
162
163         /*
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
170          *
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.
176          *
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.
183          */
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;
197         }
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;
204                 }
205         }
206
207         LOCKHANDLE(dbmp, dbmp->mutexp);
208         TAILQ_INSERT_TAIL(&dbmp->dbmfq, dbmfp, q);
209         UNLOCKHANDLE(dbmp, dbmp->mutexp);
210
211         *retp = dbmfp;
212         return (0);
213
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.
217          */
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);
225 }
226
227 /*
228  * __memp_mf_open --
229  *      Open an MPOOLFILE.
230  */
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;
241 {
242         MPOOLFILE *mfp;
243         int ret;
244         u_int8_t idbuf[DB_FILE_ID_LEN];
245         void *p;
246
247         /* Temporary files can't match previous files. */
248         if (istemp)
249                 goto alloc;
250
251         /*
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.
255          */
256         if (fileid == NULL) {
257                 if ((ret =
258                     __db_fileid(dbmp->dbenv, dbmfp->path, 0, idbuf)) != 0)
259                         return (ret);
260                 fileid = idbuf;
261         }
262
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;
276                         }
277                         /* Found it: increment the reference count. */
278                         ++mfp->ref;
279                         goto ret1;
280                 }
281
282         /* Allocate a new MPOOLFILE. */
283 alloc:  if ((ret = __memp_ralloc(dbmp, sizeof(MPOOLFILE), NULL, &mfp)) != 0)
284                 goto ret1;
285
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;
293
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);
299
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);
308         }
309
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;
320         }
321
322         /* Prepend the MPOOLFILE to the list of MPOOLFILE's. */
323         SH_TAILQ_INSERT_HEAD(&dbmp->mp->mpfq, mfp, q, __mpoolfile);
324
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;
335         }
336
337 ret1:   *retp = mfp;
338         return (0);
339 }
340
341 /*
342  * memp_fclose --
343  *      Close a backing file for the memory pool.
344  */
345 int
346 memp_fclose(dbmfp)
347         DB_MPOOLFILE *dbmfp;
348 {
349         DB_MPOOL *dbmp;
350         int ret, t_ret;
351
352         dbmp = dbmfp->dbmp;
353         ret = 0;
354
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);
359
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);
364
365         /* Close the underlying MPOOLFILE. */
366         (void)__memp_mf_close(dbmp, dbmfp);
367
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));
372
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;
378         }
379
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);
387         }
388
389         /* Discard the DB_MPOOLFILE structure. */
390         FREE(dbmfp, sizeof(DB_MPOOLFILE));
391
392         return (ret);
393 }
394
395 /*
396  * __memp_mf_close --
397  *      Close down an MPOOLFILE.
398  */
399 static int
400 __memp_mf_close(dbmp, dbmfp)
401         DB_MPOOL *dbmp;
402         DB_MPOOLFILE *dbmfp;
403 {
404         BH *bhp, *nbhp;
405         MPOOL *mp;
406         MPOOLFILE *mfp;
407         size_t mf_offset;
408
409         mp = dbmp->mp;
410         mfp = dbmfp->mfp;
411
412         LOCKREGION(dbmp);
413
414         /* If more than a single reference, simply decrement. */
415         if (mfp->ref > 1) {
416                 --mfp->ref;
417                 goto ret1;
418         }
419
420         /*
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.
425          */
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);
429
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
437
438                 if (bhp->mf_offset == mf_offset) {
439                         __memp_bhfree(dbmp, mfp, bhp, 0);
440                         SH_TAILQ_INSERT_HEAD(&mp->bhfq, bhp, q, __bh);
441                 }
442         }
443
444         /* Delete from the list of MPOOLFILEs. */
445         SH_TAILQ_REMOVE(&mp->mpfq, mfp, q, __mpoolfile);
446
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));
454
455 ret1:   UNLOCKREGION(dbmp);
456         return (0);
457 }