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