2N/A/*-
2N/A * Copyright (c) 1990, 1993, 1994
2N/A * The Regents of the University of California. All rights reserved.
2N/A *
2N/A * This code is derived from software contributed to Berkeley by
2N/A * Mike Olson.
2N/A *
2N/A * Redistribution and use in source and binary forms, with or without
2N/A * modification, are permitted provided that the following conditions
2N/A * are met:
2N/A * 1. Redistributions of source code must retain the above copyright
2N/A * notice, this list of conditions and the following disclaimer.
2N/A * 2. Redistributions in binary form must reproduce the above copyright
2N/A * notice, this list of conditions and the following disclaimer in the
2N/A * documentation and/or other materials provided with the distribution.
2N/A * 3. All advertising materials mentioning features or use of this software
2N/A * must display the following acknowledgement:
2N/A * This product includes software developed by the University of
2N/A * California, Berkeley and its contributors.
2N/A * 4. Neither the name of the University nor the names of its contributors
2N/A * may be used to endorse or promote products derived from this software
2N/A * without specific prior written permission.
2N/A *
2N/A * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2N/A * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2N/A * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2N/A * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2N/A * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2N/A * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2N/A * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2N/A * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2N/A * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2N/A * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2N/A * SUCH DAMAGE.
2N/A */
2N/A
2N/A#if defined(LIBC_SCCS) && !defined(lint)
2N/Astatic char sccsid[] = "@(#)rec_open.c 8.12 (Berkeley) 11/18/94";
2N/A#endif /* LIBC_SCCS and not lint */
2N/A
2N/A#include <sys/types.h>
2N/A#ifdef RECNO_USE_MMAP
2N/A#include <sys/mman.h>
2N/A#endif
2N/A#include <sys/stat.h>
2N/A
2N/A#include <errno.h>
2N/A#include <fcntl.h>
2N/A#include <limits.h>
2N/A#include <stddef.h>
2N/A#include <stdio.h>
2N/A#include <unistd.h>
2N/A
2N/A#include "db-int.h"
2N/A#include "recno.h"
2N/A
2N/ADB *
2N/A__rec_open(fname, flags, mode, openinfo, dflags)
2N/A const char *fname;
2N/A int flags, mode, dflags;
2N/A const RECNOINFO *openinfo;
2N/A{
2N/A BTREE *t;
2N/A BTREEINFO btopeninfo;
2N/A DB *dbp;
2N/A PAGE *h;
2N/A struct stat sb;
2N/A int rfd, sverrno;
2N/A
2N/A /* Open the user's file -- if this fails, we're done. */
2N/A if (fname != NULL && (rfd = open(fname, flags | O_BINARY, mode)) < 0)
2N/A return (NULL);
2N/A
2N/A if (fname != NULL && fcntl(rfd, F_SETFD, 1) == -1) {
2N/A close(rfd);
2N/A return NULL;
2N/A }
2N/A
2N/A /* Create a btree in memory (backed by disk). */
2N/A dbp = NULL;
2N/A if (openinfo) {
2N/A if (openinfo->flags & ~(R_FIXEDLEN | R_NOKEY | R_SNAPSHOT))
2N/A goto einval;
2N/A btopeninfo.flags = 0;
2N/A btopeninfo.cachesize = openinfo->cachesize;
2N/A btopeninfo.maxkeypage = 0;
2N/A btopeninfo.minkeypage = 0;
2N/A btopeninfo.psize = openinfo->psize;
2N/A btopeninfo.compare = NULL;
2N/A btopeninfo.prefix = NULL;
2N/A btopeninfo.lorder = openinfo->lorder;
2N/A dbp = __bt_open(openinfo->bfname,
2N/A O_RDWR | O_BINARY, S_IRUSR | S_IWUSR, &btopeninfo, dflags);
2N/A } else
2N/A dbp = __bt_open(NULL, O_RDWR | O_BINARY, S_IRUSR | S_IWUSR, NULL, dflags);
2N/A if (dbp == NULL)
2N/A goto err;
2N/A
2N/A /*
2N/A * Some fields in the tree structure are recno specific. Fill them
2N/A * in and make the btree structure look like a recno structure. We
2N/A * don't change the bt_ovflsize value, it's close enough and slightly
2N/A * bigger.
2N/A */
2N/A t = dbp->internal;
2N/A if (openinfo) {
2N/A if (openinfo->flags & R_FIXEDLEN) {
2N/A F_SET(t, R_FIXLEN);
2N/A t->bt_reclen = openinfo->reclen;
2N/A if (t->bt_reclen == 0)
2N/A goto einval;
2N/A }
2N/A t->bt_bval = openinfo->bval;
2N/A } else
2N/A t->bt_bval = '\n';
2N/A
2N/A F_SET(t, R_RECNO);
2N/A if (fname == NULL)
2N/A F_SET(t, R_EOF | R_INMEM);
2N/A else
2N/A t->bt_rfd = rfd;
2N/A
2N/A if (fname != NULL) {
2N/A /*
2N/A * In 4.4BSD, stat(2) returns true for ISSOCK on pipes.
2N/A * Unfortunately, that's not portable, so we use lseek
2N/A * and check the errno values.
2N/A */
2N/A errno = 0;
2N/A if (lseek(rfd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE) {
2N/A switch (flags & O_ACCMODE) {
2N/A case O_RDONLY:
2N/A F_SET(t, R_RDONLY);
2N/A break;
2N/A default:
2N/A goto einval;
2N/A }
2N/Aslow: if ((t->bt_rfp = fdopen(rfd, "rb")) == NULL)
2N/A goto err;
2N/A F_SET(t, R_CLOSEFP);
2N/A t->bt_irec =
2N/A F_ISSET(t, R_FIXLEN) ? __rec_fpipe : __rec_vpipe;
2N/A } else {
2N/A switch (flags & O_ACCMODE) {
2N/A case O_RDONLY:
2N/A F_SET(t, R_RDONLY);
2N/A break;
2N/A case O_RDWR:
2N/A break;
2N/A default:
2N/A goto einval;
2N/A }
2N/A
2N/A if (fstat(rfd, &sb))
2N/A goto err;
2N/A /*
2N/A * Kluge -- we'd like to test to see if the file is too
2N/A * big to mmap. Since, we don't know what size or type
2N/A * off_t's or size_t's are, what the largest unsigned
2N/A * integral type is, or what random insanity the local
2N/A * C compiler will perpetrate, doing the comparison in
2N/A * a portable way is flatly impossible. Hope that mmap
2N/A * fails if the file is too large.
2N/A */
2N/A if (sb.st_size == 0)
2N/A F_SET(t, R_EOF);
2N/A else {
2N/A#ifdef RECNO_USE_MMAP
2N/A /*
2N/A * XXX
2N/A * Mmap doesn't work correctly on many current
2N/A * systems. In particular, it can fail subtly,
2N/A * with cache coherency problems. Don't use it
2N/A * for now.
2N/A */
2N/A t->bt_msize = sb.st_size;
2N/A if ((t->bt_smap = mmap(NULL, t->bt_msize,
2N/A PROT_READ, MAP_PRIVATE, rfd,
2N/A (off_t)0)) == (caddr_t)-1)
2N/A goto slow;
2N/A t->bt_cmap = t->bt_smap;
2N/A t->bt_emap = t->bt_smap + sb.st_size;
2N/A t->bt_irec = F_ISSET(t, R_FIXLEN) ?
2N/A __rec_fmap : __rec_vmap;
2N/A F_SET(t, R_MEMMAPPED);
2N/A#else
2N/A goto slow;
2N/A#endif
2N/A }
2N/A }
2N/A }
2N/A
2N/A /* Use the recno routines. */
2N/A dbp->close = __rec_close;
2N/A dbp->del = __rec_delete;
2N/A dbp->fd = __rec_fd;
2N/A dbp->get = __rec_get;
2N/A dbp->put = __rec_put;
2N/A dbp->seq = __rec_seq;
2N/A dbp->sync = __rec_sync;
2N/A
2N/A /* If the root page was created, reset the flags. */
2N/A if ((h = mpool_get(t->bt_mp, P_ROOT, 0)) == NULL)
2N/A goto err;
2N/A if ((h->flags & P_TYPE) == P_BLEAF) {
2N/A F_CLR(h, P_TYPE);
2N/A F_SET(h, P_RLEAF);
2N/A mpool_put(t->bt_mp, h, MPOOL_DIRTY);
2N/A } else
2N/A mpool_put(t->bt_mp, h, 0);
2N/A
2N/A if (openinfo && openinfo->flags & R_SNAPSHOT &&
2N/A !F_ISSET(t, R_EOF | R_INMEM) &&
2N/A t->bt_irec(t, MAX_REC_NUMBER) == RET_ERROR)
2N/A goto err;
2N/A return (dbp);
2N/A
2N/Aeinval: errno = EINVAL;
2N/Aerr: sverrno = errno;
2N/A if (dbp != NULL)
2N/A (void)__bt_close(dbp);
2N/A if (fname != NULL)
2N/A (void)close(rfd);
2N/A errno = sverrno;
2N/A return (NULL);
2N/A}
2N/A
2N/Aint
2N/A__rec_fd(dbp)
2N/A const DB *dbp;
2N/A{
2N/A BTREE *t;
2N/A
2N/A t = dbp->internal;
2N/A
2N/A /* Toss any page pinned across calls. */
2N/A if (t->bt_pinned != NULL) {
2N/A mpool_put(t->bt_mp, t->bt_pinned, 0);
2N/A t->bt_pinned = NULL;
2N/A }
2N/A
2N/A /* In-memory database can't have a file descriptor. */
2N/A if (F_ISSET(t, R_INMEM)) {
2N/A errno = ENOENT;
2N/A return (-1);
2N/A }
2N/A return (t->bt_rfd);
2N/A}