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