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 49 static struct stat st; 50 static char *dbm_base; 51 static int ifd; 52 static 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 */ 59 int 60 dbm_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 120 fail: 121 save_errno = errno; 122 close(ifd); 123 errno = save_errno; 124 return -1; 125 } 126 127 void 128 dbm_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 */ 142 void * 143 dbm_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 */ 163 int32_t * 164 dbm_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 */ 175 int32_t 176 dbm_addr(const void *p) 177 { 178 return htobe32((const char *)p - dbm_base); 179 } 180 181 int 182 dbm_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