1 /* $NetBSD: cap_mkdb.c,v 1.16 2002/01/31 19:23:50 tv Exp $ */ 2 3 /*- 4 * Copyright (c) 1992, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. All advertising materials mentioning features or use of this software 16 * must display the following acknowledgement: 17 * This product includes software developed by the University of 18 * California, Berkeley and its contributors. 19 * 4. Neither the name of the University nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36 #include <sys/cdefs.h> 37 #if defined(__COPYRIGHT) && !defined(lint) 38 __COPYRIGHT("@(#) Copyright (c) 1992, 1993\n\ 39 The Regents of the University of California. All rights reserved.\n"); 40 #endif /* not lint */ 41 42 #if defined(__RCSID) && !defined(lint) 43 #if 0 44 static char sccsid[] = "@(#)cap_mkdb.c 8.2 (Berkeley) 4/27/95"; 45 #endif 46 __RCSID("$NetBSD: cap_mkdb.c,v 1.16 2002/01/31 19:23:50 tv Exp $"); 47 #endif /* not lint */ 48 49 #include <sys/param.h> 50 #include <sys/stat.h> 51 52 #include <db.h> 53 #include <err.h> 54 #include <fcntl.h> 55 #include <stdio.h> 56 #include <stdlib.h> 57 #include <string.h> 58 #include <unistd.h> 59 #include <ctype.h> 60 61 static void db_build (char **); 62 static void dounlink (void); 63 static void usage (void); 64 static int count_records(char **); 65 66 DB *capdbp; 67 int verbose; 68 char *capdb, *capname, buf[8 * 1024]; 69 70 HASHINFO openinfo = { 71 4096, /* bsize */ 72 16, /* ffactor */ 73 2048, /* nelem */ 74 2048 * 1024, /* cachesize */ 75 NULL, /* hash() */ 76 0 /* lorder */ 77 }; 78 79 /* 80 * Mkcapdb creates a capability hash database for quick retrieval of capability 81 * records. The database contains 2 types of entries: records and references 82 * marked by the first byte in the data. A record entry contains the actual 83 * capability record whereas a reference contains the name (key) under which 84 * the correct record is stored. 85 */ 86 int 87 main(int argc, char *argv[]) 88 { 89 int c, byteorder; 90 91 capname = NULL; 92 byteorder = 0; 93 while ((c = getopt(argc, argv, "bf:lv")) != -1) { 94 switch(c) { 95 case 'b': 96 case 'l': 97 if (byteorder != 0) 98 usage(); 99 byteorder = c == 'b' ? 4321 : 1234; 100 break; 101 case 'f': 102 capname = optarg; 103 break; 104 case 'v': 105 verbose = 1; 106 break; 107 case '?': 108 default: 109 usage(); 110 } 111 } 112 argc -= optind; 113 argv += optind; 114 115 if (*argv == NULL) 116 usage(); 117 118 /* Set byte order */ 119 openinfo.lorder = byteorder; 120 121 /* 122 * Set nelem to twice the value returned by count_record(). 123 */ 124 openinfo.nelem = count_records(argv) << 1; 125 126 /* 127 * The database file is the first argument if no name is specified. 128 * Make arrangements to unlink it if exit badly. 129 */ 130 (void)snprintf(buf, sizeof(buf), "%s.db", capname ? capname : *argv); 131 if ((capname = strdup(buf)) == NULL) 132 err(1, "strdup"); 133 if ((capdbp = dbopen(capname, O_CREAT | O_TRUNC | O_RDWR, 134 DEFFILEMODE, DB_HASH, &openinfo)) == NULL) 135 err(1, "%s", buf); 136 137 if (atexit(dounlink)) 138 err(1, "atexit"); 139 140 db_build(argv); 141 142 if (capdbp->close(capdbp) < 0) 143 err(1, "%s", capname); 144 capname = NULL; 145 exit(0); 146 } 147 148 static void 149 dounlink(void) 150 { 151 if (capname != NULL) 152 (void)unlink(capname); 153 } 154 155 /* 156 * Any changes to these definitions should be made also in the getcap(3) 157 * library routines. 158 */ 159 #define RECOK (char)0 160 #define TCERR (char)1 161 #define SHADOW (char)2 162 163 /* 164 * Db_build() builds the name and capabilty databases according to the 165 * details above. 166 */ 167 static void 168 db_build(char **ifiles) 169 { 170 DBT key, data; 171 recno_t reccnt; 172 size_t len, bplen; 173 int st; 174 char *bp, *p, *t; 175 176 data.data = NULL; 177 key.data = NULL; 178 for (reccnt = 0, bplen = 0; (st = cgetnext(&bp, ifiles)) > 0; free(bp)){ 179 180 /* 181 * Allocate enough memory to store record, terminating 182 * NULL and one extra byte. 183 */ 184 len = strlen(bp); 185 if (bplen <= len + 2) { 186 bplen += MAX(256, len + 2); 187 if ((data.data = realloc(data.data, bplen)) == NULL) 188 err(1, "realloc"); 189 } 190 191 /* Find the end of the name field. */ 192 if ((p = strchr(bp, ':')) == NULL) { 193 warnx("no name field: %.*s", (int)(MIN(len, 20)), bp); 194 continue; 195 } 196 197 /* First byte of stored record indicates status. */ 198 switch(st) { 199 case 1: 200 ((char *)(data.data))[0] = RECOK; 201 break; 202 case 2: 203 ((char *)(data.data))[0] = TCERR; 204 warnx("Record not tc expanded: %.*s", (int)(p - bp),bp); 205 break; 206 } 207 208 /* Create the stored record. */ 209 memmove(&((u_char *)(data.data))[1], bp, len + 1); 210 data.size = len + 2; 211 212 /* Store the record under the name field. */ 213 key.data = bp; 214 key.size = p - bp; 215 216 switch(capdbp->put(capdbp, &key, &data, R_NOOVERWRITE)) { 217 case -1: 218 err(1, "put"); 219 /* NOTREACHED */ 220 case 1: 221 warnx("ignored duplicate: %.*s", 222 (int)key.size, (char *)key.data); 223 continue; 224 } 225 ++reccnt; 226 227 /* If only one name, ignore the rest. */ 228 if ((p = strchr(bp, '|')) == NULL) 229 continue; 230 231 /* The rest of the names reference the entire name. */ 232 ((char *)(data.data))[0] = SHADOW; 233 memmove(&((u_char *)(data.data))[1], key.data, key.size); 234 data.size = key.size + 1; 235 236 /* Store references for other names. */ 237 for (p = t = bp;; ++p) { 238 if (p > t && (*p == ':' || *p == '|')) { 239 key.size = p - t; 240 key.data = t; 241 switch(capdbp->put(capdbp, 242 &key, &data, R_NOOVERWRITE)) { 243 case -1: 244 err(1, "put"); 245 /* NOTREACHED */ 246 case 1: 247 warnx("ignored duplicate: %.*s", 248 (int)key.size, (char *)key.data); 249 } 250 t = p + 1; 251 } 252 if (*p == ':') 253 break; 254 } 255 } 256 257 switch(st) { 258 case -1: 259 err(1, "file argument"); 260 /* NOTREACHED */ 261 case -2: 262 errx(1, "potential reference loop detected"); 263 /* NOTREACHED */ 264 } 265 266 if (verbose) 267 (void)printf("cap_mkdb: %d capability records\n", reccnt); 268 } 269 270 static void 271 usage(void) 272 { 273 (void)fprintf(stderr, 274 "usage: cap_mkdb [-b|-l] [-v] [-f outfile] file1 [file2 ...]\n"); 275 exit(1); 276 } 277 278 /* 279 * Count number of records in input files. This does not need 280 * to be really accurate (the result is used only as a hint). 281 * It seems to match number of records should a cgetnext() be used, though. 282 */ 283 static int 284 count_records(char **list) 285 { 286 FILE *fp; 287 char *line; 288 size_t len; 289 int nelem, slash; 290 291 /* scan input files and count individual records */ 292 for(nelem = 0, slash = 0; *list && (fp = fopen(*list++, "r")); ) { 293 while((line = fgetln(fp, &len))) { 294 if (len < 2) 295 continue; 296 if (!isspace((unsigned char) *line) && *line != ':' 297 && *line != '#' && !slash) 298 nelem++; 299 300 slash = (line[len - 2] == '\\'); 301 } 302 fclose(fp); 303 } 304 305 if (nelem == 0) { 306 /* no records found; pass default size hint */ 307 nelem = 1; 308 } else if (!powerof2(nelem)) { 309 /* set nelem to nearest bigger power-of-two number */ 310 int bt = 1; 311 while(bt < nelem) bt <<= 1; 312 nelem = bt; 313 } 314 315 return nelem; 316 } 317