1 /* $OpenBSD: dbm_map.c,v 1.6 2017/02/09 18:26:17 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 <sys/mman.h> 22 #include <sys/stat.h> 23 #include <sys/types.h> 24 25 #include <endian.h> 26 #include <err.h> 27 #include <errno.h> 28 #include <fcntl.h> 29 #include <regex.h> 30 #include <stdint.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <unistd.h> 34 35 #include "mansearch.h" 36 #include "dbm_map.h" 37 #include "dbm.h" 38 39 static struct stat st; 40 static char *dbm_base; 41 static int ifd; 42 static int32_t max_offset; 43 44 /* 45 * Open a disk-based database for read-only access. 46 * Validate the file format as far as it is not mandoc-specific. 47 * Return 0 on success. Return -1 and set errno on failure. 48 */ 49 int 50 dbm_map(const char *fname) 51 { 52 int save_errno; 53 const int32_t *magic; 54 55 if ((ifd = open(fname, O_RDONLY)) == -1) 56 return -1; 57 if (fstat(ifd, &st) == -1) 58 goto fail; 59 if (st.st_size < 5) { 60 warnx("dbm_map(%s): File too short", fname); 61 errno = EFTYPE; 62 goto fail; 63 } 64 if (st.st_size > INT32_MAX) { 65 errno = EFBIG; 66 goto fail; 67 } 68 if ((dbm_base = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, 69 ifd, 0)) == MAP_FAILED) 70 goto fail; 71 magic = dbm_getint(0); 72 if (be32toh(*magic) != MANDOCDB_MAGIC) { 73 if (strncmp(dbm_base, "SQLite format 3", 15)) 74 warnx("dbm_map(%s): " 75 "Bad initial magic %x (expected %x)", 76 fname, be32toh(*magic), MANDOCDB_MAGIC); 77 else 78 warnx("dbm_map(%s): " 79 "Obsolete format based on SQLite 3", 80 fname); 81 errno = EFTYPE; 82 goto fail; 83 } 84 magic = dbm_getint(1); 85 if (be32toh(*magic) != MANDOCDB_VERSION) { 86 warnx("dbm_map(%s): Bad version number %d (expected %d)", 87 fname, be32toh(*magic), MANDOCDB_VERSION); 88 errno = EFTYPE; 89 goto fail; 90 } 91 max_offset = be32toh(*dbm_getint(3)) + sizeof(int32_t); 92 if (st.st_size != max_offset) { 93 warnx("dbm_map(%s): Inconsistent file size %lld (expected %d)", 94 fname, (long long)st.st_size, max_offset); 95 errno = EFTYPE; 96 goto fail; 97 } 98 if ((magic = dbm_get(*dbm_getint(3))) == NULL) { 99 errno = EFTYPE; 100 goto fail; 101 } 102 if (be32toh(*magic) != MANDOCDB_MAGIC) { 103 warnx("dbm_map(%s): Bad final magic %x (expected %x)", 104 fname, be32toh(*magic), MANDOCDB_MAGIC); 105 errno = EFTYPE; 106 goto fail; 107 } 108 return 0; 109 110 fail: 111 save_errno = errno; 112 close(ifd); 113 errno = save_errno; 114 return -1; 115 } 116 117 void 118 dbm_unmap(void) 119 { 120 if (munmap(dbm_base, st.st_size) == -1) 121 warn("dbm_unmap: munmap"); 122 if (close(ifd) == -1) 123 warn("dbm_unmap: close"); 124 dbm_base = (char *)-1; 125 } 126 127 /* 128 * Take a raw integer as it was read from the database. 129 * Interpret it as an offset into the database file 130 * and return a pointer to that place in the file. 131 */ 132 void * 133 dbm_get(int32_t offset) 134 { 135 offset = be32toh(offset); 136 if (offset < 0) { 137 warnx("dbm_get: Database corrupt: offset %d", offset); 138 return NULL; 139 } 140 if (offset >= max_offset) { 141 warnx("dbm_get: Database corrupt: offset %d > %d", 142 offset, max_offset); 143 return NULL; 144 } 145 return dbm_base + offset; 146 } 147 148 /* 149 * Assume the database starts with some integers. 150 * Assume they are numbered starting from 0, increasing. 151 * Get a pointer to one with the number "offset". 152 */ 153 int32_t * 154 dbm_getint(int32_t offset) 155 { 156 return (int32_t *)dbm_base + offset; 157 } 158 159 /* 160 * The reverse of dbm_get(). 161 * Take pointer into the database file 162 * and convert it to the raw integer 163 * that would be used to refer to that place in the file. 164 */ 165 int32_t 166 dbm_addr(const void *p) 167 { 168 return htobe32((const char *)p - dbm_base); 169 } 170 171 int 172 dbm_match(const struct dbm_match *match, const char *str) 173 { 174 switch (match->type) { 175 case DBM_EXACT: 176 return strcmp(str, match->str) == 0; 177 case DBM_SUB: 178 return strcasestr(str, match->str) != NULL; 179 case DBM_REGEX: 180 return regexec(match->re, str, 0, NULL, 0) == 0; 181 default: 182 abort(); 183 } 184 } 185