1a770bfdf06c85882d97c74747000102e789cb42
[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.25 (Sleepycat) 8/27/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 *, DB_MPOOLFILE *,
32     int, 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         LOCKINIT(dbmp, &dbmfp->mutex);
101         dbmfp->dbmp = dbmp;
102         dbmfp->fd = -1;
103         if (LF_ISSET(DB_RDONLY))
104                 F_SET(dbmfp, MP_READONLY);
105
106         if (path == NULL) {
107                 if (LF_ISSET(DB_RDONLY)) {
108                         __db_err(dbenv,
109                             "memp_fopen: temporary files can't be readonly");
110                         ret = EINVAL;
111                         goto err;
112                 }
113                 dbmfp->path = (char *)TEMPORARY;
114                 F_SET(dbmfp, MP_PATH_TEMP);
115         } else {
116                 /* Calculate the real name for this file. */
117                 if ((ret = __db_appname(dbenv,
118                     DB_APP_DATA, NULL, path, NULL, &dbmfp->path)) != 0)
119                         goto err;
120                 F_SET(dbmfp, MP_PATH_ALLOC);
121
122
123                 /* Open the file. */
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));
128                         goto err;
129                 }
130
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)
134                         goto err;
135                 if (size % pagesize) {
136                         __db_err(dbenv,
137                             "%s: file size not a multiple of the pagesize",
138                             dbmfp->path);
139                         ret = EINVAL;
140                         goto err;
141                 }
142         }
143
144         /* Find/allocate the shared file object. */
145         if (needlock)
146                 LOCKREGION(dbmp);
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);
150         if (needlock)
151                 UNLOCKREGION(dbmp);
152         if (ret != 0)
153                 goto err;
154
155         dbmfp->mfp = mfp;
156
157         /*
158          * If a file:
159          *
160          *      + is read-only
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
164          *
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
169          * as here.
170          *
171          * XXX
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.
177          */
178 #define DB_MAXMMAPSIZE  (10 * 1024 * 1024)      /* 10 Mb. */
179         dbmfp->addr = NULL;
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);
184         if (mfp->can_mmap) {
185                 dbmfp->len = size;
186                 if (__db_mmap(dbmfp->fd, dbmfp->len, 1, 1, &dbmfp->addr) != 0) {
187                         mfp->can_mmap = 0;
188                         dbmfp->addr = NULL;
189                 }
190         }
191
192         LOCKHANDLE(dbmp, &dbmp->mutex);
193         TAILQ_INSERT_TAIL(&dbmp->dbmfq, dbmfp, q);
194         UNLOCKHANDLE(dbmp, &dbmp->mutex);
195
196         *retp = dbmfp;
197         return (0);
198
199 err:    if (F_ISSET(dbmfp, MP_PATH_ALLOC))
200                 FREES(dbmfp->path);
201         if (dbmfp->fd != -1)
202                 (void)__db_close(dbmfp->fd);
203         if (dbmfp != NULL)
204                 FREE(dbmfp, sizeof(DB_MPOOLFILE));
205         return (ret);
206 }
207
208 /*
209  * __memp_mf_open --
210  *      Open an MPOOLFILE.
211  */
212 static int
213 __memp_mf_open(dbmp, dbmfp,
214     ftype, readonly, pagesize, lsn_offset, pgcookie, fileid, istemp, retp)
215         DB_MPOOL *dbmp;
216         DB_MPOOLFILE *dbmfp;
217         int ftype, readonly, lsn_offset, istemp;
218         size_t pagesize;
219         DBT *pgcookie;
220         u_int8_t *fileid;
221         MPOOLFILE **retp;
222 {
223         MPOOLFILE *mfp;
224         int ret;
225         u_int8_t idbuf[DB_FILE_ID_LEN];
226         void *p;
227
228         /* Temporary files can't match previous files. */
229         if (istemp)
230                 goto alloc;
231
232         /*
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
235          * the party.
236          */
237         if (fileid == NULL) {
238                 if ((ret =
239                     __db_fileid(dbmp->dbenv, dbmfp->path, 0, idbuf)) != 0)
240                         return (ret);
241                 fileid = idbuf;
242         }
243
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))
247                 if (!memcmp(fileid,
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",
253                                     dbmfp->path);
254                                 ret = EINVAL;
255                                 mfp = NULL;
256                                 goto ret1;
257                         }
258                         /*
259                          * Found it: increment the reference count and update
260                          * the mmap-able status.
261                          */
262                         ++mfp->ref;
263                         if (!readonly)
264                                 mfp->can_mmap = 0;
265                         goto ret1;
266                 }
267
268         /* Allocate a new MPOOLFILE. */
269 alloc:  if ((ret = __memp_ralloc(dbmp, sizeof(MPOOLFILE), NULL, &mfp)) != 0)
270                 goto ret1;
271
272         /* Initialize the structure. */
273         memset(mfp, 0, sizeof(MPOOLFILE));
274         mfp->ref = 1;
275         mfp->ftype = ftype;
276         mfp->lsn_off = lsn_offset;
277         mfp->stat.st_pagesize = pagesize;
278
279         /* Copy the file path into shared memory. */
280         if ((ret = __memp_ralloc(dbmp,
281             strlen(dbmfp->path) + 1, &mfp->path_off, &p)) != 0)
282                 goto err;
283         memcpy(p, dbmfp->path, strlen(dbmfp->path) + 1);
284
285         /* Copy the file identification string into shared memory. */
286         if (istemp)
287                 mfp->fileid_off = 0;
288         else {
289                 if ((ret = __memp_ralloc(dbmp,
290                     DB_FILE_ID_LEN, &mfp->fileid_off, &p)) != 0)
291                         goto err;
292                 memcpy(p, fileid, DB_FILE_ID_LEN);
293         }
294
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;
299         } else {
300                 if ((ret = __memp_ralloc(dbmp,
301                     pgcookie->size, &mfp->pgcookie_off, &p)) != 0)
302                         goto err;
303                 memcpy(p, pgcookie->data, pgcookie->size);
304                 mfp->pgcookie_len = pgcookie->size;
305         }
306
307         /* Prepend the MPOOLFILE to the list of MPOOLFILE's. */
308         SH_TAILQ_INSERT_HEAD(&dbmp->mp->mpfq, mfp, q, __mpoolfile);
309
310         if (0) {
311 err:            if (mfp->path_off != 0)
312                         __db_shalloc_free(dbmp->addr,
313                             ADDR(dbmp, mfp->path_off));
314                 if (!istemp)
315                         __db_shalloc_free(dbmp->addr,
316                             ADDR(dbmp, mfp->fileid_off));
317                 if (mfp != NULL)
318                         __db_shalloc_free(dbmp->addr, mfp);
319                 mfp = NULL;
320         }
321
322 ret1:   *retp = mfp;
323         return (0);
324 }
325
326 /*
327  * memp_fclose --
328  *      Close a backing file for the memory pool.
329  */
330 int
331 memp_fclose(dbmfp)
332         DB_MPOOLFILE *dbmfp;
333 {
334         DB_MPOOL *dbmp;
335         int ret, t_ret;
336
337         dbmp = dbmfp->dbmp;
338         ret = 0;
339
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);
344
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);
349
350         /* Close the underlying MPOOLFILE. */
351         (void)__memp_mf_close(dbmp, dbmfp);
352
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));
357
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));
361                 if (ret != 0)
362                         t_ret = ret;
363         }
364
365         /* Potentially allocated path. */
366         if (F_ISSET(dbmfp, MP_PATH_ALLOC))
367                 FREES(dbmfp->path);
368
369         /* Free the DB_MPOOLFILE structure. */
370         FREE(dbmfp, sizeof(DB_MPOOLFILE));
371
372         return (ret);
373 }
374
375 /*
376  * __memp_mf_close --
377  *      Close down an MPOOLFILE.
378  */
379 static int
380 __memp_mf_close(dbmp, dbmfp)
381         DB_MPOOL *dbmp;
382         DB_MPOOLFILE *dbmfp;
383 {
384         BH *bhp, *nbhp;
385         MPOOL *mp;
386         MPOOLFILE *mfp;
387         size_t mf_offset;
388
389         mp = dbmp->mp;
390         mfp = dbmfp->mfp;
391
392         LOCKREGION(dbmp);
393
394         /* If more than a single reference, simply decrement. */
395         if (mfp->ref > 1) {
396                 --mfp->ref;
397                 goto ret1;
398         }
399
400         /*
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
404          * no purpose.
405          */
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);
409
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);
416 #endif
417
418                 if (bhp->mf_offset == mf_offset) {
419                         __memp_bhfree(dbmp, mfp, bhp, 0);
420                         SH_TAILQ_INSERT_HEAD(&mp->bhfq, bhp, q, __bh);
421                 }
422         }
423
424         /* Delete from the list of MPOOLFILEs. */
425         SH_TAILQ_REMOVE(&mp->mpfq, mfp, q, __mpoolfile);
426
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));
434
435 ret1:   UNLOCKREGION(dbmp);
436         return (0);
437 }