file_cmds-264.1.1.tar.gz
[apple/file_cmds.git] / mtree / compare.c
1 /*-
2 * Copyright (c) 1989, 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 char sccsid[] = "@(#)compare.c 8.1 (Berkeley) 6/6/93";
33 #endif /* not lint */
34 #endif
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD: src/usr.sbin/mtree/compare.c,v 1.34 2005/03/29 11:44:17 tobez Exp $");
37
38 #include <sys/param.h>
39 #include <sys/stat.h>
40 #include <sys/time.h>
41
42 #include <err.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <fts.h>
46 #ifndef __APPLE__
47 #ifdef ENABLE_MD5
48 #include <md5.h>
49 #endif
50 #ifdef ENABLE_RMD160
51 #include <ripemd.h>
52 #endif
53 #ifdef ENABLE_SHA1
54 #include <sha.h>
55 #endif
56 #ifdef ENABLE_SHA256
57 #include <sha256.h>
58 #endif
59 #endif /* !__APPLE__ */
60 #include <stdint.h>
61 #include <stdio.h>
62 #include <time.h>
63 #include <unistd.h>
64 #include <vis.h>
65
66 #include "mtree.h"
67 #include "extern.h"
68
69 #ifdef __APPLE__
70 #include "commoncrypto.h"
71 #endif /* __APPLE__ */
72
73 #define INDENTNAMELEN 8
74 #define LABEL \
75 if (!label++) { \
76 len = printf("%s changed\n", RP(p)); \
77 tab = "\t"; \
78 }
79
80 int
81 compare(char *name __unused, NODE *s, FTSENT *p)
82 {
83 struct timeval tv[2];
84 uint32_t val;
85 int fd, label;
86 off_t len;
87 char *cp;
88 const char *tab = "";
89 char *fflags, *badflags;
90 u_long flags;
91
92 label = 0;
93 switch(s->type) {
94 case F_BLOCK:
95 if (!S_ISBLK(p->fts_statp->st_mode))
96 goto typeerr;
97 break;
98 case F_CHAR:
99 if (!S_ISCHR(p->fts_statp->st_mode))
100 goto typeerr;
101 break;
102 case F_DIR:
103 if (!S_ISDIR(p->fts_statp->st_mode))
104 goto typeerr;
105 break;
106 case F_FIFO:
107 if (!S_ISFIFO(p->fts_statp->st_mode))
108 goto typeerr;
109 break;
110 case F_FILE:
111 if (!S_ISREG(p->fts_statp->st_mode))
112 goto typeerr;
113 break;
114 case F_LINK:
115 if (!S_ISLNK(p->fts_statp->st_mode))
116 goto typeerr;
117 break;
118 case F_SOCK:
119 if (!S_ISSOCK(p->fts_statp->st_mode)) {
120 typeerr: LABEL;
121 (void)printf("\ttype expected %s found %s\n",
122 ftype(s->type), inotype(p->fts_statp->st_mode));
123 return (label);
124 }
125 break;
126 }
127 /* Set the uid/gid first, then set the mode. */
128 if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) {
129 LABEL;
130 (void)printf("%suser expected %lu found %lu",
131 tab, (u_long)s->st_uid, (u_long)p->fts_statp->st_uid);
132 if (uflag)
133 if (chown(p->fts_accpath, s->st_uid, -1))
134 (void)printf(" not modified: %s\n",
135 strerror(errno));
136 else
137 (void)printf(" modified\n");
138 else
139 (void)printf("\n");
140 tab = "\t";
141 }
142 if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) {
143 LABEL;
144 (void)printf("%sgid expected %lu found %lu",
145 tab, (u_long)s->st_gid, (u_long)p->fts_statp->st_gid);
146 if (uflag)
147 if (chown(p->fts_accpath, -1, s->st_gid))
148 (void)printf(" not modified: %s\n",
149 strerror(errno));
150 else
151 (void)printf(" modified\n");
152 else
153 (void)printf("\n");
154 tab = "\t";
155 }
156 if (s->flags & F_MODE &&
157 !S_ISLNK(p->fts_statp->st_mode) &&
158 s->st_mode != (p->fts_statp->st_mode & MBITS)) {
159 LABEL;
160 (void)printf("%spermissions expected %#o found %#o",
161 tab, s->st_mode, p->fts_statp->st_mode & MBITS);
162 if (uflag)
163 if (chmod(p->fts_accpath, s->st_mode))
164 (void)printf(" not modified: %s\n",
165 strerror(errno));
166 else
167 (void)printf(" modified\n");
168 else
169 (void)printf("\n");
170 tab = "\t";
171 }
172 if (s->flags & F_NLINK && s->type != F_DIR &&
173 s->st_nlink != p->fts_statp->st_nlink) {
174 LABEL;
175 (void)printf("%slink_count expected %u found %u\n",
176 tab, s->st_nlink, p->fts_statp->st_nlink);
177 tab = "\t";
178 }
179 if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size &&
180 !S_ISDIR(p->fts_statp->st_mode)) {
181 LABEL;
182 (void)printf("%ssize expected %jd found %jd\n", tab,
183 (intmax_t)s->st_size, (intmax_t)p->fts_statp->st_size);
184 tab = "\t";
185 }
186 if ((s->flags & F_TIME) &&
187 ((s->st_mtimespec.tv_sec != p->fts_statp->st_mtimespec.tv_sec) ||
188 (s->st_mtimespec.tv_nsec != p->fts_statp->st_mtimespec.tv_nsec))) {
189 LABEL;
190 (void)printf("%smodification time expected %.24s.%09ld ",
191 tab, ctime(&s->st_mtimespec.tv_sec), s->st_mtimespec.tv_nsec);
192 (void)printf("found %.24s.%09ld",
193 ctime(&p->fts_statp->st_mtimespec.tv_sec), p->fts_statp->st_mtimespec.tv_nsec);
194 if (uflag) {
195 tv[0].tv_sec = s->st_mtimespec.tv_sec;
196 tv[0].tv_usec = s->st_mtimespec.tv_nsec / 1000;
197 tv[1] = tv[0];
198 if (utimes(p->fts_accpath, tv))
199 (void)printf(" not modified: %s\n",
200 strerror(errno));
201 else
202 (void)printf(" modified\n");
203 } else
204 (void)printf("\n");
205 tab = "\t";
206 }
207 if (s->flags & F_CKSUM) {
208 if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) {
209 LABEL;
210 (void)printf("%scksum: %s: %s\n",
211 tab, p->fts_accpath, strerror(errno));
212 tab = "\t";
213 } else if (crc(fd, &val, &len)) {
214 (void)close(fd);
215 LABEL;
216 (void)printf("%scksum: %s: %s\n",
217 tab, p->fts_accpath, strerror(errno));
218 tab = "\t";
219 } else {
220 (void)close(fd);
221 if (s->cksum != val) {
222 LABEL;
223 (void)printf("%scksum expected %lu found %lu\n",
224 tab, s->cksum, (unsigned long)val);
225 tab = "\t";
226 }
227 }
228 }
229 if (s->flags & F_FLAGS) {
230 // There are unpublished flags that should not fail comparison
231 // we convert to string and back to filter them out
232 fflags = badflags = flags_to_string(p->fts_statp->st_flags);
233 if (strcmp("none", fflags) == 0) {
234 flags = 0;
235 } else if (strtofflags(&badflags, &flags, NULL) != 0)
236 errx(1, "invalid flag %s", badflags);
237 free(fflags);
238 if (s->st_flags != flags) {
239 LABEL;
240 fflags = flags_to_string(s->st_flags);
241 (void)printf("%sflags expected \"%s\"", tab, fflags);
242 free(fflags);
243
244 fflags = flags_to_string(flags);
245 (void)printf(" found \"%s\"", fflags);
246 free(fflags);
247
248 if (uflag)
249 if (chflags(p->fts_accpath, (u_int)s->st_flags))
250 (void)printf(" not modified: %s\n",
251 strerror(errno));
252 else
253 (void)printf(" modified\n");
254 else
255 (void)printf("\n");
256 tab = "\t";
257 }
258 }
259 #ifdef ENABLE_MD5
260 if (s->flags & F_MD5) {
261 char *new_digest, buf[33];
262
263 new_digest = MD5File(p->fts_accpath, buf);
264 if (!new_digest) {
265 LABEL;
266 printf("%sMD5: %s: %s\n", tab, p->fts_accpath,
267 strerror(errno));
268 tab = "\t";
269 } else if (strcmp(new_digest, s->md5digest)) {
270 LABEL;
271 printf("%sMD5 expected %s found %s\n", tab, s->md5digest,
272 new_digest);
273 tab = "\t";
274 }
275 }
276 #endif /* ENABLE_MD5 */
277 #ifdef ENABLE_SHA1
278 if (s->flags & F_SHA1) {
279 char *new_digest, buf[41];
280
281 new_digest = SHA1_File(p->fts_accpath, buf);
282 if (!new_digest) {
283 LABEL;
284 printf("%sSHA-1: %s: %s\n", tab, p->fts_accpath,
285 strerror(errno));
286 tab = "\t";
287 } else if (strcmp(new_digest, s->sha1digest)) {
288 LABEL;
289 printf("%sSHA-1 expected %s found %s\n",
290 tab, s->sha1digest, new_digest);
291 tab = "\t";
292 }
293 }
294 #endif /* ENABLE_SHA1 */
295 #ifdef ENABLE_RMD160
296 if (s->flags & F_RMD160) {
297 char *new_digest, buf[41];
298
299 new_digest = RIPEMD160_File(p->fts_accpath, buf);
300 if (!new_digest) {
301 LABEL;
302 printf("%sRIPEMD160: %s: %s\n", tab,
303 p->fts_accpath, strerror(errno));
304 tab = "\t";
305 } else if (strcmp(new_digest, s->rmd160digest)) {
306 LABEL;
307 printf("%sRIPEMD160 expected %s found %s\n",
308 tab, s->rmd160digest, new_digest);
309 tab = "\t";
310 }
311 }
312 #endif /* ENABLE_RMD160 */
313 #ifdef ENABLE_SHA256
314 if (s->flags & F_SHA256) {
315 char *new_digest, buf[kSHA256NullTerminatedBuffLen];
316
317 new_digest = SHA256_File(p->fts_accpath, buf);
318 if (!new_digest) {
319 LABEL;
320 printf("%sSHA-256: %s: %s\n", tab, p->fts_accpath,
321 strerror(errno));
322 tab = "\t";
323 } else if (strcmp(new_digest, s->sha256digest)) {
324 LABEL;
325 printf("%sSHA-256 expected %s found %s\n",
326 tab, s->sha256digest, new_digest);
327 tab = "\t";
328 }
329 }
330 #endif /* ENABLE_SHA256 */
331
332 if (s->flags & F_SLINK &&
333 strcmp(cp = rlink(p->fts_accpath), s->slink)) {
334 LABEL;
335 (void)printf("%slink_ref expected %s found %s\n",
336 tab, s->slink, cp);
337 }
338 if ((s->flags & F_BTIME) &&
339 ((s->st_birthtimespec.tv_sec != p->fts_statp->st_birthtimespec.tv_sec) ||
340 (s->st_birthtimespec.tv_nsec != p->fts_statp->st_birthtimespec.tv_nsec))) {
341 LABEL;
342 (void)printf("%sbirth time expected %.24s.%09ld ",
343 tab, ctime(&s->st_birthtimespec.tv_sec), s->st_birthtimespec.tv_nsec);
344 (void)printf("found %.24s.%09ld\n",
345 ctime(&p->fts_statp->st_birthtimespec.tv_sec), p->fts_statp->st_birthtimespec.tv_nsec);
346 tab = "\t";
347 }
348 if ((s->flags & F_ATIME) &&
349 ((s->st_atimespec.tv_sec != p->fts_statp->st_atimespec.tv_sec) ||
350 (s->st_atimespec.tv_nsec != p->fts_statp->st_atimespec.tv_nsec))) {
351 LABEL;
352 (void)printf("%saccess time expected %.24s.%09ld ",
353 tab, ctime(&s->st_atimespec.tv_sec), s->st_atimespec.tv_nsec);
354 (void)printf("found %.24s.%09ld\n",
355 ctime(&p->fts_statp->st_atimespec.tv_sec), p->fts_statp->st_atimespec.tv_nsec);
356 tab = "\t";
357 }
358 if ((s->flags & F_CTIME) &&
359 ((s->st_ctimespec.tv_sec != p->fts_statp->st_ctimespec.tv_sec) ||
360 (s->st_ctimespec.tv_nsec != p->fts_statp->st_ctimespec.tv_nsec))) {
361 LABEL;
362 (void)printf("%smetadata modification time expected %.24s.%09ld ",
363 tab, ctime(&s->st_ctimespec.tv_sec), s->st_ctimespec.tv_nsec);
364 (void)printf("found %.24s.%09ld\n",
365 ctime(&p->fts_statp->st_ctimespec.tv_sec), p->fts_statp->st_ctimespec.tv_nsec);
366 tab = "\t";
367 }
368 if (s->flags & F_PTIME) {
369 int supported;
370 struct timespec ptimespec = ptime(p->fts_accpath, &supported);
371 if (!supported) {
372 LABEL;
373 (void)printf("%stime added to parent folder expected %.24s.%09ld found that it is not supported\n",
374 tab, ctime(&s->st_ptimespec.tv_sec), s->st_ptimespec.tv_nsec);
375 tab = "\t";
376 } else if ((s->st_ptimespec.tv_sec != ptimespec.tv_sec) ||
377 (s->st_ptimespec.tv_nsec != ptimespec.tv_nsec)) {
378 LABEL;
379 (void)printf("%stime added to parent folder expected %.24s.%09ld ",
380 tab, ctime(&s->st_ptimespec.tv_sec), s->st_ptimespec.tv_nsec);
381 (void)printf("found %.24s.%09ld\n",
382 ctime(&ptimespec.tv_sec), ptimespec.tv_nsec);
383 tab = "\t";
384 }
385 }
386 if (s->flags & F_XATTRS) {
387 char *new_digest, buf[kSHA256NullTerminatedBuffLen];
388 new_digest = SHA256_Path_XATTRs(p->fts_accpath, buf);
389 if (!new_digest) {
390 LABEL;
391 printf("%sxattrsdigest missing, expected: %s\n", tab, s->xattrsdigest);
392 tab = "\t";
393 } else if (strcmp(new_digest, s->xattrsdigest)) {
394 LABEL;
395 printf("%sxattrsdigest expected %s found %s\n",
396 tab, s->xattrsdigest, new_digest);
397 tab = "\t";
398 }
399 }
400 if ((s->flags & F_INODE) &&
401 (p->fts_statp->st_ino != s->st_ino)) {
402 LABEL;
403 (void)printf("%sinode expected %llu found %llu\n",
404 tab, s->st_ino, p->fts_ino);
405 tab = "\t";
406 }
407 if (s->flags & F_ACL) {
408 char *new_digest, buf[kSHA256NullTerminatedBuffLen];
409 new_digest = SHA256_Path_ACL(p->fts_accpath, buf);
410 if (!new_digest) {
411 LABEL;
412 printf("%sacldigest missing, expected: %s\n", tab, s->acldigest);
413 tab = "\t";
414 } else if (strcmp(new_digest, s->acldigest)) {
415 LABEL;
416 printf("%sacldigest expected %s found %s\n",
417 tab, s->acldigest, new_digest);
418 tab = "\t";
419 }
420 }
421
422 return (label);
423 }
424
425 const char *
426 inotype(u_int type)
427 {
428 switch(type & S_IFMT) {
429 case S_IFBLK:
430 return ("block");
431 case S_IFCHR:
432 return ("char");
433 case S_IFDIR:
434 return ("dir");
435 case S_IFIFO:
436 return ("fifo");
437 case S_IFREG:
438 return ("file");
439 case S_IFLNK:
440 return ("link");
441 case S_IFSOCK:
442 return ("socket");
443 default:
444 return ("unknown");
445 }
446 /* NOTREACHED */
447 }
448
449 const char *
450 ftype(u_int type)
451 {
452 switch(type) {
453 case F_BLOCK:
454 return ("block");
455 case F_CHAR:
456 return ("char");
457 case F_DIR:
458 return ("dir");
459 case F_FIFO:
460 return ("fifo");
461 case F_FILE:
462 return ("file");
463 case F_LINK:
464 return ("link");
465 case F_SOCK:
466 return ("socket");
467 default:
468 return ("unknown");
469 }
470 /* NOTREACHED */
471 }
472
473 char *
474 rlink(char *name)
475 {
476 static char lbuf[MAXPATHLEN];
477 ssize_t len;
478
479 if ((len = readlink(name, lbuf, sizeof(lbuf) - 1)) == -1)
480 err(1, "line %d: %s", lineno, name);
481 lbuf[len] = '\0';
482 return (lbuf);
483 }