file_cmds-321.100.10.0.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 <CoreFoundation/CoreFoundation.h>
39 #include <sys/param.h>
40 #include <sys/stat.h>
41 #include <sys/time.h>
42
43 #include <err.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <fts.h>
47 #ifndef __APPLE__
48 #ifdef ENABLE_MD5
49 #include <md5.h>
50 #endif
51 #ifdef ENABLE_RMD160
52 #include <ripemd.h>
53 #endif
54 #ifdef ENABLE_SHA1
55 #include <sha.h>
56 #endif
57 #ifdef ENABLE_SHA256
58 #include <sha256.h>
59 #endif
60 #endif /* !__APPLE__ */
61 #include <stdint.h>
62 #include <stdio.h>
63 #include <time.h>
64 #include <unistd.h>
65 #include <vis.h>
66
67 #include "metrics.h"
68 #include "mtree.h"
69 #include "extern.h"
70
71 #ifdef __APPLE__
72 #include "commoncrypto.h"
73 #endif /* __APPLE__ */
74
75 #define INDENTNAMELEN 8
76 #define LABEL \
77 if (!label++) { \
78 len = printf("%s changed\n", RP(p)); \
79 tab = "\t"; \
80 }
81
82 extern CFMutableDictionaryRef dict;
83
84 // max/min times apfs can store on disk
85 #define APFS_MAX_TIME 0x7fffffffffffffffLL
86 #define APFS_MIN_TIME (-0x7fffffffffffffffLL-1)
87
88 static uint64_t
89 timespec_to_apfs_timestamp(struct timespec *ts)
90 {
91 int64_t total;
92 int64_t seconds;
93
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);
99
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;
109 }
110
111 return (uint64_t)total;
112 }
113
114 static void
115 set_key_value_pair(void *in_key, uint64_t *in_val, bool is_string)
116 {
117 CFStringRef key;
118 CFNumberRef val;
119
120 if (is_string) {
121 key = CFStringCreateWithCString(NULL, (const char*)in_key, kCFStringEncodingUTF8);
122
123 } else {
124 key = CFStringCreateWithFormat(NULL, NULL, CFSTR("%llu"), *(uint64_t*)in_key);
125 }
126
127 val = CFNumberCreate(NULL, kCFNumberSInt64Type, in_val);
128
129 // we always expect the key to be not present
130 if (key && val) {
131 CFDictionaryAddValue(dict, key, val);
132 } else {
133 if (key) {
134 CFRelease(key);
135 }
136 if (val) {
137 CFRelease(val);
138 }
139 RECORD_FAILURE(1, EINVAL);
140 errx(1, "set_key_value_pair: key/value is null");
141 }
142
143 if (key) {
144 CFRelease(key);
145 }
146 if (val) {
147 CFRelease(val);
148 }
149 }
150
151 int
152 compare(char *name __unused, NODE *s, FTSENT *p)
153 {
154 int error = 0;
155 struct timeval tv[2];
156 uint32_t val;
157 int fd, label;
158 off_t len;
159 char *cp;
160 const char *tab = "";
161 char *fflags, *badflags;
162 u_long flags;
163
164 label = 0;
165 switch(s->type) {
166 case F_BLOCK:
167 if (!S_ISBLK(p->fts_statp->st_mode)) {
168 RECORD_FAILURE(2, EINVAL);
169 goto typeerr;
170 }
171 break;
172 case F_CHAR:
173 if (!S_ISCHR(p->fts_statp->st_mode)) {
174 RECORD_FAILURE(3, EINVAL);
175 goto typeerr;
176 }
177 break;
178 case F_DIR:
179 if (!S_ISDIR(p->fts_statp->st_mode)) {
180 RECORD_FAILURE(4, EINVAL);
181 goto typeerr;
182 }
183 break;
184 case F_FIFO:
185 if (!S_ISFIFO(p->fts_statp->st_mode)) {
186 RECORD_FAILURE(5, EINVAL);
187 goto typeerr;
188 }
189 break;
190 case F_FILE:
191 if (!S_ISREG(p->fts_statp->st_mode)) {
192 RECORD_FAILURE(6, EINVAL);
193 goto typeerr;
194 }
195 break;
196 case F_LINK:
197 if (!S_ISLNK(p->fts_statp->st_mode)) {
198 RECORD_FAILURE(7, EINVAL);
199 goto typeerr;
200 }
201 break;
202 case F_SOCK:
203 if (!S_ISSOCK(p->fts_statp->st_mode)) {
204 RECORD_FAILURE(8, EINVAL);
205 typeerr: LABEL;
206 (void)printf("\ttype expected %s found %s\n",
207 ftype(s->type), inotype(p->fts_statp->st_mode));
208 return (label);
209 }
210 break;
211 }
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) {
214 LABEL;
215 (void)printf("%suser expected %lu found %lu",
216 tab, (u_long)s->st_uid, (u_long)p->fts_statp->st_uid);
217 if (uflag) {
218 if (chown(p->fts_accpath, s->st_uid, -1)) {
219 error = errno;
220 RECORD_FAILURE(9, error);
221 (void)printf(" not modified: %s\n",
222 strerror(error));
223 } else {
224 (void)printf(" modified\n");
225 }
226 } else {
227 (void)printf("\n");
228 }
229 tab = "\t";
230 }
231 if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) {
232 LABEL;
233 (void)printf("%sgid expected %lu found %lu",
234 tab, (u_long)s->st_gid, (u_long)p->fts_statp->st_gid);
235 if (uflag) {
236 if (chown(p->fts_accpath, -1, s->st_gid)) {
237 error = errno;
238 RECORD_FAILURE(10, error);
239 (void)printf(" not modified: %s\n",
240 strerror(error));
241 } else {
242 (void)printf(" modified\n");
243 }
244 } else {
245 (void)printf("\n");
246 }
247 tab = "\t";
248 }
249 if (s->flags & F_MODE &&
250 !S_ISLNK(p->fts_statp->st_mode) &&
251 s->st_mode != (p->fts_statp->st_mode & MBITS)) {
252 LABEL;
253 (void)printf("%spermissions expected %#o found %#o",
254 tab, s->st_mode, p->fts_statp->st_mode & MBITS);
255 if (uflag) {
256 if (chmod(p->fts_accpath, s->st_mode)) {
257 error = errno;
258 RECORD_FAILURE(11, error);
259 (void)printf(" not modified: %s\n",
260 strerror(error));
261 } else {
262 (void)printf(" modified\n");
263 }
264 } else {
265 (void)printf("\n");
266 }
267 tab = "\t";
268 }
269 if (s->flags & F_NLINK && s->type != F_DIR &&
270 s->st_nlink != p->fts_statp->st_nlink) {
271 LABEL;
272 (void)printf("%slink_count expected %u found %u\n",
273 tab, s->st_nlink, p->fts_statp->st_nlink);
274 tab = "\t";
275 }
276 if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size &&
277 !S_ISDIR(p->fts_statp->st_mode)) {
278 LABEL;
279 (void)printf("%ssize expected %jd found %jd\n", tab,
280 (intmax_t)s->st_size, (intmax_t)p->fts_statp->st_size);
281 tab = "\t";
282 }
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))) {
286 if (!mflag) {
287 LABEL;
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);
292 if (uflag) {
293 tv[0].tv_sec = s->st_mtimespec.tv_sec;
294 tv[0].tv_usec = s->st_mtimespec.tv_nsec / 1000;
295 tv[1] = tv[0];
296 if (utimes(p->fts_accpath, tv)) {
297 error = errno;
298 RECORD_FAILURE(12, error);
299 (void)printf(" not modified: %s\n",
300 strerror(error));
301 } else {
302 (void)printf(" modified\n");
303 }
304 } else {
305 (void)printf("\n");
306 }
307 tab = "\t";
308 }
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);
313 insert_mod = 1;
314 }
315 }
316 if (s->flags & F_CKSUM) {
317 if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) {
318 LABEL;
319 error = errno;
320 RECORD_FAILURE(13, error);
321 (void)printf("%scksum: %s: %s\n",
322 tab, p->fts_accpath, strerror(error));
323 tab = "\t";
324 } else if (crc(fd, &val, &len)) {
325 (void)close(fd);
326 LABEL;
327 error = errno;
328 RECORD_FAILURE(14, error);
329 (void)printf("%scksum: %s: %s\n",
330 tab, p->fts_accpath, strerror(error));
331 tab = "\t";
332 } else {
333 (void)close(fd);
334 if (s->cksum != val) {
335 LABEL;
336 (void)printf("%scksum expected %lu found %lu\n",
337 tab, s->cksum, (unsigned long)val);
338 tab = "\t";
339 }
340 }
341 }
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) {
347 flags = 0;
348 } else if (strtofflags(&badflags, &flags, NULL) != 0)
349 errx(1, "invalid flag %s", badflags);
350 free(fflags);
351 if (s->st_flags != flags) {
352 LABEL;
353 fflags = flags_to_string(s->st_flags);
354 (void)printf("%sflags expected \"%s\"", tab, fflags);
355 free(fflags);
356
357 fflags = flags_to_string(flags);
358 (void)printf(" found \"%s\"", fflags);
359 free(fflags);
360
361 if (uflag) {
362 if (chflags(p->fts_accpath, (u_int)s->st_flags)) {
363 error = errno;
364 RECORD_FAILURE(15, error);
365 (void)printf(" not modified: %s\n",
366 strerror(error));
367 } else {
368 (void)printf(" modified\n");
369 }
370 } else {
371 (void)printf("\n");
372 }
373 tab = "\t";
374 }
375 }
376 #ifdef ENABLE_MD5
377 if (s->flags & F_MD5) {
378 char *new_digest, buf[33];
379 #ifdef __clang__
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
385 #endif
386 if (!new_digest) {
387 LABEL;
388 error = errno;
389 RECORD_FAILURE(16, error);
390 printf("%sMD5: %s: %s\n", tab, p->fts_accpath,
391 strerror(error));
392 tab = "\t";
393 } else if (strcmp(new_digest, s->md5digest)) {
394 LABEL;
395 printf("%sMD5 expected %s found %s\n", tab, s->md5digest,
396 new_digest);
397 tab = "\t";
398 }
399 }
400 #endif /* ENABLE_MD5 */
401 #ifdef ENABLE_SHA1
402 if (s->flags & F_SHA1) {
403 char *new_digest, buf[41];
404 #ifdef __clang__
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
410 #endif
411 if (!new_digest) {
412 LABEL;
413 error = errno;
414 RECORD_FAILURE(17, error);
415 printf("%sSHA-1: %s: %s\n", tab, p->fts_accpath,
416 strerror(error));
417 tab = "\t";
418 } else if (strcmp(new_digest, s->sha1digest)) {
419 LABEL;
420 printf("%sSHA-1 expected %s found %s\n",
421 tab, s->sha1digest, new_digest);
422 tab = "\t";
423 }
424 }
425 #endif /* ENABLE_SHA1 */
426 #ifdef ENABLE_RMD160
427 if (s->flags & F_RMD160) {
428 char *new_digest, buf[41];
429 #ifdef __clang__
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
435 #endif
436 if (!new_digest) {
437 LABEL;
438 error = errno;
439 RECORD_FAILURE(18, error);
440 printf("%sRIPEMD160: %s: %s\n", tab,
441 p->fts_accpath, strerror(error));
442 tab = "\t";
443 } else if (strcmp(new_digest, s->rmd160digest)) {
444 LABEL;
445 printf("%sRIPEMD160 expected %s found %s\n",
446 tab, s->rmd160digest, new_digest);
447 tab = "\t";
448 }
449 }
450 #endif /* ENABLE_RMD160 */
451 #ifdef ENABLE_SHA256
452 if (s->flags & F_SHA256) {
453 char *new_digest, buf[kSHA256NullTerminatedBuffLen];
454
455 new_digest = SHA256_File(p->fts_accpath, buf);
456 if (!new_digest) {
457 LABEL;
458 error = errno;
459 RECORD_FAILURE(19, error);
460 printf("%sSHA-256: %s: %s\n", tab, p->fts_accpath,
461 strerror(error));
462 tab = "\t";
463 } else if (strcmp(new_digest, s->sha256digest)) {
464 LABEL;
465 printf("%sSHA-256 expected %s found %s\n",
466 tab, s->sha256digest, new_digest);
467 tab = "\t";
468 }
469 }
470 #endif /* ENABLE_SHA256 */
471
472 if (s->flags & F_SLINK &&
473 strcmp(cp = rlink(p->fts_accpath), s->slink)) {
474 LABEL;
475 (void)printf("%slink_ref expected %s found %s\n",
476 tab, s->slink, cp);
477 }
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))) {
481 if (!mflag) {
482 LABEL;
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);
487 tab = "\t";
488 }
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);
493 insert_birth = 1;
494 }
495 }
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))) {
499 if (!mflag) {
500 LABEL;
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);
505 tab = "\t";
506 }
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);
511 insert_access = 1;
512
513 }
514 }
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))) {
518 if (!mflag) {
519 LABEL;
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);
524 tab = "\t";
525 }
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);
530 insert_change = 1;
531 }
532 }
533 if (s->flags & F_PTIME) {
534 int supported;
535 struct timespec ptimespec = ptime(p->fts_accpath, &supported);
536 if (!supported) {
537 if (mflag) {
538 ptimespec.tv_sec = 0;
539 ptimespec.tv_nsec = 0;
540 supported = 1;
541 } else {
542 LABEL;
543 (void)printf("%stime added to parent folder expected %.24s.%09ld found that it is not supported\n",
544 tab, ctime(&s->st_ptimespec.tv_sec), s->st_ptimespec.tv_nsec);
545 tab = "\t";
546 }
547 }
548 if (supported && ((s->st_ptimespec.tv_sec != ptimespec.tv_sec) ||
549 (s->st_ptimespec.tv_nsec != ptimespec.tv_nsec))) {
550 if (!mflag) {
551 LABEL;
552 (void)printf("%stime added to parent folder expected %.24s.%09ld ",
553 tab, ctime(&s->st_ptimespec.tv_sec), s->st_ptimespec.tv_nsec);
554 (void)printf("found %.24s.%09ld\n",
555 ctime(&ptimespec.tv_sec), ptimespec.tv_nsec);
556 tab = "\t";
557 } else if (!insert_parent && mflag) {
558 uint64_t s_added_time = timespec_to_apfs_timestamp(&s->st_ptimespec);
559 char *added_string = "DATEADDED";
560 set_key_value_pair(added_string, &s_added_time, true);
561 insert_parent = 1;
562 }
563 }
564 }
565 if (s->flags & F_XATTRS) {
566 char buf[kSHA256NullTerminatedBuffLen];
567 xattr_info *ai;
568 ai = SHA256_Path_XATTRs(p->fts_accpath, buf);
569 if (!mflag) {
570 if (ai && !ai->digest) {
571 LABEL;
572 printf("%sxattrsdigest missing, expected: %s\n", tab, s->xattrsdigest);
573 tab = "\t";
574 } else if (ai && strcmp(ai->digest, s->xattrsdigest)) {
575 LABEL;
576 printf("%sxattrsdigest expected %s found %s\n",
577 tab, s->xattrsdigest, ai->digest);
578 tab = "\t";
579 }
580 }
581 if (mflag) {
582 if (ai && ai->xdstream_priv_id != s->xdstream_priv_id) {
583 set_key_value_pair((void*)&ai->xdstream_priv_id, &s->xdstream_priv_id, false);
584 }
585 }
586 free(ai);
587 }
588 if ((s->flags & F_INODE) &&
589 (p->fts_statp->st_ino != s->st_ino)) {
590 if (!mflag) {
591 LABEL;
592 (void)printf("%sinode expected %llu found %llu\n",
593 tab, s->st_ino, p->fts_statp->st_ino);
594 tab = "\t";
595 }
596 if (mflag) {
597 set_key_value_pair((void*)&p->fts_statp->st_ino, &s->st_ino, false);
598 }
599 }
600 if (s->flags & F_ACL) {
601 char *new_digest, buf[kSHA256NullTerminatedBuffLen];
602 new_digest = SHA256_Path_ACL(p->fts_accpath, buf);
603 if (!new_digest) {
604 LABEL;
605 printf("%sacldigest missing, expected: %s\n", tab, s->acldigest);
606 tab = "\t";
607 } else if (strcmp(new_digest, s->acldigest)) {
608 LABEL;
609 printf("%sacldigest expected %s found %s\n",
610 tab, s->acldigest, new_digest);
611 tab = "\t";
612 }
613 }
614 if (s->flags & F_SIBLINGID) {
615 uint64_t new_sibling_id = get_sibling_id(p->fts_accpath);
616 new_sibling_id = (new_sibling_id != p->fts_statp->st_ino) ? new_sibling_id : 0;
617 if (new_sibling_id != s->sibling_id) {
618 if (!mflag) {
619 LABEL;
620 (void)printf("%ssibling id expected %llu found %llu\n",
621 tab, s->sibling_id, new_sibling_id);
622 tab = "\t";
623 }
624 if (mflag) {
625 set_key_value_pair((void*)&new_sibling_id, &s->sibling_id, false);
626 }
627 }
628 }
629
630 return (label);
631 }
632
633 const char *
634 inotype(u_int type)
635 {
636 switch(type & S_IFMT) {
637 case S_IFBLK:
638 return ("block");
639 case S_IFCHR:
640 return ("char");
641 case S_IFDIR:
642 return ("dir");
643 case S_IFIFO:
644 return ("fifo");
645 case S_IFREG:
646 return ("file");
647 case S_IFLNK:
648 return ("link");
649 case S_IFSOCK:
650 return ("socket");
651 default:
652 return ("unknown");
653 }
654 /* NOTREACHED */
655 }
656
657 const char *
658 ftype(u_int type)
659 {
660 switch(type) {
661 case F_BLOCK:
662 return ("block");
663 case F_CHAR:
664 return ("char");
665 case F_DIR:
666 return ("dir");
667 case F_FIFO:
668 return ("fifo");
669 case F_FILE:
670 return ("file");
671 case F_LINK:
672 return ("link");
673 case F_SOCK:
674 return ("socket");
675 default:
676 return ("unknown");
677 }
678 /* NOTREACHED */
679 }
680
681 char *
682 rlink(char *name)
683 {
684 int error = 0;
685 static char lbuf[MAXPATHLEN];
686 ssize_t len;
687
688 if ((len = readlink(name, lbuf, sizeof(lbuf) - 1)) == -1) {
689 error = errno;
690 RECORD_FAILURE(20, error);
691 errc(1, error, "line %d: %s", lineno, name);
692 }
693 lbuf[len] = '\0';
694 return (lbuf);
695 }