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