Annotation of mandoc/dbm_map.c, Revision 1.8
1.8 ! schwarze 1: /* $Id: dbm_map.c,v 1.7 2016/10/22 10:09:27 schwarze Exp $ */
1.1 schwarze 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: */
1.2 schwarze 21: #include "config.h"
22:
1.1 schwarze 23: #include <sys/mman.h>
24: #include <sys/stat.h>
25: #include <sys/types.h>
26:
1.3 schwarze 27: #if HAVE_ENDIAN
1.1 schwarze 28: #include <endian.h>
1.3 schwarze 29: #elif HAVE_SYS_ENDIAN
30: #include <sys/endian.h>
31: #elif HAVE_NTOHL
32: #include <arpa/inet.h>
33: #endif
1.2 schwarze 34: #if HAVE_ERR
1.1 schwarze 35: #include <err.h>
1.2 schwarze 36: #endif
1.1 schwarze 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) {
1.4 schwarze 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);
1.1 schwarze 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) {
1.6 schwarze 103: warnx("dbm_map(%s): Inconsistent file size %lld (expected %d)",
1.7 schwarze 104: fname, (long long)st.st_size, max_offset);
1.1 schwarze 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);
1.5 schwarze 146: if (offset < 0) {
147: warnx("dbm_get: Database corrupt: offset %d", offset);
148: return NULL;
149: }
150: if (offset >= max_offset) {
1.1 schwarze 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: {
1.8 ! schwarze 178: return htobe32((const char *)p - dbm_base);
1.1 schwarze 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: }
CVSweb