1 /* $NetBSD: cap_mkdb.c,v 1.17 2003/01/31 20:50:30 uwe 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 #if HAVE_CONFIG_H 37 #include "config.h" 38 #endif 39 40 #include <sys/cdefs.h> 41 #if defined(__COPYRIGHT) && !defined(lint) 42 __COPYRIGHT("@(#) Copyright (c) 1992, 1993\n\ 43 The Regents of the University of California. All rights reserved.\n"); 44 #endif /* not lint */ 45 46 #if defined(__RCSID) && !defined(lint) 47 #if 0 48 static char sccsid[] = "@(#)cap_mkdb.c 8.2 (Berkeley) 4/27/95"; 49 #endif 50 __RCSID("$NetBSD: cap_mkdb.c,v 1.17 2003/01/31 20:50:30 uwe Exp $"); 51 #endif /* not lint */ 52 53 #include <sys/param.h> 54 #include <sys/stat.h> 55 56 #include <db.h> 57 #include <err.h> 58 #include <fcntl.h> 59 #include <stdio.h> 60 #include <stdlib.h> 61 #include <string.h> 62 #include <unistd.h> 63 #include <ctype.h> 64 65 static void db_build (char **); 66 static void dounlink (void); 67 static void usage (void); 68 static int count_records(char **); 69 70 DB *capdbp; 71 int verbose; 72 char *capdb, *capname, buf[8 * 1024]; 73 74 HASHINFO openinfo = { 75 4096, /* bsize */ 76 16, /* ffactor */ 77 2048, /* nelem */ 78 2048 * 1024, /* cachesize */ 79 NULL, /* hash() */ 80 0 /* lorder */ 81 }; 82 83 /* 84 * Mkcapdb creates a capability hash database for quick retrieval of capability 85 * records. The database contains 2 types of entries: records and references 86 * marked by the first byte in the data. A record entry contains the actual 87 * capability record whereas a reference contains the name (key) under which 88 * the correct record is stored. 89 */ 90 int 91 main(int argc, char *argv[]) 92 { 93 int c, byteorder; 94 95 capname = NULL; 96 byteorder = 0; 97 while ((c = getopt(argc, argv, "bf:lv")) != -1) { 98 switch(c) { 99 case 'b': 100 case 'l': 101 if (byteorder != 0) 102 usage(); 103 byteorder = c == 'b' ? 4321 : 1234; 104 break; 105 case 'f': 106 capname = optarg; 107 break; 108 case 'v': 109 verbose = 1; 110 break; 111 case '?': 112 default: 113 usage(); 114 } 115 } 116 argc -= optind; 117 argv += optind; 118 119 if (*argv == NULL) 120 usage(); 121 122 /* Set byte order */ 123 openinfo.lorder = byteorder; 124 125 /* 126 * Set nelem to twice the value returned by count_record(). 127 */ 128 openinfo.nelem = count_records(argv) << 1; 129 130 /* 131 * The database file is the first argument if no name is specified. 132 * Make arrangements to unlink it if exit badly. 133 */ 134 (void)snprintf(buf, sizeof(buf), "%s.db", capname ? capname : *argv); 135 if ((capname = strdup(buf)) == NULL) 136 err(1, "strdup"); 137 if ((capdbp = dbopen(capname, O_CREAT | O_TRUNC | O_RDWR, 138 DEFFILEMODE, DB_HASH, &openinfo)) == NULL) 139 err(1, "%s", buf); 140 141 if (atexit(dounlink)) 142 err(1, "atexit"); 143 144 db_build(argv); 145 146 if (capdbp->close(capdbp) < 0) 147 err(1, "%s", capname); 148 capname = NULL; 149 exit(0); 150 } 151 152 static void 153 dounlink(void) 154 { 155 if (capname != NULL) 156 (void)unlink(capname); 157 } 158 159 /* 160 * Any changes to these definitions should be made also in the getcap(3) 161 * library routines. 162 */ 163 #define RECOK (char)0 164 #define TCERR (char)1 165 #define SHADOW (char)2 166 167 /* 168 * Db_build() builds the name and capabilty databases according to the 169 * details above. 170 */ 171 static void 172 db_build(char **ifiles) 173 { 174 DBT key, data; 175 recno_t reccnt; 176 size_t len, bplen; 177 int st; 178 char *bp, *p, *t; 179 180 data.data = NULL; 181 key.data = NULL; 182 for (reccnt = 0, bplen = 0; (st = cgetnext(&bp, ifiles)) > 0; free(bp)){ 183 184 /* 185 * Allocate enough memory to store record, terminating 186 * NULL and one extra byte. 187 */ 188 len = strlen(bp); 189 if (bplen <= len + 2) { 190 bplen += MAX(256, len + 2); 191 if ((data.data = realloc(data.data, bplen)) == NULL) 192 err(1, "realloc"); 193 } 194 195 /* Find the end of the name field. */ 196 if ((p = strchr(bp, ':')) == NULL) { 197 warnx("no name field: %.*s", (int)(MIN(len, 20)), bp); 198 continue; 199 } 200 201 /* First byte of stored record indicates status. */ 202 switch(st) { 203 case 1: 204 ((char *)(data.data))[0] = RECOK; 205 break; 206 case 2: 207 ((char *)(data.data))[0] = TCERR; 208 warnx("Record not tc expanded: %.*s", (int)(p - bp),bp); 209 break; 210 } 211 212 /* Create the stored record. */ 213 memmove(&((u_char *)(data.data))[1], bp, len + 1); 214 data.size = len + 2; 215 216 /* Store the record under the name field. */ 217 key.data = bp; 218 key.size = p - bp; 219 220 switch(capdbp->put(capdbp, &key, &data, R_NOOVERWRITE)) { 221 case -1: 222 err(1, "put"); 223 /* NOTREACHED */ 224 case 1: 225 warnx("ignored duplicate: %.*s", 226 (int)key.size, (char *)key.data); 227 continue; 228 } 229 ++reccnt; 230 231 /* If only one name, ignore the rest. */ 232 if ((p = strchr(bp, '|')) == NULL) 233 continue; 234 235 /* The rest of the names reference the entire name. */ 236 ((char *)(data.data))[0] = SHADOW; 237 memmove(&((u_char *)(data.data))[1], key.data, key.size); 238 data.size = key.size + 1; 239 240 /* Store references for other names. */ 241 for (p = t = bp;; ++p) { 242 if (p > t && (*p == ':' || *p == '|')) { 243 key.size = p - t; 244 key.data = t; 245 switch(capdbp->put(capdbp, 246 &key, &data, R_NOOVERWRITE)) { 247 case -1: 248 err(1, "put"); 249 /* NOTREACHED */ 250 case 1: 251 warnx("ignored duplicate: %.*s", 252 (int)key.size, (char *)key.data); 253 } 254 t = p + 1; 255 } 256 if (*p == ':') 257 break; 258 } 259 } 260 261 switch(st) { 262 case -1: 263 err(1, "file argument"); 264 /* NOTREACHED */ 265 case -2: 266 errx(1, "potential reference loop detected"); 267 /* NOTREACHED */ 268 } 269 270 if (verbose) 271 (void)printf("cap_mkdb: %d capability records\n", reccnt); 272 } 273 274 static void 275 usage(void) 276 { 277 (void)fprintf(stderr, 278 "usage: cap_mkdb [-b|-l] [-v] [-f outfile] file1 [file2 ...]\n"); 279 exit(1); 280 } 281 282 /* 283 * Count number of records in input files. This does not need 284 * to be really accurate (the result is used only as a hint). 285 * It seems to match number of records should a cgetnext() be used, though. 286 */ 287 static int 288 count_records(char **list) 289 { 290 FILE *fp; 291 char *line; 292 size_t len; 293 int nelem, slash; 294 295 /* scan input files and count individual records */ 296 for(nelem = 0, slash = 0; *list && (fp = fopen(*list++, "r")); ) { 297 while((line = fgetln(fp, &len))) { 298 if (len < 2) 299 continue; 300 if (!isspace((unsigned char) *line) && *line != ':' 301 && *line != '#' && !slash) 302 nelem++; 303 304 slash = (line[len - 2] == '\\'); 305 } 306 fclose(fp); 307 } 308 309 if (nelem == 0) { 310 /* no records found; pass default size hint */ 311 nelem = 1; 312 } else if (!powerof2(nelem)) { 313 /* set nelem to nearest bigger power-of-two number */ 314 int bt = 1; 315 while(bt < nelem) bt <<= 1; 316 nelem = bt; 317 } 318 319 return nelem; 320 } 321