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