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