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