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