1 /* $NetBSD: mtree.c,v 1.52 2025/12/18 18:17:26 christos Exp $ */
2
3 /*-
4 * Copyright (c) 1989, 1990, 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(__COPYRIGHT) && !defined(lint)
38 __COPYRIGHT("@(#) Copyright (c) 1989, 1990, 1993\
39 The Regents of the University of California. All rights reserved.");
40 #endif /* not lint */
41
42 #if defined(__RCSID) && !defined(lint)
43 #if 0
44 static char sccsid[] = "@(#)mtree.c 8.1 (Berkeley) 6/6/93";
45 #else
46 __RCSID("$NetBSD: mtree.c,v 1.52 2025/12/18 18:17:26 christos Exp $");
47 #endif
48 #endif /* not lint */
49
50 #include <sys/param.h>
51 #include <sys/stat.h>
52
53 #include <errno.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <unistd.h>
58
59 #include "extern.h"
60
61 int ftsoptions = FTS_PHYSICAL;
62 int bflag, dflag, eflag, iflag, jflag, lflag, mflag, nflag, qflag, rflag,
63 sflag, tflag, uflag;
64 char fullpath[MAXPATHLEN];
65
66 static struct {
67 enum flavor flavor;
68 const char name[9];
69 } flavors[] = {
70 {F_MTREE, "mtree"},
71 {F_FREEBSD9, "freebsd9"},
72 {F_NETBSD6, "netbsd6"},
73 };
74
75 __dead static void usage(void);
76 static int parsekeys(char **);
77
78 int
main(int argc,char ** argv)79 main(int argc, char **argv)
80 {
81 int ch, status;
82 unsigned int i;
83 int cflag, Cflag, Dflag, Uflag, wflag, rkeys;
84 char *dir, *p;
85 FILE *spec1, *spec2;
86
87 setprogname(argv[0]);
88
89 cflag = Cflag = Dflag = Uflag = wflag = rkeys = 0;
90 dir = NULL;
91 init_excludes();
92 spec1 = stdin;
93 spec2 = NULL;
94
95 while ((ch = getopt(argc, argv,
96 "bcCdDeE:f:F:I:ijk:K:lLmMnN:O:p:PqrR:s:StuUwWxX:"))
97 != -1) {
98 switch((char)ch) {
99 case 'b':
100 bflag = 1;
101 break;
102 case 'c':
103 cflag = 1;
104 break;
105 case 'C':
106 Cflag = 1;
107 break;
108 case 'd':
109 dflag = 1;
110 break;
111 case 'D':
112 Dflag = 1;
113 break;
114 case 'E':
115 parsetags(&excludetags, optarg);
116 break;
117 case 'e':
118 eflag = 1;
119 break;
120 case 'f':
121 if (spec1 == stdin) {
122 spec1 = fopen(optarg, "r");
123 if (spec1 == NULL)
124 mtree_err("%s: %s", optarg,
125 strerror(errno));
126 } else if (spec2 == NULL) {
127 spec2 = fopen(optarg, "r");
128 if (spec2 == NULL)
129 mtree_err("%s: %s", optarg,
130 strerror(errno));
131 } else
132 usage();
133 break;
134 case 'F':
135 for (i = 0; i < __arraycount(flavors); i++)
136 if (strcmp(optarg, flavors[i].name) == 0) {
137 flavor = flavors[i].flavor;
138 break;
139 }
140 if (i == __arraycount(flavors))
141 usage();
142 break;
143 case 'i':
144 iflag = 1;
145 break;
146 case 'I':
147 parsetags(&includetags, optarg);
148 break;
149 case 'j':
150 jflag = 1;
151 break;
152 case 'k':
153 keys = F_TYPE;
154 /*FALLTHROUGH*/
155 case 'K':
156 keys |= parsekeys(&optarg);
157 break;
158 case 'l':
159 lflag = 1;
160 break;
161 case 'L':
162 ftsoptions &= ~FTS_PHYSICAL;
163 ftsoptions |= FTS_LOGICAL;
164 break;
165 case 'm':
166 mflag = 1;
167 break;
168 case 'M':
169 mtree_Mflag = 1;
170 break;
171 case 'n':
172 nflag = 1;
173 break;
174 case 'N':
175 if (! setup_getid(optarg))
176 mtree_err(
177 "Unable to use user and group databases in `%s'",
178 optarg);
179 break;
180 case 'O':
181 load_only(optarg);
182 break;
183 case 'p':
184 dir = optarg;
185 break;
186 case 'P':
187 ftsoptions &= ~FTS_LOGICAL;
188 ftsoptions |= FTS_PHYSICAL;
189 break;
190 case 'q':
191 qflag = 1;
192 break;
193 case 'r':
194 rflag++;
195 break;
196 case 'R':
197 rkeys |= parsekeys(&optarg);
198 break;
199 case 's':
200 sflag = 1;
201 crc_total = (uint32_t)~strtol(optarg, &p, 0);
202 if (*p)
203 mtree_err("illegal seed value -- %s", optarg);
204 break;
205 case 'S':
206 mtree_Sflag = 1;
207 break;
208 case 't':
209 tflag = 1;
210 break;
211 case 'u':
212 uflag = 1;
213 break;
214 case 'U':
215 Uflag = uflag = 1;
216 break;
217 case 'w':
218 wflag = 1;
219 break;
220 case 'W':
221 mtree_Wflag = 1;
222 break;
223 case 'x':
224 ftsoptions |= FTS_XDEV;
225 break;
226 case 'X':
227 read_excludes_file(optarg);
228 break;
229 case '?':
230 default:
231 usage();
232 }
233 }
234 argc -= optind;
235 argv += optind;
236
237 if (argc)
238 usage();
239
240 keys &= ~rkeys;
241
242 switch (flavor) {
243 case F_FREEBSD9:
244 if (cflag && iflag) {
245 warnx("-c and -i passed, replacing -i with -j for "
246 "FreeBSD compatibility");
247 iflag = 0;
248 jflag = 1;
249 }
250 if (dflag && !bflag) {
251 warnx("Adding -b to -d for FreeBSD compatibility");
252 bflag = 1;
253 }
254 if (uflag && !iflag) {
255 warnx("Adding -i to -%c for FreeBSD compatibility",
256 Uflag ? 'U' : 'u');
257 iflag = 1;
258 }
259 if (uflag && !tflag) {
260 warnx("Adding -t to -%c for FreeBSD compatibility",
261 Uflag ? 'U' : 'u');
262 tflag = 1;
263 }
264 if (wflag)
265 warnx("The -w flag is a no-op");
266 break;
267 default:
268 if (wflag)
269 usage();
270 }
271
272 if (spec2 && (cflag || Cflag || Dflag))
273 mtree_err("Double -f, -c, -C and -D flags are mutually "
274 "exclusive");
275
276 if (dir && spec2)
277 mtree_err("Double -f and -p flags are mutually exclusive");
278
279 if (dir && chdir(dir))
280 mtree_err("%s: %s", dir, strerror(errno));
281
282 if ((cflag || sflag) && !getcwd(fullpath, sizeof(fullpath)))
283 mtree_err("%s", strerror(errno));
284
285 if ((cflag && Cflag) || (cflag && Dflag) || (Cflag && Dflag))
286 mtree_err("-c, -C and -D flags are mutually exclusive");
287
288 if (iflag && mflag)
289 mtree_err("-i and -m flags are mutually exclusive");
290
291 if (lflag && uflag)
292 mtree_err("-l and -u flags are mutually exclusive");
293
294 if (cflag) {
295 cwalk(stdout);
296 exit(0);
297 }
298 if (Cflag || Dflag) {
299 dump_nodes(stdout, "", spec(spec1), Dflag);
300 exit(0);
301 }
302 if (spec2 != NULL)
303 status = mtree_specspec(spec1, spec2);
304 else
305 status = verify(spec1);
306 if (Uflag && (status == MISMATCHEXIT))
307 status = 0;
308 exit(status);
309 }
310
311 static int
parsekeys(char ** arg)312 parsekeys(char **arg)
313 {
314 const char *p;
315 int k = 0;
316
317 while ((p = strsep(arg, " \t,")) != NULL)
318 if (*p != '\0')
319 k |= parsekey(p, NULL);
320 return k;
321 }
322
323 static void
usage(void)324 usage(void)
325 {
326 unsigned int i;
327
328 fprintf(stderr,
329 "usage: %s [-bCcDdejLlMnPqrStUuWx] [-i|-m] [-E tags]\n"
330 "\t\t[-f spec] [-f spec]\n"
331 "\t\t[-I tags] [-K keywords] [-k keywords] [-N dbdir] [-p path]\n"
332 "\t\t[-R keywords] [-s seed] [-X exclude-file]\n"
333 "\t\t[-F flavor]\n",
334 getprogname());
335 fprintf(stderr, "\nflavors:");
336 for (i = 0; i < __arraycount(flavors); i++)
337 fprintf(stderr, " %s", flavors[i].name);
338 fprintf(stderr, "\n");
339 exit(1);
340 }
341