xref: /dragonfly/lib/libc/db/btree/bt_open.c (revision 08acb08c0a6e4dcab346cdf0c24c7567d8a66858)
1 /*-
2  * Copyright (c) 1990, 1993, 1994
3  *        The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Mike Olson.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * @(#)bt_open.c    8.10 (Berkeley) 8/17/94
33  * $FreeBSD: head/lib/libc/db/btree/bt_open.c 190498 2009-03-28 07:31:02Z delphij $
34  */
35 
36 /*
37  * Implementation of btree access method for 4.4BSD.
38  *
39  * The design here was originally based on that of the btree access method
40  * used in the Postgres database system at UC Berkeley.  This implementation
41  * is wholly independent of the Postgres code.
42  */
43 
44 #include "namespace.h"
45 #include <sys/param.h>
46 #include <sys/stat.h>
47 #include <sys/file.h>
48 
49 #include <errno.h>
50 #include <fcntl.h>
51 #include <limits.h>
52 #include <signal.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <unistd.h>
57 #include "un-namespace.h"
58 
59 #include <db.h>
60 #include "btree.h"
61 
62 #ifdef DEBUG
63 #undef    MINPSIZE
64 #define   MINPSIZE  128
65 #endif
66 
67 static int byteorder(void);
68 static int nroot(BTREE *);
69 static int tmp(void);
70 
71 /*
72  * __BT_OPEN -- Open a btree.
73  *
74  * Creates and fills a DB struct, and calls the routine that actually
75  * opens the btree.
76  *
77  * Parameters:
78  *        fname:    filename (NULL for in-memory trees)
79  *        flags:    open flag bits
80  *        mode:     open permission bits
81  *        b:        BTREEINFO pointer
82  *
83  * Returns:
84  *        NULL on failure, pointer to DB on success.
85  *
86  */
87 DB *
__bt_open(const char * fname,int flags,mode_t mode,const BTREEINFO * openinfo,int dflags)88 __bt_open(const char *fname, int flags, mode_t mode, const BTREEINFO *openinfo,
89             int dflags)
90 {
91           struct stat sb;
92           BTMETA m;
93           BTREE *t;
94           BTREEINFO b;
95           DB *dbp;
96           pgno_t ncache;
97           ssize_t nr;
98           int machine_lorder, saved_errno;
99 
100           t = NULL;
101 
102           /*
103            * Intention is to make sure all of the user's selections are okay
104            * here and then use them without checking.  Can't be complete, since
105            * we don't know the right page size, lorder or flags until the backing
106            * file is opened.  Also, the file's page size can cause the cachesize
107            * to change.
108            */
109           machine_lorder = byteorder();
110           if (openinfo) {
111                     b = *openinfo;
112 
113                     /* Flags: R_DUP. */
114                     if (b.flags & ~(R_DUP))
115                               goto einval;
116 
117                     /*
118                      * Page size must be indx_t aligned and >= MINPSIZE.  Default
119                      * page size is set farther on, based on the underlying file
120                      * transfer size.
121                      */
122                     if (b.psize &&
123                         (b.psize < MINPSIZE || b.psize > MAX_PAGE_OFFSET + 1 ||
124                         b.psize & (sizeof(indx_t) - 1) ))
125                               goto einval;
126 
127                     /* Minimum number of keys per page; absolute minimum is 2. */
128                     if (b.minkeypage) {
129                               if (b.minkeypage < 2)
130                                         goto einval;
131                     } else
132                               b.minkeypage = DEFMINKEYPAGE;
133 
134                     /* If no comparison, use default comparison and prefix. */
135                     if (b.compare == NULL) {
136                               b.compare = __bt_defcmp;
137                               if (b.prefix == NULL)
138                                         b.prefix = __bt_defpfx;
139                     }
140 
141                     if (b.lorder == 0)
142                               b.lorder = machine_lorder;
143           } else {
144                     b.compare = __bt_defcmp;
145                     b.cachesize = 0;
146                     b.flags = 0;
147                     b.lorder = machine_lorder;
148                     b.minkeypage = DEFMINKEYPAGE;
149                     b.prefix = __bt_defpfx;
150                     b.psize = 0;
151           }
152 
153           /* Check for the ubiquitous PDP-11. */
154           if (b.lorder != BIG_ENDIAN && b.lorder != LITTLE_ENDIAN)
155                     goto einval;
156 
157           /* Allocate and initialize DB and BTREE structures. */
158           if ((t = (BTREE *)calloc(1, sizeof(BTREE))) == NULL)
159                     goto err;
160           t->bt_fd = -1;                          /* Don't close unopened fd on error. */
161           t->bt_lorder = b.lorder;
162           t->bt_order = NOT;
163           t->bt_cmp = b.compare;
164           t->bt_pfx = b.prefix;
165           t->bt_rfd = -1;
166 
167           if ((t->bt_dbp = dbp = (DB *)calloc(1, sizeof(DB))) == NULL)
168                     goto err;
169           if (t->bt_lorder != machine_lorder)
170                     F_SET(t, B_NEEDSWAP);
171 
172           dbp->type = DB_BTREE;
173           dbp->internal = t;
174           dbp->close = __bt_close;
175           dbp->del = __bt_delete;
176           dbp->fd = __bt_fd;
177           dbp->get = __bt_get;
178           dbp->put = __bt_put;
179           dbp->seq = __bt_seq;
180           dbp->sync = __bt_sync;
181 
182           /*
183            * If no file name was supplied, this is an in-memory btree and we
184            * open a backing temporary file.  Otherwise, it's a disk-based tree.
185            */
186           if (fname) {
187                     switch (flags & O_ACCMODE) {
188                     case O_RDONLY:
189                               F_SET(t, B_RDONLY);
190                               break;
191                     case O_RDWR:
192                               break;
193                     case O_WRONLY:
194                     default:
195                               goto einval;
196                     }
197 
198                     if ((t->bt_fd = _open(fname, flags | O_CLOEXEC, mode)) < 0)
199                               goto err;
200 
201           } else {
202                     if ((flags & O_ACCMODE) != O_RDWR)
203                               goto einval;
204                     if ((t->bt_fd = tmp()) == -1)
205                               goto err;
206                     F_SET(t, B_INMEM);
207           }
208 
209           if (_fcntl(t->bt_fd, F_SETFD, 1) == -1)
210                     goto err;
211 
212           if (_fstat(t->bt_fd, &sb))
213                     goto err;
214           if (sb.st_size) {
215                     if ((nr = _read(t->bt_fd, &m, sizeof(BTMETA))) < 0)
216                               goto err;
217                     if (nr != sizeof(BTMETA))
218                               goto eftype;
219 
220                     /*
221                      * Read in the meta-data.  This can change the notion of what
222                      * the lorder, page size and flags are, and, when the page size
223                      * changes, the cachesize value can change too.  If the user
224                      * specified the wrong byte order for an existing database, we
225                      * don't bother to return an error, we just clear the NEEDSWAP
226                      * bit.
227                      */
228                     if (m.magic == BTREEMAGIC)
229                               F_CLR(t, B_NEEDSWAP);
230                     else {
231                               F_SET(t, B_NEEDSWAP);
232                               M_32_SWAP(m.magic);
233                               M_32_SWAP(m.version);
234                               M_32_SWAP(m.psize);
235                               M_32_SWAP(m.free);
236                               M_32_SWAP(m.nrecs);
237                               M_32_SWAP(m.flags);
238                     }
239                     if (m.magic != BTREEMAGIC || m.version != BTREEVERSION)
240                               goto eftype;
241                     if (m.psize < MINPSIZE || m.psize > MAX_PAGE_OFFSET + 1 ||
242                         m.psize & (sizeof(indx_t) - 1) )
243                               goto eftype;
244                     if (m.flags & ~SAVEMETA)
245                               goto eftype;
246                     b.psize = m.psize;
247                     F_SET(t, m.flags);
248                     t->bt_free = m.free;
249                     t->bt_nrecs = m.nrecs;
250           } else {
251                     /*
252                      * Set the page size to the best value for I/O to this file.
253                      * Don't overflow the page offset type.
254                      *
255                      * Stop using st_blksize, its meaningless on a modern system
256                      * and can cause db to operate inefficiently.  Instead we
257                      * use NOMPSIZE.
258                      */
259                     if (b.psize == 0) {
260                               b.psize = NOMPSIZE;
261                               if (b.psize < MINPSIZE)
262                                         b.psize = MINPSIZE;
263                               if (b.psize > MAX_PAGE_OFFSET + 1)
264                                         b.psize = MAX_PAGE_OFFSET + 1;
265                     }
266 
267                     /* Set flag if duplicates permitted. */
268                     if (!(b.flags & R_DUP))
269                               F_SET(t, B_NODUPS);
270 
271                     t->bt_free = P_INVALID;
272                     t->bt_nrecs = 0;
273                     F_SET(t, B_METADIRTY);
274           }
275 
276           t->bt_psize = b.psize;
277 
278           /* Set the cache size; must be a multiple of the page size. */
279           if (b.cachesize && b.cachesize & (b.psize - 1) )
280                     b.cachesize += (~b.cachesize & (b.psize - 1) ) + 1;
281           if (b.cachesize < b.psize * MINCACHE)
282                     b.cachesize = b.psize * MINCACHE;
283 
284           /* Calculate number of pages to cache. */
285           ncache = (b.cachesize + t->bt_psize - 1) / t->bt_psize;
286 
287           /*
288            * The btree data structure requires that at least two keys can fit on
289            * a page, but other than that there's no fixed requirement.  The user
290            * specified a minimum number per page, and we translated that into the
291            * number of bytes a key/data pair can use before being placed on an
292            * overflow page.  This calculation includes the page header, the size
293            * of the index referencing the leaf item and the size of the leaf item
294            * structure.  Also, don't let the user specify a minkeypage such that
295            * a key/data pair won't fit even if both key and data are on overflow
296            * pages.
297            */
298           t->bt_ovflsize = (t->bt_psize - BTDATAOFF) / b.minkeypage -
299               (sizeof(indx_t) + NBLEAFDBT(0, 0));
300           if (t->bt_ovflsize < NBLEAFDBT(NOVFLSIZE, NOVFLSIZE) + sizeof(indx_t))
301                     t->bt_ovflsize =
302                         NBLEAFDBT(NOVFLSIZE, NOVFLSIZE) + sizeof(indx_t);
303 
304           /* Initialize the buffer pool. */
305           if ((t->bt_mp =
306               mpool_open(NULL, t->bt_fd, t->bt_psize, ncache)) == NULL)
307                     goto err;
308           if (!F_ISSET(t, B_INMEM))
309                     mpool_filter(t->bt_mp, __bt_pgin, __bt_pgout, t);
310 
311           /* Create a root page if new tree. */
312           if (nroot(t) == RET_ERROR)
313                     goto err;
314 
315           /* Global flags. */
316           if (dflags & DB_LOCK)
317                     F_SET(t, B_DB_LOCK);
318           if (dflags & DB_SHMEM)
319                     F_SET(t, B_DB_SHMEM);
320           if (dflags & DB_TXN)
321                     F_SET(t, B_DB_TXN);
322 
323           return (dbp);
324 
325 einval:   errno = EINVAL;
326           goto err;
327 
328 eftype:   errno = EFTYPE;
329           goto err;
330 
331 err:      saved_errno = errno;
332           if (t) {
333                     if (t->bt_dbp)
334                               free(t->bt_dbp);
335                     if (t->bt_fd != -1)
336                               _close(t->bt_fd);
337                     free(t);
338           }
339           errno = saved_errno;
340           return (NULL);
341 }
342 
343 /*
344  * NROOT -- Create the root of a new tree.
345  *
346  * Parameters:
347  *        t:        tree
348  *
349  * Returns:
350  *        RET_ERROR, RET_SUCCESS
351  */
352 static int
nroot(BTREE * t)353 nroot(BTREE *t)
354 {
355           PAGE *meta, *root;
356           pgno_t npg;
357 
358           if ((root = mpool_get(t->bt_mp, 1, 0)) != NULL) {
359                     if (root->lower == 0 &&
360                         root->pgno == 0 &&
361                         root->linp[0] == 0) {
362                               mpool_delete(t->bt_mp, root);
363                               errno = EINVAL;
364                     } else {
365                               mpool_put(t->bt_mp, root, 0);
366                               return (RET_SUCCESS);
367                     }
368           }
369           if (errno != EINVAL)                    /* It's OK to not exist. */
370                     return (RET_ERROR);
371           errno = 0;
372 
373           if ((meta = mpool_new(t->bt_mp, &npg, MPOOL_PAGE_NEXT)) == NULL)
374                     return (RET_ERROR);
375 
376           if ((root = mpool_new(t->bt_mp, &npg, MPOOL_PAGE_NEXT)) == NULL)
377                     return (RET_ERROR);
378 
379           if (npg != P_ROOT)
380                     return (RET_ERROR);
381           root->pgno = npg;
382           root->prevpg = root->nextpg = P_INVALID;
383           root->lower = BTDATAOFF;
384           root->upper = t->bt_psize;
385           root->flags = P_BLEAF;
386           memset(meta, 0, t->bt_psize);
387           mpool_put(t->bt_mp, meta, MPOOL_DIRTY);
388           mpool_put(t->bt_mp, root, MPOOL_DIRTY);
389           return (RET_SUCCESS);
390 }
391 
392 static int
tmp(void)393 tmp(void)
394 {
395           sigset_t set, oset;
396           int fd, len;
397           char *envtmp = NULL;
398           char path[MAXPATHLEN];
399 
400           if (issetugid() == 0)
401                     envtmp = getenv("TMPDIR");
402           len = snprintf(path,
403               sizeof(path), "%s/bt.XXXXXXXXXX", envtmp ? envtmp : "/tmp");
404           if (len < 0 || len >= (int)sizeof(path)) {
405                     errno = ENAMETOOLONG;
406                     return(-1);
407           }
408 
409           sigfillset(&set);
410           _sigprocmask(SIG_BLOCK, &set, &oset);
411           if ((fd = mkostemp(path, O_CLOEXEC)) != -1)
412                     _unlink(path);
413           _sigprocmask(SIG_SETMASK, &oset, NULL);
414           return(fd);
415 }
416 
417 static int
byteorder(void)418 byteorder(void)
419 {
420           uint32_t x;
421           unsigned char *p;
422 
423           x = 0x01020304;
424           p = (unsigned char *)&x;
425           switch (*p) {
426           case 1:
427                     return (BIG_ENDIAN);
428           case 4:
429                     return (LITTLE_ENDIAN);
430           default:
431                     return (0);
432           }
433 }
434 
435 int
__bt_fd(const DB * dbp)436 __bt_fd(const DB *dbp)
437 {
438           BTREE *t;
439 
440           t = dbp->internal;
441 
442           /* Toss any page pinned across calls. */
443           if (t->bt_pinned != NULL) {
444                     mpool_put(t->bt_mp, t->bt_pinned, 0);
445                     t->bt_pinned = NULL;
446           }
447 
448           /* In-memory database can't have a file descriptor. */
449           if (F_ISSET(t, B_INMEM)) {
450                     errno = ENOENT;
451                     return (-1);
452           }
453           return (t->bt_fd);
454 }
455