1/* $Id: dbm_map.c,v 1.8 2017/02/17 14:43:54 schwarze Exp $ */ 2/* 3 * Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 * 17 * Low-level routines for the map-based version 18 * of the mandoc database, for read-only access. 19 * The interface is defined in "dbm_map.h". 20 */ 21#include "config.h" 22 23#include <sys/mman.h> 24#include <sys/stat.h> 25#include <sys/types.h> 26 27#if HAVE_ENDIAN 28#include <endian.h> 29#elif HAVE_SYS_ENDIAN 30#include <sys/endian.h> 31#elif HAVE_NTOHL 32#include <arpa/inet.h> 33#endif 34#if HAVE_ERR 35#include <err.h> 36#endif 37#include <errno.h> 38#include <fcntl.h> 39#include <regex.h> 40#include <stdint.h> 41#include <stdlib.h> 42#include <string.h> 43#include <unistd.h> 44 45#include "mansearch.h" 46#include "dbm_map.h" 47#include "dbm.h" 48 49static struct stat st; 50static char *dbm_base; 51static int ifd; 52static int32_t max_offset; 53 54/* 55 * Open a disk-based database for read-only access. 56 * Validate the file format as far as it is not mandoc-specific. 57 * Return 0 on success. Return -1 and set errno on failure. 58 */ 59int 60dbm_map(const char *fname) 61{ 62 int save_errno; 63 const int32_t *magic; 64 65 if ((ifd = open(fname, O_RDONLY)) == -1) 66 return -1; 67 if (fstat(ifd, &st) == -1) 68 goto fail; 69 if (st.st_size < 5) { 70 warnx("dbm_map(%s): File too short", fname); 71 errno = EFTYPE; 72 goto fail; 73 } 74 if (st.st_size > INT32_MAX) { 75 errno = EFBIG; 76 goto fail; 77 } 78 if ((dbm_base = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, 79 ifd, 0)) == MAP_FAILED) 80 goto fail; 81 magic = dbm_getint(0); 82 if (be32toh(*magic) != MANDOCDB_MAGIC) { 83 if (strncmp(dbm_base, "SQLite format 3", 15)) 84 warnx("dbm_map(%s): " 85 "Bad initial magic %x (expected %x)", 86 fname, be32toh(*magic), MANDOCDB_MAGIC); 87 else 88 warnx("dbm_map(%s): " 89 "Obsolete format based on SQLite 3", 90 fname); 91 errno = EFTYPE; 92 goto fail; 93 } 94 magic = dbm_getint(1); 95 if (be32toh(*magic) != MANDOCDB_VERSION) { 96 warnx("dbm_map(%s): Bad version number %d (expected %d)", 97 fname, be32toh(*magic), MANDOCDB_VERSION); 98 errno = EFTYPE; 99 goto fail; 100 } 101 max_offset = be32toh(*dbm_getint(3)) + sizeof(int32_t); 102 if (st.st_size != max_offset) { 103 warnx("dbm_map(%s): Inconsistent file size %lld (expected %d)", 104 fname, (long long)st.st_size, max_offset); 105 errno = EFTYPE; 106 goto fail; 107 } 108 if ((magic = dbm_get(*dbm_getint(3))) == NULL) { 109 errno = EFTYPE; 110 goto fail; 111 } 112 if (be32toh(*magic) != MANDOCDB_MAGIC) { 113 warnx("dbm_map(%s): Bad final magic %x (expected %x)", 114 fname, be32toh(*magic), MANDOCDB_MAGIC); 115 errno = EFTYPE; 116 goto fail; 117 } 118 return 0; 119 120fail: 121 save_errno = errno; 122 close(ifd); 123 errno = save_errno; 124 return -1; 125} 126 127void 128dbm_unmap(void) 129{ 130 if (munmap(dbm_base, st.st_size) == -1) 131 warn("dbm_unmap: munmap"); 132 if (close(ifd) == -1) 133 warn("dbm_unmap: close"); 134 dbm_base = (char *)-1; 135} 136 137/* 138 * Take a raw integer as it was read from the database. 139 * Interpret it as an offset into the database file 140 * and return a pointer to that place in the file. 141 */ 142void * 143dbm_get(int32_t offset) 144{ 145 offset = be32toh(offset); 146 if (offset < 0) { 147 warnx("dbm_get: Database corrupt: offset %d", offset); 148 return NULL; 149 } 150 if (offset >= max_offset) { 151 warnx("dbm_get: Database corrupt: offset %d > %d", 152 offset, max_offset); 153 return NULL; 154 } 155 return dbm_base + offset; 156} 157 158/* 159 * Assume the database starts with some integers. 160 * Assume they are numbered starting from 0, increasing. 161 * Get a pointer to one with the number "offset". 162 */ 163int32_t * 164dbm_getint(int32_t offset) 165{ 166 return (int32_t *)dbm_base + offset; 167} 168 169/* 170 * The reverse of dbm_get(). 171 * Take pointer into the database file 172 * and convert it to the raw integer 173 * that would be used to refer to that place in the file. 174 */ 175int32_t 176dbm_addr(const void *p) 177{ 178 return htobe32((const char *)p - dbm_base); 179} 180 181int 182dbm_match(const struct dbm_match *match, const char *str) 183{ 184 switch (match->type) { 185 case DBM_EXACT: 186 return strcmp(str, match->str) == 0; 187 case DBM_SUB: 188 return strcasestr(str, match->str) != NULL; 189 case DBM_REGEX: 190 return regexec(match->re, str, 0, NULL, 0) == 0; 191 default: 192 abort(); 193 } 194} 195