xfs: Verify correctness of upgrading an fs to support large extent counters
[xfstests-dev.git] / src / fssum.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2012 STRATO AG.  All rights reserved.
4  */
5 #ifdef __linux__
6 #define _BSD_SOURCE
7 #define _DEFAULT_SOURCE
8 #define _LARGEFILE64_SOURCE
9 #ifndef _GNU_SOURCE
10 #define _GNU_SOURCE
11 #endif
12 #endif
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <string.h>
17 #include <fcntl.h>
18 #include <dirent.h>
19 #include <errno.h>
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <sys/sysmacros.h>
23 #include <sys/xattr.h>
24 #ifdef __SOLARIS__
25 #include <sys/mkdev.h>
26 #endif
27 #include "md5.h"
28 #include <netinet/in.h>
29 #include <inttypes.h>
30 #include <assert.h>
31 #include <endian.h>
32
33 #define CS_SIZE 16
34 #define CHUNKS  128
35
36 #ifdef __linux__
37 #ifndef SEEK_DATA
38 #define SEEK_DATA 3
39 #define SEEK_HOLE 4
40 #endif
41 #endif
42
43 /* TODO: add hardlink recognition */
44
45 struct excludes {
46         char *path;
47         int len;
48 };
49
50 typedef struct _sum {
51 #ifdef HAVE_OPENSSL
52         EVP_MD_CTX      *ctx;
53 #else
54         MD5_CTX         md5;
55 #endif
56         unsigned char   out[16];
57 } sum_t;
58
59 typedef int (*sum_file_data_t)(int fd, sum_t *dst);
60
61 int gen_manifest = 0;
62 int in_manifest = 0;
63 char *checksum = NULL;
64 struct excludes *excludes;
65 int n_excludes = 0;
66 int verbose = 0;
67 FILE *out_fp;
68 FILE *in_fp;
69
70 enum _flags {
71         FLAG_UID,
72         FLAG_GID,
73         FLAG_MODE,
74         FLAG_ATIME,
75         FLAG_MTIME,
76         FLAG_CTIME,
77         FLAG_DATA,
78         FLAG_XATTRS,
79         FLAG_OPEN_ERROR,
80         FLAG_STRUCTURE,
81         NUM_FLAGS
82 };
83
84 const char flchar[] = "ugoamcdtes";
85 char line[65536];
86
87 int flags[NUM_FLAGS] = {1, 1, 1, 1, 1, 0, 1, 1, 0, 0};
88
89 char *
90 getln(char *buf, int size, FILE *fp)
91 {
92         char *p;
93         int l;
94
95         p = fgets(buf, size, fp);
96         if (!p)
97                 return NULL;
98
99         l = strlen(p);
100         while(l > 0  && (p[l - 1] == '\n' || p[l - 1] == '\r'))
101                 p[--l] = 0;
102
103         return p;
104 }
105
106 void
107 parse_flag(int c)
108 {
109         int i;
110         int is_upper = 0;
111
112         if (c >= 'A' && c <= 'Z') {
113                 is_upper = 1;
114                 c += 'a' - 'A';
115         }
116         for (i = 0; flchar[i]; ++i) {
117                 if (flchar[i] == c) {
118                         flags[i] = is_upper ? 0 : 1;
119                         return;
120                 }
121         }
122         fprintf(stderr, "unrecognized flag %c\n", c);
123         exit(-1);
124 }
125
126 void
127 parse_flags(char *p)
128 {
129         while (*p)
130                 parse_flag(*p++);
131 }
132
133 void
134 usage(void)
135 {
136         fprintf(stderr, "usage: fssum <options> <path>\n");
137         fprintf(stderr, "  options:\n");
138         fprintf(stderr, "    -f           : write out a full manifest file\n");
139         fprintf(stderr, "    -w <file>    : send output to file\n");
140         fprintf(stderr, "    -v           : verbose mode (debugging only)\n");
141         fprintf(stderr, "    -r <file>    : read checksum or manifest from file\n");
142         fprintf(stderr, "    -[ugoamcdtes]: specify which fields to include in checksum calculation.\n");
143         fprintf(stderr, "         u       : include uid\n");
144         fprintf(stderr, "         g       : include gid\n");
145         fprintf(stderr, "         o       : include mode\n");
146         fprintf(stderr, "         m       : include mtime\n");
147         fprintf(stderr, "         a       : include atime\n");
148         fprintf(stderr, "         c       : include ctime\n");
149         fprintf(stderr, "         d       : include file data\n");
150         fprintf(stderr, "         t       : include xattrs\n");
151         fprintf(stderr, "         e       : include open errors (aborts otherwise)\n");
152         fprintf(stderr, "         s       : include block structure (holes)\n");
153         fprintf(stderr, "    -[UGOAMCDTES]: exclude respective field from calculation\n");
154         fprintf(stderr, "    -n           : reset all flags\n");
155         fprintf(stderr, "    -N           : set all flags\n");
156         fprintf(stderr, "    -x path      : exclude path when building checksum (multiple ok)\n");
157         fprintf(stderr, "    -h           : this help\n\n");
158         fprintf(stderr, "The default field mask is ugoamCdtES. If the checksum/manifest is read from a\n");
159         fprintf(stderr, "file, the mask is taken from there and the values given on the command line\n");
160         fprintf(stderr, "are ignored.\n");
161         exit(-1);
162 }
163
164 static char buf[65536];
165
166 void *
167 alloc(size_t sz)
168 {
169         void *p = malloc(sz);
170
171         if (!p) {
172                 fprintf(stderr, "malloc failed\n");
173                 exit(-1);
174         }
175
176         return p;
177 }
178
179 void
180 sum_init(sum_t *cs)
181 {
182 #ifdef HAVE_OPENSSL
183         cs->ctx = EVP_MD_CTX_new();
184         if (!cs->ctx) {
185                 fprintf(stderr, "evp md ctx allocation failed\n");
186                 exit(-1);
187         }
188         EVP_DigestInit(cs->ctx, EVP_md5());
189 #else
190         MD5_Init(&cs->md5);
191 #endif
192 }
193
194 void
195 sum_fini(sum_t *cs)
196 {
197 #ifdef HAVE_OPENSSL
198         EVP_DigestFinal(cs->ctx, cs->out, NULL);
199         EVP_MD_CTX_free(cs->ctx);
200         cs->ctx = NULL;
201 #else
202         MD5_Final(cs->out, &cs->md5);
203 #endif
204 }
205
206 void
207 sum_add(sum_t *cs, void *buf, int size)
208 {
209 #ifdef HAVE_OPENSSL
210         EVP_DigestUpdate(cs->ctx, buf, size);
211 #else
212         MD5_Update(&cs->md5, buf, size);
213 #endif
214 }
215
216 void
217 sum_add_sum(sum_t *dst, sum_t *src)
218 {
219         sum_add(dst, src->out, sizeof(src->out));
220 }
221
222 void
223 sum_add_u64(sum_t *dst, uint64_t val)
224 {
225         uint64_t v = htobe64(val);
226         sum_add(dst, &v, sizeof(v));
227 }
228
229 void
230 sum_add_time(sum_t *dst, time_t t)
231 {
232         sum_add_u64(dst, t);
233 }
234
235 char *
236 sum_to_string(sum_t *dst)
237 {
238         int i;
239         char *s = alloc(CS_SIZE * 2 + 1);
240
241         for (i = 0; i < CS_SIZE; ++i)
242                 sprintf(s + i * 2, "%02x", dst->out[i]);
243
244         return s;
245 }
246
247 int
248 namecmp(const void *aa, const void *bb)
249 {
250         char * const *a = aa;
251         char * const *b = bb;
252
253         return strcmp(*a, *b);
254 }
255
256 int
257 sum_xattrs(int fd, sum_t *dst)
258 {
259         ssize_t buflen;
260         ssize_t len;
261         char *buf;
262         char *p;
263         char **names = NULL;
264         int num_xattrs = 0;
265         int ret = 0;
266         int i;
267
268         buflen = flistxattr(fd, NULL, 0);
269         if (buflen < 0)
270                 return -errno;
271         /* no xattrs exist */
272         if (buflen == 0)
273                 return 0;
274
275         buf = malloc(buflen);
276         if (!buf)
277                 return -ENOMEM;
278
279         buflen = flistxattr(fd, buf, buflen);
280         if (buflen < 0) {
281                 ret = -errno;
282                 goto out;
283         }
284
285         /*
286          * Keep the list of xattrs sorted, because the order in which they are
287          * listed is filesystem dependent, so we want to get the same checksum
288          * on different filesystems.
289          */
290
291         p = buf;
292         len = buflen;
293         while (len > 0) {
294                 int keylen;
295
296                 keylen = strlen(p) + 1; /* +1 for NULL terminator */
297                 len -= keylen;
298                 p += keylen;
299                 num_xattrs++;
300         }
301
302         names = malloc(sizeof(char *) * num_xattrs);
303         if (!names) {
304                 ret = -ENOMEM;
305                 goto out;
306         }
307
308         p = buf;
309         for (i = 0; i < num_xattrs; i++) {
310                 names[i] = p;
311                 p += strlen(p) + 1; /* +1 for NULL terminator */
312         }
313
314         qsort(names, num_xattrs, sizeof(char *), namecmp);
315
316         for (i = 0; i < num_xattrs; i++) {
317                 len = fgetxattr(fd, names[i], NULL, 0);
318                 if (len < 0) {
319                         ret = -errno;
320                         goto out;
321                 }
322                 sum_add(dst, names[i], strlen(names[i]));
323                 /* no value */
324                 if (len == 0)
325                         continue;
326                 p = malloc(len);
327                 if (!p) {
328                         ret = -ENOMEM;
329                         goto out;
330                 }
331                 len = fgetxattr(fd, names[i], p, len);
332                 if (len < 0) {
333                         ret = -errno;
334                         free(p);
335                         goto out;
336                 }
337                 sum_add(dst, p, len);
338                 free(p);
339         }
340 out:
341         free(buf);
342         free(names);
343
344         return ret;
345 }
346
347 int
348 sum_file_data_permissive(int fd, sum_t *dst)
349 {
350         int ret;
351
352         while (1) {
353                 ret = read(fd, buf, sizeof(buf));
354                 if (ret < 0)
355                         return -errno;
356                 sum_add(dst, buf, ret);
357                 if (ret < sizeof(buf))
358                         break;
359         }
360         return 0;
361 }
362
363 int
364 sum_file_data_strict(int fd, sum_t *dst)
365 {
366         int ret;
367         off_t pos;
368
369         pos = lseek(fd, 0, SEEK_CUR);
370         if (pos == (off_t)-1)
371                 return errno == ENXIO ? 0 : -2;
372
373         while (1) {
374                 pos = lseek(fd, pos, SEEK_DATA);
375                 if (pos == (off_t)-1)
376                         return errno == ENXIO ? 0 : -2;
377                 ret = read(fd, buf, sizeof(buf));
378                 assert(ret); /* eof found by lseek */
379                 if (ret <= 0)
380                         return ret;
381                 if (verbose >= 2)
382                         fprintf(stderr,
383                                 "adding to sum at file offset %llu, %d bytes\n",
384                                 (unsigned long long)pos, ret);
385                 sum_add_u64(dst, (uint64_t)pos);
386                 sum_add(dst, buf, ret);
387                 pos += ret;
388         }
389 }
390
391 char *
392 escape(char *in)
393 {
394         char *out = alloc(strlen(in) * 3 + 1);
395         char *src = in;
396         char *dst = out;
397
398         for (; *src; ++src) {
399                 if (*src >= 32 && *src < 127 && *src != '\\') {
400                         *dst++ = *src;
401                 } else {
402                         sprintf(dst, "\\%02x", (unsigned char)*src);
403                         dst += 3;
404                 }
405         }
406         *dst = 0;
407
408         return out;
409 }
410
411 void
412 excess_file(const char *fn)
413 {
414         printf("only in local fs: %s\n", fn);
415 }
416
417 void
418 missing_file(const char *fn)
419 {
420         printf("only in remote fs: %s\n", fn);
421 }
422
423 int
424 pathcmp(const char *a, const char *b)
425 {
426         int len_a = strlen(a);
427         int len_b = strlen(b);
428
429         /*
430          * as the containing directory is sent after the files, it has to
431          * come out bigger in the comparison.
432          */
433         if (len_a < len_b && a[len_a - 1] == '/' && strncmp(a, b, len_a) == 0)
434                 return 1;
435         if (len_a > len_b && b[len_b - 1] == '/' && strncmp(a, b, len_b) == 0)
436                 return -1;
437
438         return strcmp(a, b);
439 }
440
441 void
442 check_match(char *fn, char *local_m, char *remote_m,
443             char *local_c, char *remote_c)
444 {
445         int match_m = !strcmp(local_m, remote_m);
446         int match_c = !strcmp(local_c, remote_c);
447
448         if (match_m && !match_c) {
449                 printf("data mismatch in %s\n", fn);
450         } else if (!match_m && match_c) {
451                 printf("metadata mismatch in %s\n", fn);
452         } else if (!match_m && !match_c) {
453                 printf("metadata and data mismatch in %s\n", fn);
454         }
455 }
456
457 char *prev_fn;
458 char *prev_m;
459 char *prev_c;
460 void
461 check_manifest(char *fn, char *m, char *c, int last_call)
462 {
463         char *rem_m;
464         char *rem_c;
465         char *l;
466         int cmp;
467
468         if (prev_fn) {
469                 if (last_call)
470                         cmp = -1;
471                 else
472                         cmp = pathcmp(prev_fn, fn);
473                 if (cmp > 0) {
474                         excess_file(fn);
475                         return;
476                 } else if (cmp < 0) {
477                         missing_file(prev_fn);
478                 } else {
479                         check_match(fn, m, prev_m, c, prev_c);
480                 }
481                 free(prev_fn);
482                 free(prev_m);
483                 free(prev_c);
484                 prev_fn = NULL;
485                 prev_m = NULL;
486                 prev_c = NULL;
487                 if (cmp == 0)
488                         return;
489         }
490         while ((l = getln(line, sizeof(line), in_fp))) {
491                 rem_c = strrchr(l, ' ');
492                 if (!rem_c) {
493                         /* final cs */
494                         checksum = strdup(l);
495                         break;
496                 }
497                 if (rem_c == l) {
498 malformed:
499                         fprintf(stderr, "malformed input\n");
500                         exit(-1);
501                 }
502                 *rem_c++ = 0;
503                 rem_m = strrchr(l, ' ');
504                 if (!rem_m)
505                         goto malformed;
506                 *rem_m++ = 0;
507
508                 if (last_call)
509                         cmp = -1;
510                 else
511                         cmp = pathcmp(l, fn);
512                 if (cmp == 0) {
513                         check_match(fn, m, rem_m, c, rem_c);
514                         return;
515                 } else if (cmp > 0) {
516                         excess_file(fn);
517                         prev_fn = strdup(l);
518                         prev_m = strdup(rem_m);
519                         prev_c = strdup(rem_c); 
520                         return;
521                 }
522                 missing_file(l);
523         }
524         if (!last_call)
525                 excess_file(fn);
526 }
527
528 void
529 sum(int dirfd, int level, sum_t *dircs, char *path_prefix, char *path_in)
530 {
531         DIR *d;
532         struct dirent *de;
533         char **namelist = NULL;
534         int alloclen = 0;
535         int entries = 0;
536         int i;
537         int ret;
538         int fd;
539         int excl;
540         sum_file_data_t sum_file_data = flags[FLAG_STRUCTURE] ?
541                         sum_file_data_strict : sum_file_data_permissive;
542         struct stat64 dir_st;
543
544         if (fstat64(dirfd, &dir_st)) {
545                 perror("fstat");
546                 exit(-1);
547         }
548
549         d = fdopendir(dirfd);
550         if (!d) {
551                 perror("opendir");
552                 exit(-1);
553         }
554         while((de = readdir(d))) {
555                 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
556                         continue;
557                 if (entries == alloclen) {
558                         alloclen += CHUNKS;
559                         namelist = realloc(namelist,
560                                            alloclen * sizeof(*namelist));
561                         if (!namelist) {
562                                 fprintf(stderr, "malloc failed\n");
563                                 exit(-1);
564                         }
565                 }
566                 namelist[entries] = strdup(de->d_name);
567                 if (!namelist[entries]) {
568                         fprintf(stderr, "malloc failed\n");
569                         exit(-1);
570                 }
571                 ++entries;
572         }
573         qsort(namelist, entries, sizeof(*namelist), namecmp);
574         for (i = 0; i < entries; ++i) {
575                 struct stat64 st;
576                 sum_t cs;
577                 sum_t meta;
578                 char *path;
579
580                 sum_init(&cs);
581                 sum_init(&meta);
582                 path = alloc(strlen(path_in) + strlen(namelist[i]) + 3);
583                 sprintf(path, "%s/%s", path_in, namelist[i]);
584                 for (excl = 0; excl < n_excludes; ++excl) {
585                         if (strncmp(excludes[excl].path, path,
586                             excludes[excl].len) == 0)
587                                 goto next;
588                 }
589
590                 ret = fchdir(dirfd);
591                 if (ret == -1) {
592                         perror("fchdir");
593                         exit(-1);
594                 }
595                 ret = lstat64(namelist[i], &st);
596                 if (ret) {
597                         fprintf(stderr, "stat failed for %s/%s: %s\n",
598                                 path_prefix, path, strerror(errno));
599                         exit(-1);
600                 }
601
602                 /* We are crossing into a different subvol, skip this subtree. */
603                 if (st.st_dev != dir_st.st_dev)
604                         goto next;
605
606                 sum_add_u64(&meta, level);
607                 sum_add(&meta, namelist[i], strlen(namelist[i]));
608                 if (!S_ISDIR(st.st_mode))
609                         sum_add_u64(&meta, st.st_nlink);
610                 if (flags[FLAG_UID])
611                         sum_add_u64(&meta, st.st_uid);
612                 if (flags[FLAG_GID])
613                         sum_add_u64(&meta, st.st_gid);
614                 if (flags[FLAG_MODE])
615                         sum_add_u64(&meta, st.st_mode);
616                 if (flags[FLAG_ATIME])
617                         sum_add_time(&meta, st.st_atime);
618                 if (flags[FLAG_MTIME])
619                         sum_add_time(&meta, st.st_mtime);
620                 if (flags[FLAG_CTIME])
621                         sum_add_time(&meta, st.st_ctime);
622                 if (flags[FLAG_XATTRS] &&
623                     (S_ISDIR(st.st_mode) || S_ISREG(st.st_mode))) {
624                         fd = openat(dirfd, namelist[i], 0);
625                         if (fd == -1 && flags[FLAG_OPEN_ERROR]) {
626                                 sum_add_u64(&meta, errno);
627                         } else if (fd == -1) {
628                                 fprintf(stderr, "open failed for %s/%s: %s\n",
629                                         path_prefix, path, strerror(errno));
630                                 exit(-1);
631                         } else {
632                                 ret = sum_xattrs(fd, &meta);
633                                 close(fd);
634                                 if (ret < 0) {
635                                         fprintf(stderr,
636                                                 "failed to read xattrs from "
637                                                 "%s/%s: %s\n",
638                                                 path_prefix, path,
639                                                 strerror(-ret));
640                                         exit(-1);
641                                 }
642                         }
643                 }
644                 if (S_ISDIR(st.st_mode)) {
645                         fd = openat(dirfd, namelist[i], 0);
646                         if (fd == -1 && flags[FLAG_OPEN_ERROR]) {
647                                 sum_add_u64(&meta, errno);
648                         } else if (fd == -1) {
649                                 fprintf(stderr, "open failed for %s/%s: %s\n",
650                                         path_prefix, path, strerror(errno));
651                                 exit(-1);
652                         } else {
653                                 sum(fd, level + 1, &cs, path_prefix, path);
654                                 close(fd);
655                         }
656                 } else if (S_ISREG(st.st_mode)) {
657                         sum_add_u64(&meta, st.st_size);
658                         if (flags[FLAG_DATA]) {
659                                 if (verbose)
660                                         fprintf(stderr, "file %s\n",
661                                                 namelist[i]);
662                                 fd = openat(dirfd, namelist[i], 0);
663                                 if (fd == -1 && flags[FLAG_OPEN_ERROR]) {
664                                         sum_add_u64(&meta, errno);
665                                 } else if (fd == -1) {
666                                         fprintf(stderr,
667                                                 "open failed for %s/%s: %s\n",
668                                                 path_prefix, path,
669                                                 strerror(errno));
670                                         exit(-1);
671                                 }
672                                 if (fd != -1) {
673                                         ret = sum_file_data(fd, &cs);
674                                         if (ret < 0) {
675                                                 fprintf(stderr,
676                                                         "read failed for "
677                                                         "%s/%s: %s\n",
678                                                         path_prefix, path,
679                                                         strerror(errno));
680                                                 exit(-1);
681                                         }
682                                         close(fd);
683                                 }
684                         }
685                 } else if (S_ISLNK(st.st_mode)) {
686                         ret = readlink(namelist[i], buf, sizeof(buf));
687                         if (ret == -1) {
688                                 perror("readlink");
689                                 exit(-1);
690                         }
691                         sum_add(&cs, buf, ret);
692                 } else if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
693                         sum_add_u64(&cs, major(st.st_rdev));
694                         sum_add_u64(&cs, minor(st.st_rdev));
695                 }
696                 sum_fini(&cs);
697                 sum_fini(&meta);
698                 if (gen_manifest || in_manifest) {
699                         char *fn;
700                         char *m;
701                         char *c;
702
703                         if (S_ISDIR(st.st_mode))
704                                 strcat(path, "/");
705                         fn = escape(path);
706                         m = sum_to_string(&meta);
707                         c = sum_to_string(&cs);
708
709                         if (gen_manifest)
710                                 fprintf(out_fp, "%s %s %s\n", fn, m, c);
711                         if (in_manifest)
712                                 check_manifest(fn, m, c, 0);
713                         free(c);
714                         free(m);
715                         free(fn);
716                 }
717                 sum_add_sum(dircs, &cs);
718                 sum_add_sum(dircs, &meta);
719 next:
720                 free(path);
721         }
722 }
723
724 int
725 main(int argc, char *argv[])
726 {
727         extern char *optarg;
728         extern int optind;
729         int     c;
730         char *path;
731         int fd;
732         sum_t cs;
733         char flagstring[sizeof(flchar)];
734         int i;
735         int plen;
736         int elen;
737         int n_flags = 0;
738         const char *allopts = "heEfuUgGoOaAmMcCdDtTsSnNw:r:vx:";
739
740         out_fp = stdout;
741         while ((c = getopt(argc, argv, allopts)) != EOF) {
742                 switch(c) {
743                 case 'f':
744                         gen_manifest = 1;
745                         break;
746                 case 'u':
747                 case 'U':
748                 case 'g':
749                 case 'G':
750                 case 'o':
751                 case 'O':
752                 case 'a':
753                 case 'A':
754                 case 'm':
755                 case 'M':
756                 case 'c':
757                 case 'C':
758                 case 'd':
759                 case 'D':
760                 case 'T':
761                 case 't':
762                 case 'e':
763                 case 'E':
764                 case 's':
765                 case 'S':
766                         ++n_flags;
767                         parse_flag(c);
768                         break;
769                 case 'n':
770                         for (i = 0; i < NUM_FLAGS; ++i)
771                                 flags[i] = 0;
772                         break;
773                 case 'N':
774                         for (i = 0; i < NUM_FLAGS; ++i)
775                                 flags[i] = 1;
776                         break;
777                 case 'w':
778                         out_fp = fopen(optarg, "w");
779                         if (!out_fp) {
780                                 fprintf(stderr,
781                                         "failed to open output file: %s\n",
782                                         strerror(errno));
783                                 exit(-1);
784                         }
785                         break;
786                 case 'r':
787                         in_fp = fopen(optarg, "r");
788                         if (!in_fp) {
789                                 fprintf(stderr,
790                                         "failed to open input file: %s\n",
791                                         strerror(errno));
792                                 exit(-1);
793                         }
794                         break;
795                 case 'x':
796                         ++n_excludes;
797                         excludes = realloc(excludes,
798                                            sizeof(*excludes) * n_excludes);
799                         if (!excludes) {
800                                 fprintf(stderr,
801                                         "failed to alloc exclude space\n");
802                                 exit(-1);
803                         }
804                         excludes[n_excludes - 1].path = optarg;
805                         break;
806                 case 'v':
807                         ++verbose;
808                         break;
809                 case 'h':
810                 case '?':
811                         usage();
812                 }
813         }
814
815         if (optind + 1 != argc) {
816                 fprintf(stderr, "missing path\n");
817                 usage();
818         }
819
820         if (in_fp) {
821                 char *l = getln(line, sizeof(line), in_fp);
822                 char *p;
823
824                 if (l == NULL) {
825                         fprintf(stderr, "failed to read line from input\n");
826                         exit(-1);
827                 }
828                 if (strncmp(l, "Flags: ", 7) == 0) {
829                         l += 7;
830                         in_manifest = 1;
831                         parse_flags(l);
832                 } else if ((p = strchr(l, ':'))) {
833                         *p++ = 0;
834                         parse_flags(l);
835                         checksum = strdup(p);
836                 } else {
837                         fprintf(stderr, "invalid input file format\n");
838                         exit(-1);
839                 }
840                 if (n_flags)
841                         fprintf(stderr, "warning: "
842                                 "command line flags ignored in -r mode\n");
843         }
844         strcpy(flagstring, flchar);
845         for (i = 0; i < NUM_FLAGS; ++i) {
846                 if (flags[i] == 0)
847                         flagstring[i] -= 'a' - 'A';
848         }
849
850         path = argv[optind];
851         plen = strlen(path);
852         if (path[plen - 1] == '/') {
853                 --plen;
854                 path[plen] = '\0';
855         }
856
857         for (i = 0; i < n_excludes; ++i) {
858                 if (strncmp(path, excludes[i].path, plen) != 0)
859                         fprintf(stderr,
860                                 "warning: exclude %s outside of path %s\n",
861                                 excludes[i].path, path);
862                 else
863                         excludes[i].path += plen;
864                 elen = strlen(excludes[i].path);
865                 if (excludes[i].path[elen - 1] == '/')
866                         --elen;
867                 excludes[i].path[elen] = '\0';
868                 excludes[i].len = elen;
869         }
870
871         fd = open(path, O_RDONLY);
872         if (fd == -1) {
873                 fprintf(stderr, "failed to open %s: %s\n", path,
874                         strerror(errno));
875                 exit(-1);
876         }
877
878         if (gen_manifest)
879                 fprintf(out_fp, "Flags: %s\n", flagstring);
880
881         sum_init(&cs);
882         sum(fd, 1, &cs, path, "");
883         sum_fini(&cs);
884
885         close(fd);
886         if (in_manifest)
887                 check_manifest("", "", "", 1);
888
889         if (!checksum) {
890                 if (in_manifest) {
891                         fprintf(stderr, "malformed input\n");
892                         exit(-1);
893                 }
894                 if (!gen_manifest)
895                         fprintf(out_fp, "%s:", flagstring);
896
897                 fprintf(out_fp, "%s\n", sum_to_string(&cs));
898         } else {
899                 if (strcmp(checksum, sum_to_string(&cs)) == 0) {
900                         printf("OK\n");
901                         exit(0);
902                 } else {
903                         printf("FAIL\n");
904                         exit(1);
905                 }
906         }
907
908         exit(0);
909 }