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 * 4. 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
33#if defined(LIBC_SCCS) && !defined(lint)
34static char sccsid[] = "@(#)rec_open.c	8.10 (Berkeley) 9/1/94";
35#endif /* LIBC_SCCS and not lint */
36#include <sys/cdefs.h>
37__FBSDID("$FreeBSD$");
38
39#include "namespace.h"
40#include <sys/types.h>
41#include <sys/mman.h>
42#include <sys/stat.h>
43
44#include <errno.h>
45#include <fcntl.h>
46#include <limits.h>
47#include <stddef.h>
48#include <stdio.h>
49#include <unistd.h>
50#include "un-namespace.h"
51
52#include <db.h>
53#include "recno.h"
54
55DB *
56__rec_open(const char *fname, int flags, int mode, const RECNOINFO *openinfo,
57    int dflags)
58{
59	BTREE *t;
60	BTREEINFO btopeninfo;
61	DB *dbp;
62	PAGE *h;
63	struct stat sb;
64	int rfd, sverrno;
65
66	/* Open the user's file -- if this fails, we're done. */
67	if (fname != NULL && (rfd = _open(fname, flags, mode)) < 0)
68		return (NULL);
69
70	/* Create a btree in memory (backed by disk). */
71	dbp = NULL;
72	if (openinfo) {
73		if (openinfo->flags & ~(R_FIXEDLEN | R_NOKEY | R_SNAPSHOT))
74			goto einval;
75		btopeninfo.flags = 0;
76		btopeninfo.cachesize = openinfo->cachesize;
77		btopeninfo.maxkeypage = 0;
78		btopeninfo.minkeypage = 0;
79		btopeninfo.psize = openinfo->psize;
80		btopeninfo.compare = NULL;
81		btopeninfo.prefix = NULL;
82		btopeninfo.lorder = openinfo->lorder;
83		dbp = __bt_open(openinfo->bfname,
84		    O_RDWR, S_IRUSR | S_IWUSR, &btopeninfo, dflags);
85	} else
86		dbp = __bt_open(NULL, O_RDWR, S_IRUSR | S_IWUSR, NULL, dflags);
87	if (dbp == NULL)
88		goto err;
89
90	/*
91	 * Some fields in the tree structure are recno specific.  Fill them
92	 * in and make the btree structure look like a recno structure.  We
93	 * don't change the bt_ovflsize value, it's close enough and slightly
94	 * bigger.
95	 */
96	t = dbp->internal;
97	if (openinfo) {
98		if (openinfo->flags & R_FIXEDLEN) {
99			F_SET(t, R_FIXLEN);
100			t->bt_reclen = openinfo->reclen;
101			if (t->bt_reclen == 0)
102				goto einval;
103		}
104		t->bt_bval = openinfo->bval;
105	} else
106		t->bt_bval = '\n';
107
108	F_SET(t, R_RECNO);
109	if (fname == NULL)
110		F_SET(t, R_EOF | R_INMEM);
111	else
112		t->bt_rfd = rfd;
113
114	if (fname != NULL) {
115		/*
116		 * In 4.4BSD, stat(2) returns true for ISSOCK on pipes.
117		 * Unfortunately, that's not portable, so we use lseek
118		 * and check the errno values.
119		 */
120		errno = 0;
121		if (lseek(rfd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE) {
122			switch (flags & O_ACCMODE) {
123			case O_RDONLY:
124				F_SET(t, R_RDONLY);
125				break;
126			default:
127				goto einval;
128			}
129slow:			if ((t->bt_rfp = fdopen(rfd, "r")) == NULL)
130				goto err;
131			F_SET(t, R_CLOSEFP);
132			t->bt_irec =
133			    F_ISSET(t, R_FIXLEN) ? __rec_fpipe : __rec_vpipe;
134		} else {
135			switch (flags & O_ACCMODE) {
136			case O_RDONLY:
137				F_SET(t, R_RDONLY);
138				break;
139			case O_RDWR:
140				break;
141			default:
142				goto einval;
143			}
144
145			if (_fstat(rfd, &sb))
146				goto err;
147			/*
148			 * Kluge -- we'd like to test to see if the file is too
149			 * big to mmap.  Since, we don't know what size or type
150			 * off_t's or size_t's are, what the largest unsigned
151			 * integral type is, or what random insanity the local
152			 * C compiler will perpetrate, doing the comparison in
153			 * a portable way is flatly impossible.  Hope that mmap
154			 * fails if the file is too large.
155			 */
156			if (sb.st_size == 0)
157				F_SET(t, R_EOF);
158			else {
159#ifdef MMAP_NOT_AVAILABLE
160				/*
161				 * XXX
162				 * Mmap doesn't work correctly on many current
163				 * systems.  In particular, it can fail subtly,
164				 * with cache coherency problems.  Don't use it
165				 * for now.
166				 */
167				t->bt_msize = sb.st_size;
168				if ((t->bt_smap = mmap(NULL, t->bt_msize,
169				    PROT_READ, MAP_PRIVATE, rfd,
170				    (off_t)0)) == MAP_FAILED)
171					goto slow;
172				t->bt_cmap = t->bt_smap;
173				t->bt_emap = t->bt_smap + sb.st_size;
174				t->bt_irec = F_ISSET(t, R_FIXLEN) ?
175				    __rec_fmap : __rec_vmap;
176				F_SET(t, R_MEMMAPPED);
177#else
178				goto slow;
179#endif
180			}
181		}
182	}
183
184	/* Use the recno routines. */
185	dbp->close = __rec_close;
186	dbp->del = __rec_delete;
187	dbp->fd = __rec_fd;
188	dbp->get = __rec_get;
189	dbp->put = __rec_put;
190	dbp->seq = __rec_seq;
191	dbp->sync = __rec_sync;
192
193	/* If the root page was created, reset the flags. */
194	if ((h = mpool_get(t->bt_mp, P_ROOT, 0)) == NULL)
195		goto err;
196	if ((h->flags & P_TYPE) == P_BLEAF) {
197		F_CLR(h, P_TYPE);
198		F_SET(h, P_RLEAF);
199		mpool_put(t->bt_mp, h, MPOOL_DIRTY);
200	} else
201		mpool_put(t->bt_mp, h, 0);
202
203	if (openinfo && openinfo->flags & R_SNAPSHOT &&
204	    !F_ISSET(t, R_EOF | R_INMEM) &&
205	    t->bt_irec(t, MAX_REC_NUMBER) == RET_ERROR)
206		goto err;
207	return (dbp);
208
209einval:	errno = EINVAL;
210err:	sverrno = errno;
211	if (dbp != NULL)
212		(void)__bt_close(dbp);
213	if (fname != NULL)
214		(void)_close(rfd);
215	errno = sverrno;
216	return (NULL);
217}
218
219int
220__rec_fd(const DB *dbp)
221{
222	BTREE *t;
223
224	t = dbp->internal;
225
226	/* Toss any page pinned across calls. */
227	if (t->bt_pinned != NULL) {
228		mpool_put(t->bt_mp, t->bt_pinned, 0);
229		t->bt_pinned = NULL;
230	}
231
232	/* In-memory database can't have a file descriptor. */
233	if (F_ISSET(t, R_INMEM)) {
234		errno = ENOENT;
235		return (-1);
236	}
237	return (t->bt_rfd);
238}
239