1 /* $NetBSD: cap_mkdb.c,v 1.22 2005/04/13 04:50:22 christos 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. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #if HAVE_NBTOOL_CONFIG_H 33 #include "nbtool_config.h" 34 #endif 35 36 #include <sys/cdefs.h> 37 #if !defined(lint) 38 __COPYRIGHT("@(#) Copyright (c) 1992, 1993\n\ 39 The Regents of the University of California. All rights reserved.\n"); 40 #if 0 41 static char sccsid[] = "@(#)cap_mkdb.c 8.2 (Berkeley) 4/27/95"; 42 #endif 43 __RCSID("$NetBSD: cap_mkdb.c,v 1.22 2005/04/13 04:50:22 christos Exp $"); 44 #endif /* not lint */ 45 46 #include <sys/param.h> 47 #include <sys/stat.h> 48 49 #include <db.h> 50 #include <err.h> 51 #include <fcntl.h> 52 #include <stdio.h> 53 #include <stdlib.h> 54 #include <string.h> 55 #include <unistd.h> 56 #include <ctype.h> 57 58 static void db_build(const char **); 59 static void dounlink(void); 60 static void usage(void) __attribute__((__unused__)); 61 static int count_records(char **); 62 63 static DB *capdbp; 64 static int verbose; 65 static char *capname, buf[8 * 1024]; 66 67 static HASHINFO openinfo = { 68 4096, /* bsize */ 69 16, /* ffactor */ 70 2048, /* nelem */ 71 2048 * 1024, /* cachesize */ 72 NULL, /* hash() */ 73 0 /* lorder */ 74 }; 75 76 /* 77 * Mkcapdb creates a capability hash database for quick retrieval of capability 78 * records. The database contains 2 types of entries: records and references 79 * marked by the first byte in the data. A record entry contains the actual 80 * capability record whereas a reference contains the name (key) under which 81 * the correct record is stored. 82 */ 83 int 84 main(int argc, char *argv[]) 85 { 86 int c, byteorder; 87 88 capname = NULL; 89 byteorder = 0; 90 while ((c = getopt(argc, argv, "bf:lv")) != -1) { 91 switch(c) { 92 case 'b': 93 case 'l': 94 if (byteorder != 0) 95 usage(); 96 byteorder = c == 'b' ? 4321 : 1234; 97 break; 98 case 'f': 99 capname = optarg; 100 break; 101 case 'v': 102 verbose = 1; 103 break; 104 case '?': 105 default: 106 usage(); 107 } 108 } 109 argc -= optind; 110 argv += optind; 111 112 if (*argv == NULL) 113 usage(); 114 115 /* Set byte order */ 116 openinfo.lorder = byteorder; 117 118 /* 119 * Set nelem to twice the value returned by count_record(). 120 */ 121 openinfo.nelem = count_records(argv) << 1; 122 123 /* 124 * The database file is the first argument if no name is specified. 125 * Make arrangements to unlink it if exit badly. 126 */ 127 (void)snprintf(buf, sizeof(buf), "%s.db.tmp", 128 capname ? capname : *argv); 129 if ((capname = strdup(buf)) == NULL) 130 err(1, "strdup"); 131 if ((capdbp = dbopen(capname, O_CREAT | O_TRUNC | O_RDWR, 132 DEFFILEMODE, DB_HASH, &openinfo)) == NULL) 133 err(1, "%s", buf); 134 135 if (atexit(dounlink)) 136 err(1, "atexit"); 137 138 db_build((const char **)argv); 139 140 if (capdbp->close(capdbp) < 0) 141 err(1, "%s", capname); 142 *strrchr(buf, '.') = '\0'; 143 if (rename(capname, buf) == -1) 144 err(1, "rename"); 145 free(capname); 146 capname = NULL; 147 return 0; 148 } 149 150 static void 151 dounlink(void) 152 { 153 if (capname != NULL) 154 (void)unlink(capname); 155 } 156 157 /* 158 * Any changes to these definitions should be made also in the getcap(3) 159 * library routines. 160 */ 161 #define RECOK (char)0 162 #define TCERR (char)1 163 #define SHADOW (char)2 164 165 /* 166 * Db_build() builds the name and capabilty databases according to the 167 * details above. 168 */ 169 static void 170 db_build(const char **ifiles) 171 { 172 DBT key, data; 173 recno_t reccnt; 174 size_t len, bplen; 175 int st; 176 char *bp, *p, *t, *n; 177 178 data.data = NULL; 179 key.data = NULL; 180 for (reccnt = 0, bplen = 0; (st = cgetnext(&bp, ifiles)) > 0; free(bp)){ 181 182 /* 183 * Allocate enough memory to store record, terminating 184 * NULL and one extra byte. 185 */ 186 len = strlen(bp); 187 if (bplen <= len + 2) { 188 if ((n = realloc(data.data, 189 bplen + MAX(256, len + 2))) == NULL) 190 err(1, "realloc"); 191 data.data = n; 192 bplen += MAX(256, len + 2); 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 (void)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 (void)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: %s [-b|-l] [-v] [-f outfile] file1 [file2 ...]\n", 279 getprogname()); 280 exit(1); 281 } 282 283 /* 284 * Count number of records in input files. This does not need 285 * to be really accurate (the result is used only as a hint). 286 * It seems to match number of records should a cgetnext() be used, though. 287 */ 288 static int 289 count_records(char **list) 290 { 291 FILE *fp; 292 char *line; 293 size_t len; 294 int nelem, slash; 295 296 /* scan input files and count individual records */ 297 for(nelem = 0, slash = 0; *list && (fp = fopen(*list++, "r")); ) { 298 while((line = fgetln(fp, &len)) != NULL) { 299 if (len < 2) 300 continue; 301 if (!isspace((unsigned char) *line) && *line != ':' 302 && *line != '#' && !slash) 303 nelem++; 304 305 slash = (line[len - 2] == '\\'); 306 } 307 (void)fclose(fp); 308 } 309 310 if (nelem == 0) { 311 /* no records found; pass default size hint */ 312 nelem = 1; 313 } else if (!powerof2(nelem)) { 314 /* set nelem to nearest bigger power-of-two number */ 315 int bt = 1; 316 while(bt < nelem) bt <<= 1; 317 nelem = bt; 318 } 319 320 return nelem; 321 } 322