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