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