Annotation of src/usr.bin/mandoc/dbm_map.c, Revision 1.3
1.3 ! schwarze 1: /* $OpenBSD: dbm_map.c,v 1.2 2016/08/22 16:05:56 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: */
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) {
1.2 schwarze 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);
1.1 schwarze 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 %llu (expected %d)",
94: fname, 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);
1.3 ! schwarze 136: if (offset < 0) {
! 137: warnx("dbm_get: Database corrupt: offset %d", offset);
! 138: return NULL;
! 139: }
! 140: if (offset >= max_offset) {
1.1 schwarze 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((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: }