file_cmds-321.100.10.0.1.tar.gz
[apple/file_cmds.git] / mtree / mtree.c
1 /*-
2 * Copyright (c) 1989, 1990, 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 * 3. 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 #if 0
31 #ifndef lint
32 static const char copyright[] =
33 "@(#) Copyright (c) 1989, 1990, 1993\n\
34 The Regents of the University of California. All rights reserved.\n";
35 #endif /* not lint */
36
37 #ifndef lint
38 static char sccsid[] = "@(#)mtree.c 8.1 (Berkeley) 6/6/93";
39 #endif /* not lint */
40 #endif
41 #include <sys/cdefs.h>
42 __FBSDID("$FreeBSD: src/usr.sbin/mtree/mtree.c,v 1.29 2004/06/04 19:29:28 ru Exp $");
43
44 #include <CoreFoundation/CoreFoundation.h>
45 #include <sys/param.h>
46 #include <sys/stat.h>
47 #include <err.h>
48 #include <errno.h>
49 #include <fts.h>
50 #include <stdio.h>
51 #include <stdint.h>
52 #include <unistd.h>
53 #include "metrics.h"
54 #include "mtree.h"
55 #include "extern.h"
56
57 #define SECONDS_IN_A_DAY (60 * 60 * 24)
58
59 int ftsoptions = FTS_PHYSICAL;
60 int cflag, dflag, eflag, iflag, nflag, qflag, rflag, sflag, uflag, Uflag, wflag, mflag, tflag, xflag;
61 int insert_mod, insert_birth, insert_access, insert_change, insert_parent;
62 struct timespec ts;
63 u_int keys;
64 char fullpath[MAXPATHLEN];
65 CFMutableDictionaryRef dict;
66 char *filepath;
67
68 static void usage(void);
69 static bool write_plist_to_file(void);
70
71 static void
72 do_cleanup(void) {
73
74 if (mflag) {
75 if (dict)
76 CFRelease(dict);
77 if (filepath)
78 free(filepath);
79 }
80 }
81
82 int
83 main(int argc, char *argv[])
84 {
85 int error = 0;
86 int ch;
87 char *dir, *p;
88 int status;
89 FILE *spec1, *spec2;
90 char *timestamp = NULL;
91 char *timeformat = "%FT%T";
92 FILE *file = NULL;
93
94 dir = NULL;
95 keys = KEYDEFAULT;
96 init_excludes();
97 spec1 = stdin;
98 spec2 = NULL;
99 set_metric_start_time(time(NULL));
100
101 atexit(do_cleanup);
102 atexit(print_metrics_to_file);
103
104 while ((ch = getopt(argc, argv, "cdef:iK:k:LnPp:qrs:UuwxX:m:F:t:E:S")) != -1)
105 switch((char)ch) {
106 case 'c':
107 cflag = 1;
108 break;
109 case 'd':
110 dflag = 1;
111 break;
112 case 'e':
113 eflag = 1;
114 break;
115 case 'f':
116 if (spec1 == stdin) {
117 spec1 = fopen(optarg, "r");
118 if (spec1 == NULL) {
119 error = errno;
120 RECORD_FAILURE(88, error);
121 errc(1, error, "%s", optarg);
122 }
123 } else if (spec2 == NULL) {
124 spec2 = fopen(optarg, "r");
125 if (spec2 == NULL) {
126 error = errno;
127 RECORD_FAILURE(89, error);
128 errc(1, error, "%s", optarg);
129 }
130 } else {
131 RECORD_FAILURE(90, WARN_USAGE);
132 usage();
133 }
134 break;
135 case 'i':
136 iflag = 1;
137 break;
138 case 'K':
139 while ((p = strsep(&optarg, " \t,")) != NULL)
140 if (*p != '\0')
141 keys |= parsekey(p, NULL);
142 break;
143 case 'k':
144 keys = F_TYPE;
145 while ((p = strsep(&optarg, " \t,")) != NULL)
146 if (*p != '\0')
147 keys |= parsekey(p, NULL);
148 break;
149 case 'L':
150 ftsoptions &= ~FTS_PHYSICAL;
151 ftsoptions |= FTS_LOGICAL;
152 break;
153 case 'n':
154 nflag = 1;
155 break;
156 case 'P':
157 ftsoptions &= ~FTS_LOGICAL;
158 ftsoptions |= FTS_PHYSICAL;
159 break;
160 case 'p':
161 dir = optarg;
162 break;
163 case 'q':
164 qflag = 1;
165 break;
166 case 'r':
167 rflag = 1;
168 break;
169 case 's':
170 sflag = 1;
171 crc_total = (uint32_t)~strtoul(optarg, &p, 0);
172 if (*p) {
173 RECORD_FAILURE(91, WARN_USAGE);
174 errx(1, "illegal seed value -- %s", optarg);
175 }
176 break;
177 case 'U':
178 Uflag = 1;
179 uflag = 1;
180 break;
181 case 'u':
182 uflag = 1;
183 break;
184 case 'w':
185 wflag = 1;
186 break;
187 case 'x':
188 ftsoptions |= FTS_XDEV;
189 break;
190 case 'X':
191 read_excludes_file(optarg);
192 break;
193 case 'm':
194 mflag = 1;
195 filepath = strdup(optarg);
196 dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
197 &kCFTypeDictionaryKeyCallBacks,
198 &kCFTypeDictionaryValueCallBacks);
199 insert_access = insert_mod = insert_birth = insert_change = 0;
200 break;
201 case 'F':
202 timeformat = optarg;
203 break;
204 case 't':
205 timestamp = optarg;
206 tflag = 1;
207 break;
208 case 'E':
209 if (!strcmp(optarg, "-")) {
210 file = stdout;
211 } else {
212 file = fopen(optarg, "w");
213 }
214 if (file == NULL) {
215 warnx("Could not open metrics log file %s", optarg);
216 } else {
217 set_metrics_file(file);
218 }
219 break;
220 case 'S':
221 xflag = 1;
222 break;
223 case '?':
224 default:
225 RECORD_FAILURE(92, WARN_USAGE);
226 usage();
227 }
228 argc -= optind;
229 // argv += optind;
230
231 if (argc) {
232 RECORD_FAILURE(93, WARN_USAGE);
233 usage();
234 }
235
236 if (timestamp) {
237 struct tm t = {};
238 char *r = strptime(timestamp, timeformat, &t);
239 if (r && r[0] == '\0') {
240 ts.tv_sec = mktime(&t);
241 if ((ts.tv_sec - time(NULL)) > 30 * SECONDS_IN_A_DAY) {
242 RECORD_FAILURE(94, WARN_TIME);
243 errx(1, "Time is more then 30 days in the future");
244 } else if (ts.tv_sec < 0) {
245 RECORD_FAILURE(95, WARN_TIME);
246 errx(1, "Time is too far in the past");
247 }
248 } else {
249 RECORD_FAILURE(96, WARN_TIME);
250 errx(1,"Cannot parse timestamp '%s' using format \"%s\"\n", timestamp, timeformat);
251 }
252 }
253
254 if (dir && chdir(dir)) {
255 error = errno;
256 RECORD_FAILURE(97, error);
257 errc(1, error, "%s", dir);
258 }
259
260 if ((cflag || sflag) && !getwd(fullpath)) {
261 RECORD_FAILURE(98, errno);
262 errx(1, "%s", fullpath);
263 }
264
265 if (dir) {
266 set_metric_path(dir);
267 }
268
269 if (cflag) {
270 cwalk();
271 exit(0);
272 }
273 if (spec2 != NULL) {
274 status = mtree_specspec(spec1, spec2);
275 if (Uflag & (status == MISMATCHEXIT)) {
276 status = 0;
277 } else {
278 RECORD_FAILURE(99, status);
279 }
280 } else {
281 status = mtree_verifyspec(spec1);
282 if (Uflag & (status == MISMATCHEXIT)) {
283 status = 0;
284 } else if (status) {
285 RECORD_FAILURE(100, status);
286 }
287 if (mflag && CFDictionaryGetCount(dict)) {
288 if (!write_plist_to_file()) {
289 RECORD_FAILURE(101, EIO);
290 errx(1, "could not write manifest to the file\n");
291 }
292 }
293 }
294 exit(status);
295 }
296
297 static void
298 usage(void)
299 {
300 (void)fprintf(stderr,
301 "usage: mtree [-LPUScdeinqruxw] [-f spec] [-K key] [-k key] [-p path] [-s seed] [-m xml dictionary] [-t timestamp]\n"
302 "\t[-X excludes]\n");
303 exit(1);
304 }
305
306 static bool
307 write_plist_to_file(void)
308 {
309 if (!dict || !filepath) {
310 RECORD_FAILURE(102, EINVAL);
311 return false;
312 }
313
314 CFIndex bytes_written = 0;
315 bool status = true;
316
317 CFStringRef file_path_str = CFStringCreateWithCString(kCFAllocatorDefault, (const char *)filepath, kCFStringEncodingUTF8);
318
319 // Create a URL specifying the file to hold the XML data.
320 CFURLRef fileURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault,
321 file_path_str, // file path name
322 kCFURLPOSIXPathStyle, // interpret as POSIX path
323 false); // is it a directory?
324
325 if (!fileURL) {
326 CFRelease(file_path_str);
327 RECORD_FAILURE(103, EINVAL);
328 return false;
329 }
330
331 CFWriteStreamRef output_stream = CFWriteStreamCreateWithFile(kCFAllocatorDefault, fileURL);
332
333 if (!output_stream) {
334 CFRelease(file_path_str);
335 CFRelease(fileURL);
336 RECORD_FAILURE(104, EIO);
337 return false;
338 }
339
340 if ((status = CFWriteStreamOpen(output_stream))) {
341 bytes_written = CFPropertyListWrite((CFPropertyListRef)dict, output_stream, kCFPropertyListXMLFormat_v1_0, 0, NULL);
342 CFWriteStreamClose(output_stream);
343 } else {
344 status = false;
345 RECORD_FAILURE(105, EIO);
346 goto out;
347 }
348
349 if (!bytes_written) {
350 status = false;
351 RECORD_FAILURE(106, EIO);
352 }
353
354 out:
355 CFRelease(output_stream);
356 CFRelease(fileURL);
357 CFRelease(file_path_str);
358
359 return status;
360 }