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