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