1/* $NetBSD: cap_mkdb.c,v 1.30 2021/12/03 13:27:39 andvar 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 41static char sccsid[] = "@(#)cap_mkdb.c 8.2 (Berkeley) 4/27/95"; 42#endif 43__RCSID("$NetBSD: cap_mkdb.c,v 1.30 2021/12/03 13:27:39 andvar 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 59static void db_build(const char **); 60static void dounlink(void); 61static void usage(void) __dead; 62static int count_records(char **); 63 64static DB *capdbp; 65static int verbose; 66static char *capname, outfile[MAXPATHLEN]; 67 68static 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 */ 84int 85main(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(outfile, sizeof(outfile), "%s.db.tmp", 130 capname ? capname : *argv); 131 if ((capname = strdup(outfile)) == NULL) 132 err(1, "strdup"); 133 p = strrchr(outfile, '.'); 134 assert(p != NULL); 135 *p = '\0'; 136 (void)unlink(outfile); 137 if ((capdbp = dbopen(capname, O_CREAT | O_TRUNC | O_RDWR, 138 DEFFILEMODE, DB_HASH, &openinfo)) == NULL) 139 err(1, "%s", outfile); 140 141 if (atexit(dounlink)) 142 err(1, "atexit"); 143 144 db_build((void *)argv); 145 146 if (capdbp->close(capdbp) < 0) 147 err(1, "%s", capname); 148 if (rename(capname, outfile) == -1) 149 err(1, "rename"); 150 free(capname); 151 capname = NULL; 152 return 0; 153} 154 155static void 156dounlink(void) 157{ 158 if (capname != NULL) 159 unlink(capname); 160} 161 162/* 163 * Any changes to these definitions should be made also in the getcap(3) 164 * library routines. 165 */ 166#define RECOK (char)0 167#define TCERR (char)1 168#define SHADOW (char)2 169 170/* 171 * Db_build() builds the name and capability databases according to the 172 * details above. 173 */ 174static void 175db_build(const char **ifiles) 176{ 177 DBT key, data; 178 recno_t reccnt; 179 size_t len, bplen; 180 int st; 181 char *bp, *p, *t, *n; 182 183 data.data = NULL; 184 key.data = NULL; 185 for (reccnt = 0, bplen = 0; (st = cgetnext(&bp, ifiles)) > 0; free(bp)){ 186 187 /* 188 * Allocate enough memory to store record, terminating 189 * NULL and one extra byte. 190 */ 191 len = strlen(bp); 192 if (bplen <= len + 2) { 193 if ((n = realloc(data.data, 194 bplen + MAX(256, len + 2))) == NULL) 195 err(1, "realloc"); 196 data.data = n; 197 bplen += MAX(256, len + 2); 198 } 199 200 /* Find the end of the name field. */ 201 if ((p = strchr(bp, ':')) == NULL) { 202 warnx("no name field: %.*s", (int)(MIN(len, 20)), bp); 203 continue; 204 } 205 206 /* First byte of stored record indicates status. */ 207 switch(st) { 208 case 1: 209 ((char *)(data.data))[0] = RECOK; 210 break; 211 case 2: 212 ((char *)(data.data))[0] = TCERR; 213 warnx("Record not tc expanded: %.*s", (int)(p - bp),bp); 214 break; 215 } 216 217 /* Create the stored record. */ 218 (void)memmove(&((u_char *)(data.data))[1], bp, len + 1); 219 data.size = len + 2; 220 221 /* Store the record under the name field. */ 222 key.data = bp; 223 key.size = p - bp; 224 225 switch(capdbp->put(capdbp, &key, &data, R_NOOVERWRITE)) { 226 case -1: 227 err(1, "put"); 228 /* NOTREACHED */ 229 case 1: 230 warnx("ignored duplicate: %.*s", 231 (int)key.size, (char *)key.data); 232 continue; 233 } 234 ++reccnt; 235 236 /* If only one name, ignore the rest. */ 237 if ((p = strchr(bp, '|')) == NULL) 238 continue; 239 240 /* The rest of the names reference the entire name. */ 241 ((char *)(data.data))[0] = SHADOW; 242 (void)memmove(&((u_char *)(data.data))[1], key.data, key.size); 243 data.size = key.size + 1; 244 245 /* Store references for other names. */ 246 for (p = t = bp;; ++p) { 247 if (p > t && (*p == ':' || *p == '|')) { 248 key.size = p - t; 249 key.data = t; 250 switch(capdbp->put(capdbp, 251 &key, &data, R_NOOVERWRITE)) { 252 case -1: 253 err(1, "put"); 254 /* NOTREACHED */ 255 case 1: 256 warnx("ignored duplicate: %.*s", 257 (int)key.size, (char *)key.data); 258 } 259 t = p + 1; 260 } 261 if (*p == ':') 262 break; 263 } 264 } 265 266 switch(st) { 267 case -1: 268 err(1, "file argument"); 269 /* NOTREACHED */ 270 case -2: 271 errx(1, "potential reference loop detected"); 272 /* NOTREACHED */ 273 } 274 275 if (verbose) 276 (void)printf("cap_mkdb: %d capability records\n", reccnt); 277} 278 279static void 280usage(void) 281{ 282 (void)fprintf(stderr, 283 "Usage: %s [-b|-l] [-v] [-f outfile] file1 [file2 ...]\n", 284 getprogname()); 285 exit(1); 286} 287 288/* 289 * Count number of records in input files. This does not need 290 * to be really accurate (the result is used only as a hint). 291 * It seems to match number of records should a cgetnext() be used, though. 292 */ 293static int 294count_records(char **list) 295{ 296 FILE *fp; 297 char *line; 298 size_t len; 299 int nelem, slash; 300 301 /* scan input files and count individual records */ 302 for(nelem = 0, slash = 0; *list && (fp = fopen(*list++, "r")); ) { 303 while((line = fgetln(fp, &len)) != NULL) { 304 if (len < 2) 305 continue; 306 if (!isspace((unsigned char) *line) && *line != ':' 307 && *line != '#' && !slash) 308 nelem++; 309 310 slash = (line[len - 2] == '\\'); 311 } 312 (void)fclose(fp); 313 } 314 315 if (nelem == 0) { 316 /* no records found; pass default size hint */ 317 nelem = 1; 318 } else if (!powerof2(nelem)) { 319 /* set nelem to nearest bigger power-of-two number */ 320 int bt = 1; 321 while(bt < nelem) bt <<= 1; 322 nelem = bt; 323 } 324 325 return nelem; 326} 327