2 * Copyright (c) 1989, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
32 static char sccsid
[] = "@(#)compare.c 8.1 (Berkeley) 6/6/93";
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 $");
38 #include <CoreFoundation/CoreFoundation.h>
39 #include <sys/param.h>
60 #endif /* !__APPLE__ */
72 #include "commoncrypto.h"
73 #endif /* __APPLE__ */
75 #define INDENTNAMELEN 8
78 len = printf("%s changed\n", RP(p)); \
82 extern CFMutableDictionaryRef dict
;
84 // max/min times apfs can store on disk
85 #define APFS_MAX_TIME 0x7fffffffffffffffLL
86 #define APFS_MIN_TIME (-0x7fffffffffffffffLL-1)
89 timespec_to_apfs_timestamp(struct timespec
*ts
)
94 // `tv_nsec' can be > one billion, so we split it into two components:
95 // seconds and actual nanoseconds
96 // this allows us to detect overflow on the *total* number of nanoseconds
97 // e.g. if (MAX_SECONDS+2, -2billion) is passed in, we return MAX_SECONDS
98 seconds
= ((int64_t)ts
->tv_nsec
/ (int64_t)NSEC_PER_SEC
);
100 // compute total nanoseconds, checking for overflow:
101 // seconds = sec + (ns/10e9)
102 // total = seconds*10e9 + ns%10e9
103 if (__builtin_saddll_overflow(ts
->tv_sec
, seconds
, &seconds
) ||
104 __builtin_smulll_overflow(seconds
, NSEC_PER_SEC
, &total
) ||
105 __builtin_saddll_overflow(((int64_t)ts
->tv_nsec
% (int64_t)NSEC_PER_SEC
), total
, &total
)) {
106 // checking the sign of "seconds" tells us whether to cap the value at
107 // the max or min time
108 total
= (ts
->tv_sec
> 0) ? APFS_MAX_TIME
: APFS_MIN_TIME
;
111 return (uint64_t)total
;
115 set_key_value_pair(void *in_key
, uint64_t *in_val
, bool is_string
)
121 key
= CFStringCreateWithCString(NULL
, (const char*)in_key
, kCFStringEncodingUTF8
);
124 key
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("%llu"), *(uint64_t*)in_key
);
127 val
= CFNumberCreate(NULL
, kCFNumberSInt64Type
, in_val
);
129 // we always expect the key to be not present
131 CFDictionaryAddValue(dict
, key
, val
);
139 RECORD_FAILURE(1, EINVAL
);
140 errx(1, "set_key_value_pair: key/value is null");
152 compare(char *name __unused
, NODE
*s
, FTSENT
*p
)
155 struct timeval tv
[2];
160 const char *tab
= "";
161 char *fflags
, *badflags
;
167 if (!S_ISBLK(p
->fts_statp
->st_mode
)) {
168 RECORD_FAILURE(2, EINVAL
);
173 if (!S_ISCHR(p
->fts_statp
->st_mode
)) {
174 RECORD_FAILURE(3, EINVAL
);
179 if (!S_ISDIR(p
->fts_statp
->st_mode
)) {
180 RECORD_FAILURE(4, EINVAL
);
185 if (!S_ISFIFO(p
->fts_statp
->st_mode
)) {
186 RECORD_FAILURE(5, EINVAL
);
191 if (!S_ISREG(p
->fts_statp
->st_mode
)) {
192 RECORD_FAILURE(6, EINVAL
);
197 if (!S_ISLNK(p
->fts_statp
->st_mode
)) {
198 RECORD_FAILURE(7, EINVAL
);
203 if (!S_ISSOCK(p
->fts_statp
->st_mode
)) {
204 RECORD_FAILURE(8, EINVAL
);
206 (void)printf("\ttype expected %s found %s\n",
207 ftype(s
->type
), inotype(p
->fts_statp
->st_mode
));
212 /* Set the uid/gid first, then set the mode. */
213 if (s
->flags
& (F_UID
| F_UNAME
) && s
->st_uid
!= p
->fts_statp
->st_uid
) {
215 (void)printf("%suser expected %lu found %lu",
216 tab
, (u_long
)s
->st_uid
, (u_long
)p
->fts_statp
->st_uid
);
218 if (chown(p
->fts_accpath
, s
->st_uid
, -1)) {
220 RECORD_FAILURE(9, error
);
221 (void)printf(" not modified: %s\n",
224 (void)printf(" modified\n");
231 if (s
->flags
& (F_GID
| F_GNAME
) && s
->st_gid
!= p
->fts_statp
->st_gid
) {
233 (void)printf("%sgid expected %lu found %lu",
234 tab
, (u_long
)s
->st_gid
, (u_long
)p
->fts_statp
->st_gid
);
236 if (chown(p
->fts_accpath
, -1, s
->st_gid
)) {
238 RECORD_FAILURE(10, error
);
239 (void)printf(" not modified: %s\n",
242 (void)printf(" modified\n");
249 if (s
->flags
& F_MODE
&&
250 !S_ISLNK(p
->fts_statp
->st_mode
) &&
251 s
->st_mode
!= (p
->fts_statp
->st_mode
& MBITS
)) {
253 (void)printf("%spermissions expected %#o found %#o",
254 tab
, s
->st_mode
, p
->fts_statp
->st_mode
& MBITS
);
256 if (chmod(p
->fts_accpath
, s
->st_mode
)) {
258 RECORD_FAILURE(11, error
);
259 (void)printf(" not modified: %s\n",
262 (void)printf(" modified\n");
269 if (s
->flags
& F_NLINK
&& s
->type
!= F_DIR
&&
270 s
->st_nlink
!= p
->fts_statp
->st_nlink
) {
272 (void)printf("%slink_count expected %u found %u\n",
273 tab
, s
->st_nlink
, p
->fts_statp
->st_nlink
);
276 if (s
->flags
& F_SIZE
&& s
->st_size
!= p
->fts_statp
->st_size
&&
277 !S_ISDIR(p
->fts_statp
->st_mode
)) {
279 (void)printf("%ssize expected %jd found %jd\n", tab
,
280 (intmax_t)s
->st_size
, (intmax_t)p
->fts_statp
->st_size
);
283 if ((s
->flags
& F_TIME
) &&
284 ((s
->st_mtimespec
.tv_sec
!= p
->fts_statp
->st_mtimespec
.tv_sec
) ||
285 (s
->st_mtimespec
.tv_nsec
!= p
->fts_statp
->st_mtimespec
.tv_nsec
))) {
288 (void)printf("%smodification time expected %.24s.%09ld ",
289 tab
, ctime(&s
->st_mtimespec
.tv_sec
), s
->st_mtimespec
.tv_nsec
);
290 (void)printf("found %.24s.%09ld",
291 ctime(&p
->fts_statp
->st_mtimespec
.tv_sec
), p
->fts_statp
->st_mtimespec
.tv_nsec
);
293 tv
[0].tv_sec
= s
->st_mtimespec
.tv_sec
;
294 tv
[0].tv_usec
= s
->st_mtimespec
.tv_nsec
/ 1000;
296 if (utimes(p
->fts_accpath
, tv
)) {
298 RECORD_FAILURE(12, error
);
299 (void)printf(" not modified: %s\n",
302 (void)printf(" modified\n");
309 if (!insert_mod
&& mflag
) {
310 uint64_t s_mod_time
= timespec_to_apfs_timestamp(&s
->st_mtimespec
);
311 char *mod_string
= "MODIFICATION";
312 set_key_value_pair(mod_string
, &s_mod_time
, true);
316 if (s
->flags
& F_CKSUM
) {
317 if ((fd
= open(p
->fts_accpath
, O_RDONLY
, 0)) < 0) {
320 RECORD_FAILURE(13, error
);
321 (void)printf("%scksum: %s: %s\n",
322 tab
, p
->fts_accpath
, strerror(error
));
324 } else if (crc(fd
, &val
, &len
)) {
328 RECORD_FAILURE(14, error
);
329 (void)printf("%scksum: %s: %s\n",
330 tab
, p
->fts_accpath
, strerror(error
));
334 if (s
->cksum
!= val
) {
336 (void)printf("%scksum expected %lu found %lu\n",
337 tab
, s
->cksum
, (unsigned long)val
);
342 if (s
->flags
& F_FLAGS
) {
343 // There are unpublished flags that should not fail comparison
344 // we convert to string and back to filter them out
345 fflags
= badflags
= flags_to_string(p
->fts_statp
->st_flags
);
346 if (strcmp("none", fflags
) == 0) {
348 } else if (strtofflags(&badflags
, &flags
, NULL
) != 0)
349 errx(1, "invalid flag %s", badflags
);
351 if (s
->st_flags
!= flags
) {
353 fflags
= flags_to_string(s
->st_flags
);
354 (void)printf("%sflags expected \"%s\"", tab
, fflags
);
357 fflags
= flags_to_string(flags
);
358 (void)printf(" found \"%s\"", fflags
);
362 if (chflags(p
->fts_accpath
, (u_int
)s
->st_flags
)) {
364 RECORD_FAILURE(15, error
);
365 (void)printf(" not modified: %s\n",
368 (void)printf(" modified\n");
377 if (s
->flags
& F_MD5
) {
378 char *new_digest
, buf
[33];
380 /* clang doesn't like MD5 due to security concerns, but it's used for file data/metadata integrity.. */
381 #pragma clang diagnostic push
382 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
383 new_digest
= MD5File(p
->fts_accpath
, buf
);
384 #pragma clang diagnostic pop
389 RECORD_FAILURE(16, error
);
390 printf("%sMD5: %s: %s\n", tab
, p
->fts_accpath
,
393 } else if (strcmp(new_digest
, s
->md5digest
)) {
395 printf("%sMD5 expected %s found %s\n", tab
, s
->md5digest
,
400 #endif /* ENABLE_MD5 */
402 if (s
->flags
& F_SHA1
) {
403 char *new_digest
, buf
[41];
405 /* clang doesn't like SHA1 due to security concerns, but it's used for file data/metadata integrity.. */
406 #pragma clang diagnostic push
407 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
408 new_digest
= SHA1_File(p
->fts_accpath
, buf
);
409 #pragma clang diagnostic pop
414 RECORD_FAILURE(17, error
);
415 printf("%sSHA-1: %s: %s\n", tab
, p
->fts_accpath
,
418 } else if (strcmp(new_digest
, s
->sha1digest
)) {
420 printf("%sSHA-1 expected %s found %s\n",
421 tab
, s
->sha1digest
, new_digest
);
425 #endif /* ENABLE_SHA1 */
427 if (s
->flags
& F_RMD160
) {
428 char *new_digest
, buf
[41];
430 /* clang doesn't like RIPEMD160 due to security concerns, but it's used for file data/metadata integrity.. */
431 #pragma clang diagnostic push
432 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
433 new_digest
= RIPEMD160_File(p
->fts_accpath
, buf
);
434 #pragma clang diagnostic pop
439 RECORD_FAILURE(18, error
);
440 printf("%sRIPEMD160: %s: %s\n", tab
,
441 p
->fts_accpath
, strerror(error
));
443 } else if (strcmp(new_digest
, s
->rmd160digest
)) {
445 printf("%sRIPEMD160 expected %s found %s\n",
446 tab
, s
->rmd160digest
, new_digest
);
450 #endif /* ENABLE_RMD160 */
452 if (s
->flags
& F_SHA256
) {
453 char *new_digest
, buf
[kSHA256NullTerminatedBuffLen
];
455 new_digest
= SHA256_File(p
->fts_accpath
, buf
);
459 RECORD_FAILURE(19, error
);
460 printf("%sSHA-256: %s: %s\n", tab
, p
->fts_accpath
,
463 } else if (strcmp(new_digest
, s
->sha256digest
)) {
465 printf("%sSHA-256 expected %s found %s\n",
466 tab
, s
->sha256digest
, new_digest
);
470 #endif /* ENABLE_SHA256 */
472 if (s
->flags
& F_SLINK
&&
473 strcmp(cp
= rlink(p
->fts_accpath
), s
->slink
)) {
475 (void)printf("%slink_ref expected %s found %s\n",
478 if ((s
->flags
& F_BTIME
) &&
479 ((s
->st_birthtimespec
.tv_sec
!= p
->fts_statp
->st_birthtimespec
.tv_sec
) ||
480 (s
->st_birthtimespec
.tv_nsec
!= p
->fts_statp
->st_birthtimespec
.tv_nsec
))) {
483 (void)printf("%sbirth time expected %.24s.%09ld ",
484 tab
, ctime(&s
->st_birthtimespec
.tv_sec
), s
->st_birthtimespec
.tv_nsec
);
485 (void)printf("found %.24s.%09ld\n",
486 ctime(&p
->fts_statp
->st_birthtimespec
.tv_sec
), p
->fts_statp
->st_birthtimespec
.tv_nsec
);
489 if (!insert_birth
&& mflag
) {
490 uint64_t s_create_time
= timespec_to_apfs_timestamp(&s
->st_birthtimespec
);
491 char *birth_string
= "BIRTH";
492 set_key_value_pair(birth_string
, &s_create_time
, true);
496 if ((s
->flags
& F_ATIME
) &&
497 ((s
->st_atimespec
.tv_sec
!= p
->fts_statp
->st_atimespec
.tv_sec
) ||
498 (s
->st_atimespec
.tv_nsec
!= p
->fts_statp
->st_atimespec
.tv_nsec
))) {
501 (void)printf("%saccess time expected %.24s.%09ld ",
502 tab
, ctime(&s
->st_atimespec
.tv_sec
), s
->st_atimespec
.tv_nsec
);
503 (void)printf("found %.24s.%09ld\n",
504 ctime(&p
->fts_statp
->st_atimespec
.tv_sec
), p
->fts_statp
->st_atimespec
.tv_nsec
);
507 if (!insert_access
&& mflag
) {
508 uint64_t s_access_time
= timespec_to_apfs_timestamp(&s
->st_atimespec
);
509 char *access_string
= "ACCESS";
510 set_key_value_pair(access_string
, &s_access_time
, true);
515 if ((s
->flags
& F_CTIME
) &&
516 ((s
->st_ctimespec
.tv_sec
!= p
->fts_statp
->st_ctimespec
.tv_sec
) ||
517 (s
->st_ctimespec
.tv_nsec
!= p
->fts_statp
->st_ctimespec
.tv_nsec
))) {
520 (void)printf("%smetadata modification time expected %.24s.%09ld ",
521 tab
, ctime(&s
->st_ctimespec
.tv_sec
), s
->st_ctimespec
.tv_nsec
);
522 (void)printf("found %.24s.%09ld\n",
523 ctime(&p
->fts_statp
->st_ctimespec
.tv_sec
), p
->fts_statp
->st_ctimespec
.tv_nsec
);
526 if (!insert_change
&& mflag
) {
527 uint64_t s_mod_time
= timespec_to_apfs_timestamp(&s
->st_ctimespec
);
528 char *change_string
= "CHANGE";
529 set_key_value_pair(change_string
, &s_mod_time
, true);
533 if (s
->flags
& F_PTIME
) {
535 struct timespec ptimespec
= ptime(p
->fts_accpath
, &supported
);
538 (void)printf("%stime added to parent folder expected %.24s.%09ld found that it is not supported\n",
539 tab
, ctime(&s
->st_ptimespec
.tv_sec
), s
->st_ptimespec
.tv_nsec
);
541 } else if (supported
&& ((s
->st_ptimespec
.tv_sec
!= ptimespec
.tv_sec
) ||
542 (s
->st_ptimespec
.tv_nsec
!= ptimespec
.tv_nsec
))) {
545 (void)printf("%stime added to parent folder expected %.24s.%09ld ",
546 tab
, ctime(&s
->st_ptimespec
.tv_sec
), s
->st_ptimespec
.tv_nsec
);
547 (void)printf("found %.24s.%09ld\n",
548 ctime(&ptimespec
.tv_sec
), ptimespec
.tv_nsec
);
550 } else if (!insert_parent
&& mflag
) {
551 uint64_t s_added_time
= timespec_to_apfs_timestamp(&s
->st_ptimespec
);
552 char *added_string
= "DATEADDED";
553 set_key_value_pair(added_string
, &s_added_time
, true);
558 if (s
->flags
& F_XATTRS
) {
559 char buf
[kSHA256NullTerminatedBuffLen
];
561 ai
= SHA256_Path_XATTRs(p
->fts_accpath
, buf
);
563 if (ai
&& !ai
->digest
) {
565 printf("%sxattrsdigest missing, expected: %s\n", tab
, s
->xattrsdigest
);
567 } else if (ai
&& strcmp(ai
->digest
, s
->xattrsdigest
)) {
569 printf("%sxattrsdigest expected %s found %s\n",
570 tab
, s
->xattrsdigest
, ai
->digest
);
575 if (ai
&& ai
->xdstream_priv_id
!= s
->xdstream_priv_id
) {
576 set_key_value_pair((void*)&ai
->xdstream_priv_id
, &s
->xdstream_priv_id
, false);
581 if ((s
->flags
& F_INODE
) &&
582 (p
->fts_statp
->st_ino
!= s
->st_ino
)) {
585 (void)printf("%sinode expected %llu found %llu\n",
586 tab
, s
->st_ino
, p
->fts_statp
->st_ino
);
590 set_key_value_pair((void*)&p
->fts_statp
->st_ino
, &s
->st_ino
, false);
593 if (s
->flags
& F_ACL
) {
594 char *new_digest
, buf
[kSHA256NullTerminatedBuffLen
];
595 new_digest
= SHA256_Path_ACL(p
->fts_accpath
, buf
);
598 printf("%sacldigest missing, expected: %s\n", tab
, s
->acldigest
);
600 } else if (strcmp(new_digest
, s
->acldigest
)) {
602 printf("%sacldigest expected %s found %s\n",
603 tab
, s
->acldigest
, new_digest
);
607 if (s
->flags
& F_SIBLINGID
) {
608 uint64_t new_sibling_id
= get_sibling_id(p
->fts_accpath
);
609 new_sibling_id
= (new_sibling_id
!= p
->fts_statp
->st_ino
) ? new_sibling_id
: 0;
610 if (new_sibling_id
!= s
->sibling_id
) {
613 (void)printf("%ssibling id expected %llu found %llu\n",
614 tab
, s
->sibling_id
, new_sibling_id
);
618 set_key_value_pair((void*)&new_sibling_id
, &s
->sibling_id
, false);
629 switch(type
& S_IFMT
) {
678 static char lbuf
[MAXPATHLEN
];
681 if ((len
= readlink(name
, lbuf
, sizeof(lbuf
) - 1)) == -1) {
683 RECORD_FAILURE(20, error
);
684 errc(1, error
, "line %d: %s", lineno
, name
);